generated from EmbarkStudios/opensource-template
-
Notifications
You must be signed in to change notification settings - Fork 20
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Use git clone rather than doing HTTP reqeusts * Add feature to include full license text * Rename expression/mod => expression * Add exception text as well * Add license minimization * Add docs
- Loading branch information
1 parent
3bd2231
commit 779b720
Showing
528 changed files
with
38,152 additions
and
238 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
/target | ||
update/target | ||
**/*.rs.bk | ||
Cargo.lock | ||
Cargo.lock | ||
spdx-data |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
use crate::{LicenseReq, Licensee}; | ||
use std::fmt; | ||
|
||
/// Errors that can occur when trying to minimize the requirements for an [`Expression`] | ||
#[derive(Debug, PartialEq)] | ||
pub enum MinimizeError { | ||
/// More than `64` unique licensees satisfied a requirement in the [`Expression`] | ||
TooManyRequirements(usize), | ||
/// The list of licensees did not fully satisfy the requirements in the [`Expression`] | ||
RequirementsUnmet, | ||
} | ||
|
||
impl fmt::Display for MinimizeError { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Self::TooManyRequirements(n) => write!( | ||
f, | ||
"the license expression required {} licensees which exceeds the limit of 64", | ||
n | ||
), | ||
Self::RequirementsUnmet => { | ||
f.write_str("the expression was not satisfied by the provided list of licensees") | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl std::error::Error for MinimizeError { | ||
fn description(&self) -> &str { | ||
match self { | ||
Self::TooManyRequirements(_) => "too many requirements in license expression", | ||
Self::RequirementsUnmet => { | ||
"the expression was not satisfied by the provided list of licensees" | ||
} | ||
} | ||
} | ||
} | ||
|
||
impl super::Expression { | ||
/// Given a set of [`Licensee`]s, attempts to find the minimum number that | ||
/// satisfy this [`Expression`]. The list of licensees should be given in | ||
/// priority order, eg, if you wish to accept the `Apache-2.0` license if | ||
/// it is available and the `MIT` if not, putting `Apache-2.0` before `MIT` | ||
/// will cause the ubiquitous `Apache-2.0 OR MIT` expression to minimize to | ||
/// just `Apache-2.0` as only only 1 of the licenses is required, and the | ||
/// `Apache-2.0` has priority. | ||
/// | ||
/// # Errors | ||
/// | ||
/// This method will fail if more than 64 unique licensees are satisfied by | ||
/// this expression, but such a case is unlikely to say the least in a real | ||
/// world scenario. The list of licensees must also actually satisfy this | ||
/// expression, otherwise it can't be minimized. | ||
/// | ||
/// # Example | ||
/// | ||
/// ``` | ||
/// let expr = spdx::Expression::parse("Apache-2.0 OR MIT").unwrap(); | ||
/// | ||
/// let apache_licensee = spdx::Licensee::parse("Apache-2.0").unwrap(); | ||
/// assert_eq!( | ||
/// expr.minimized_requirements([&apache_licensee, &spdx::Licensee::parse("MIT").unwrap()]).unwrap(), | ||
/// vec![apache_licensee.into_req()], | ||
/// ); | ||
/// ``` | ||
pub fn minimized_requirements<'lic>( | ||
&self, | ||
accepted: impl IntoIterator<Item = &'lic Licensee>, | ||
) -> Result<Vec<LicenseReq>, MinimizeError> { | ||
let found_set = { | ||
let mut found_set = smallvec::SmallVec::<[Licensee; 5]>::new(); | ||
|
||
for lic in accepted { | ||
if !found_set.contains(lic) | ||
&& self.requirements().any(|ereq| lic.satisfies(&ereq.req)) | ||
{ | ||
found_set.push(lic.clone()); | ||
} | ||
} | ||
|
||
if found_set.len() > 64 { | ||
return Err(MinimizeError::TooManyRequirements(found_set.len())); | ||
} | ||
|
||
// Ensure that the licensees provided actually _can_ be accepted by | ||
// this expression | ||
if !self.evaluate(|ereq| found_set.iter().any(|lic| lic.satisfies(ereq))) { | ||
return Err(MinimizeError::RequirementsUnmet); | ||
} | ||
|
||
found_set | ||
}; | ||
|
||
let set_size = (1 << found_set.len()) as u64; | ||
|
||
for mask in 1..=set_size { | ||
let eval_res = self.evaluate(|req| { | ||
for (ind, lic) in found_set.iter().enumerate() { | ||
if mask & (1 << ind) != 0 && lic.satisfies(req) { | ||
return true; | ||
} | ||
} | ||
|
||
false | ||
}); | ||
|
||
if eval_res { | ||
return Ok(found_set | ||
.into_iter() | ||
.enumerate() | ||
.filter_map(|(ind, lic)| { | ||
if mask & (1 << ind) != 0 { | ||
Some(lic.into_req()) | ||
} else { | ||
None | ||
} | ||
}) | ||
.collect()); | ||
} | ||
} | ||
|
||
// This should be impossible, but would rather not panic | ||
Ok(found_set.into_iter().map(|lic| lic.into_req()).collect()) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.