license_exprs/
lib.rs

1use std::error::Error;
2use std::fmt;
3mod spdx;
4
5use self::LicenseExpr::*;
6
7#[derive(Debug, Clone, Copy)]
8pub enum LicenseExpr<'a> {
9    License(&'a str),
10    Exception(&'a str),
11    And,
12    Or,
13    With,
14}
15
16impl<'a> fmt::Display for LicenseExpr<'a> {
17    fn fmt(&self, format: &mut fmt::Formatter) -> Result<(), fmt::Error> {
18        match *self {
19            With => format.write_str("WITH"),
20            And => format.write_str("AND"),
21            Or => format.write_str("OR"),
22            License(info) | Exception(info) => format.write_str(info),
23        }
24    }
25}
26
27#[derive(Debug, Clone, Copy)]
28pub enum ParseError<'a> {
29    UnknownLicenseId(&'a str),
30    InvalidStructure(LicenseExpr<'a>),
31}
32
33impl<'a> fmt::Display for ParseError<'a> {
34    fn fmt(&self, format: &mut fmt::Formatter) -> Result<(), fmt::Error> {
35        match *self {
36            ParseError::UnknownLicenseId(info) => {
37                format.write_fmt(format_args!("{}: {}", self.description(), info))
38            }
39            ParseError::InvalidStructure(info) => {
40                format.write_fmt(format_args!("{}: {}", self.description(), info))
41            }
42        }
43    }
44}
45
46impl<'a> Error for ParseError<'a> {
47    fn description(&self) -> &str {
48        match *self {
49            ParseError::UnknownLicenseId(_) => "unknown license or other term",
50            ParseError::InvalidStructure(_) => "invalid license expression",
51        }
52    }
53}
54
55pub fn validate_license_expr(license_expr: &str) -> Result<(), ParseError> {
56    license_expr
57        .split_whitespace()
58        .map(|word| match word {
59            "AND" => Ok(And),
60            "OR" => Ok(Or),
61            "WITH" => Ok(With),
62            _ if spdx::LICENSES
63                .binary_search(&word.trim_end_matches('+'))
64                .is_ok() =>
65            {
66                Ok(License(word))
67            }
68            _ if spdx::EXCEPTIONS.binary_search(&word).is_ok() => Ok(Exception(word)),
69            _ => Err(ParseError::UnknownLicenseId(word)),
70        })
71        .fold(Ok(Or), |prev, word| match (prev, word) {
72            (err @ Err(_), _) | (_, err @ Err(_)) => err,
73            (Ok(License(_)), Ok(With))
74            | (Ok(License(_)), Ok(And))
75            | (Ok(License(_)), Ok(Or))
76            | (Ok(Exception(_)), Ok(And))
77            | (Ok(Exception(_)), Ok(Or))
78            | (Ok(And), Ok(License(_)))
79            | (Ok(Or), Ok(License(_)))
80            | (Ok(With), Ok(Exception(_))) => word,
81            _ => Err(ParseError::InvalidStructure(word.unwrap())),
82        })
83        .and(Ok(()))
84}
85
86pub fn license_version() -> &'static str {
87    spdx::VERSION
88}