1 // Copyright (C) 2024 The Android Open Source Project 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 use spdx::LicenseReq; 16 use std::collections::BTreeSet; 17 18 use crate::Error; 19 20 /// The result of parsing and evaluating an SPDX license expression. 21 #[derive(Debug, PartialEq)] 22 pub(crate) struct LicenseTerms { 23 /// The license terms we are required to follow. 24 pub required: BTreeSet<LicenseReq>, 25 /// Additional terms mentioned in the license expression that we 26 /// do not need to follow. 27 pub not_required: BTreeSet<LicenseReq>, 28 } 29 30 impl LicenseTerms { try_from( expr: spdx::Expression, preferences: &Vec<spdx::Licensee>, ) -> Result<LicenseTerms, Error>31 pub fn try_from( 32 expr: spdx::Expression, 33 preferences: &Vec<spdx::Licensee>, 34 ) -> Result<LicenseTerms, Error> { 35 let required = BTreeSet::from_iter(expr.minimized_requirements(preferences)?); 36 let not_required = expr 37 .requirements() 38 .filter_map( 39 |req| if !required.contains(&req.req) { Some(req.req.clone()) } else { None }, 40 ) 41 .collect::<BTreeSet<_>>(); 42 Ok(LicenseTerms { required, not_required }) 43 } 44 } 45 46 #[cfg(test)] 47 mod tests { 48 use spdx::Licensee; 49 50 use super::*; 51 52 #[test] try_from()53 fn try_from() { 54 assert_eq!( 55 LicenseTerms::try_from( 56 spdx::Expression::parse("Apache-2.0").unwrap(), 57 &vec![spdx::Licensee::parse("Apache-2.0").unwrap()] 58 ) 59 .unwrap(), 60 LicenseTerms { 61 required: BTreeSet::from([Licensee::parse("Apache-2.0").unwrap().into_req()]), 62 not_required: BTreeSet::new() 63 }, 64 "Simple case" 65 ); 66 assert_eq!( 67 LicenseTerms::try_from( 68 spdx::Expression::parse("Apache-2.0 OR MIT").unwrap(), 69 &vec![spdx::Licensee::parse("Apache-2.0").unwrap()] 70 ) 71 .unwrap(), 72 LicenseTerms { 73 required: BTreeSet::from([Licensee::parse("Apache-2.0").unwrap().into_req()]), 74 not_required: BTreeSet::from([Licensee::parse("MIT").unwrap().into_req()]) 75 }, 76 "Preference" 77 ); 78 assert!( 79 matches!( 80 LicenseTerms::try_from( 81 spdx::Expression::parse("Apache-2.0").unwrap(), 82 &vec![spdx::Licensee::parse("MIT").unwrap()] 83 ), 84 Err(Error::MinimizeError(_)) 85 ), 86 "No acceptable licenses" 87 ); 88 assert_eq!( 89 LicenseTerms::try_from( 90 spdx::Expression::parse("Apache-2.0 AND MIT").unwrap(), 91 &vec![ 92 spdx::Licensee::parse("Apache-2.0").unwrap(), 93 spdx::Licensee::parse("MIT").unwrap() 94 ] 95 ) 96 .unwrap(), 97 LicenseTerms { 98 required: BTreeSet::from([ 99 Licensee::parse("Apache-2.0").unwrap().into_req(), 100 Licensee::parse("MIT").unwrap().into_req() 101 ]), 102 not_required: BTreeSet::new(), 103 }, 104 "AND" 105 ); 106 assert_eq!( 107 LicenseTerms::try_from( 108 spdx::Expression::parse("MIT AND (MIT OR Apache-2.0)").unwrap(), 109 &vec![ 110 spdx::Licensee::parse("Apache-2.0").unwrap(), 111 spdx::Licensee::parse("MIT").unwrap() 112 ] 113 ) 114 .unwrap(), 115 LicenseTerms { 116 required: BTreeSet::from([Licensee::parse("MIT").unwrap().into_req()]), 117 not_required: BTreeSet::from([Licensee::parse("Apache-2.0").unwrap().into_req(),]), 118 }, 119 "Complex expression from libm 0.2.11" 120 ); 121 } 122 } 123