@@ -4,7 +4,7 @@ use spdx_expression::SpdxExpression;
44use std:: collections:: BTreeMap ;
55use tracing:: instrument;
66use trustify_common:: db:: chunk:: EntityChunkedIter ;
7- use trustify_entity:: license;
7+ use trustify_entity:: { license, licensing_infos } ;
88use uuid:: Uuid ;
99
1010const NAMESPACE : Uuid = Uuid :: from_bytes ( [
@@ -22,7 +22,7 @@ impl LicenseInfo {
2222 Uuid :: new_v5 ( & NAMESPACE , self . license . to_lowercase ( ) . as_bytes ( ) )
2323 }
2424
25- pub fn spdx_info ( & self ) -> ( Vec < String > , Vec < String > ) {
25+ pub fn spdx_info ( & self ) -> ( Vec < String > , Vec < String > , Vec < String > ) {
2626 SpdxExpression :: parse ( & self . license )
2727 . map ( |parsed| {
2828 let spdx_licenses = parsed
@@ -38,9 +38,20 @@ impl LicenseInfo {
3838 . map ( |e| e. to_string ( ) )
3939 . collect :: < Vec < _ > > ( ) ;
4040
41- ( spdx_licenses, spdx_license_exceptions)
41+ let custom_license_refs = parsed
42+ . licenses ( )
43+ . iter ( )
44+ // There are two types of custom licenses:
45+ // 1 Ones that are defined within the current SBOM, for example those with the "LicenseRef" prefix.
46+ // 2 Ones that reference other SBOM documents, for example those with the "DocumentRef" prefix.
47+ // We only process custom licenses that are defined in the current document.
48+ . filter ( |e| e. license_ref && e. document_ref . is_none ( ) )
49+ . map ( |e| format ! ( "LicenseRef-{}" , e. identifier) )
50+ . collect :: < Vec < _ > > ( ) ;
51+
52+ ( spdx_licenses, spdx_license_exceptions, custom_license_refs)
4253 } )
43- . unwrap_or ( ( vec ! [ ] , vec ! [ ] ) )
54+ . unwrap_or ( ( vec ! [ ] , vec ! [ ] , vec ! [ ] ) )
4455 }
4556}
4657
@@ -51,19 +62,50 @@ pub struct LicenseCreator {
5162 /// Uses a [`BTreeMap`] to ensure we have a stable insertion order, avoiding deadlocks on the
5263 /// database.
5364 pub licenses : BTreeMap < Uuid , license:: ActiveModel > ,
65+
66+ pub custom_license_list : Vec < licensing_infos:: ActiveModel > ,
5467}
5568
5669impl LicenseCreator {
5770 pub fn new ( ) -> Self {
5871 Self {
5972 licenses : Default :: default ( ) ,
73+ custom_license_list : vec ! [ ] ,
6074 }
6175 }
6276
77+ pub fn put_custom_license_list (
78+ & mut self ,
79+ custom_license_list : Vec < licensing_infos:: ActiveModel > ,
80+ ) {
81+ self . custom_license_list = custom_license_list;
82+ }
83+
6384 pub fn add ( & mut self , info : & LicenseInfo ) {
6485 let uuid = info. uuid ( ) ;
6586
66- let ( spdx_licenses, spdx_exceptions) = info. spdx_info ( ) ;
87+ let ( spdx_licenses, spdx_exceptions, custom_license_refs) = info. spdx_info ( ) ;
88+ let missing_custom_refs: Vec < _ > = custom_license_refs
89+ . iter ( )
90+ . filter ( |ref_id| {
91+ !self
92+ . custom_license_list
93+ . iter ( )
94+ . any ( |c| c. license_id == Set ( ( * ref_id) . to_string ( ) ) )
95+ } )
96+ . cloned ( )
97+ . collect ( ) ;
98+ if !missing_custom_refs. is_empty ( ) {
99+ log:: warn!(
100+ "The following custom license refs are missing from custom_license_list: {:?}" ,
101+ missing_custom_refs
102+ ) ;
103+ }
104+ let custom_license_refs_value = if custom_license_refs. is_empty ( ) {
105+ None
106+ } else {
107+ Some ( self . construct_custom_license ( custom_license_refs. clone ( ) ) )
108+ } ;
67109
68110 self . licenses . entry ( uuid) . or_insert ( license:: ActiveModel {
69111 id : Set ( uuid) ,
@@ -78,9 +120,30 @@ impl LicenseCreator {
78120 } else {
79121 Set ( Some ( spdx_exceptions) )
80122 } ,
123+ custom_license_refs : Set ( custom_license_refs_value) ,
81124 } ) ;
82125 }
83126
127+ fn construct_custom_license ( & self , custom_license_ids : Vec < String > ) -> Vec < String > {
128+ use std:: collections:: HashMap ;
129+ // Build a HashMap from license_id to name for fast lookup
130+ let license_map: HashMap < & String , & String > = self
131+ . custom_license_list
132+ . iter ( )
133+ . filter_map ( |c| {
134+ if let ( Set ( license_id) , Set ( name) ) = ( & c. license_id , & c. name ) {
135+ Some ( ( license_id, name) )
136+ } else {
137+ None
138+ }
139+ } )
140+ . collect ( ) ;
141+ custom_license_ids
142+ . into_iter ( )
143+ . filter_map ( |id| license_map. get ( & id) . map ( |name| format ! ( "{}:{}" , id, name) ) )
144+ . collect ( )
145+ }
146+
84147 #[ instrument( skip_all, fields( num = self . licenses. len( ) ) , err( level=tracing:: Level :: INFO ) ) ]
85148 pub async fn create < C > ( self , db : & C ) -> Result < ( ) , DbErr >
86149 where
0 commit comments