1 // SPDX-FileCopyrightText: 2022 HH Partners
3 // SPDX-License-Identifier: MIT
5 //! Private inner structs for [`crate::SpdxExpression`].
7 use std
::{collections::HashSet, fmt::Display}
;
10 use serde
::{de::Visitor, Deserialize, Serialize}
;
13 error
::SpdxExpressionError
,
14 parser
::{parse_expression, simple_expression}
,
17 /// Simple SPDX license expression.
18 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
19 pub struct SimpleExpression
{
20 /// The license identifier.
21 pub identifier
: String
,
23 /// Optional DocumentRef for the expression.
24 pub document_ref
: Option
<String
>,
26 /// `true` if the expression is a user defined license reference.
27 pub license_ref
: bool
,
30 impl Serialize
for SimpleExpression
{
31 fn serialize
<S
>(&self, serializer
: S
) -> Result
<S
::Ok
, S
::Error
>
35 serializer
.collect_str(self)
39 struct SimpleExpressionVisitor
;
41 impl<'de
> Visitor
<'de
> for SimpleExpressionVisitor
{
42 type Value
= SimpleExpression
;
44 fn expecting(&self, formatter
: &mut std
::fmt
::Formatter
) -> std
::fmt
::Result
{
45 formatter
.write_str("a syntactically valid SPDX simple expression")
48 fn visit_str
<E
>(self, v
: &str) -> Result
<Self::Value
, E
>
52 SimpleExpression
::parse(v
)
53 .map_err(|err
| E
::custom(format
!("error parsing the expression: {}", err
)))
56 fn visit_borrowed_str
<E
>(self, v
: &'de
str) -> Result
<Self::Value
, E
>
63 fn visit_string
<E
>(self, v
: String
) -> Result
<Self::Value
, E
>
71 impl<'de
> Deserialize
<'de
> for SimpleExpression
{
72 fn deserialize
<D
>(deserializer
: D
) -> Result
<Self, D
::Error
>
74 D
: serde
::Deserializer
<'de
>,
76 deserializer
.deserialize_str(SimpleExpressionVisitor
)
80 impl Display
for SimpleExpression
{
81 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
82 let document_ref
= match &self.document_ref
{
83 Some(document_ref
) => {
84 format
!("DocumentRef-{}:", document_ref
)
86 None
=> "".to_string(),
89 let license_ref
= if self.license_ref { "LicenseRef-" }
else { "" }
;
92 "{document_ref}{license_ref}{identifier}",
93 identifier
= self.identifier
98 impl SimpleExpression
{
99 /// Create a new simple expression.
100 pub const fn new(identifier
: String
, document_ref
: Option
<String
>, license_ref
: bool
) -> Self {
108 /// Parse a simple expression.
113 /// # use spdx_expression::SimpleExpression;
114 /// # use spdx_expression::SpdxExpressionError;
116 /// let expression = SimpleExpression::parse("MIT")?;
117 /// # Ok::<(), SpdxExpressionError>(())
120 /// The function will only accept simple expressions, compound expressions will fail.
123 /// # use spdx_expression::SimpleExpression;
125 /// let expression = SimpleExpression::parse("MIT OR ISC");
126 /// assert!(expression.is_err());
131 /// Fails if parsing fails.
132 pub fn parse(expression
: &str) -> Result
<Self, SpdxExpressionError
> {
133 let (remaining
, result
) = simple_expression(expression
)?
;
135 if remaining
.is_empty() {
138 Err(SpdxExpressionError
::Parse(expression
.to_string()))
143 #[derive(Debug, Clone, PartialEq, Eq)]
144 pub struct WithExpression
{
145 pub license
: SimpleExpression
,
146 pub exception
: String
,
149 impl WithExpression
{
150 pub const fn new(license
: SimpleExpression
, exception
: String
) -> Self {
151 Self { license, exception }
155 impl Display
for WithExpression
{
156 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
159 "{license} WITH {exception}",
160 license
= self.license
,
161 exception
= self.exception
166 #[derive(Debug, PartialEq, Clone, Eq)]
167 pub enum ExpressionVariant
{
168 Simple(SimpleExpression
),
169 With(WithExpression
),
170 And(Box
<Self>, Box
<Self>),
171 Or(Box
<Self>, Box
<Self>),
175 impl Display
for ExpressionVariant
{
176 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
177 use self::ExpressionVariant
::{And, Or, Parens, Simple, With}
;
180 Simple(expression
) => write
!(f
, "{expression}"),
181 With(expression
) => write
!(f
, "{expression}"),
182 And(left
, right
) => write
!(f
, "{left} AND {right}"),
183 Or(left
, right
) => write
!(f
, "{left} OR {right}"),
184 Parens(expression
) => write
!(f
, "({expression})"),
189 impl ExpressionVariant
{
190 pub fn parse(i
: &str) -> Result
<Self, SpdxExpressionError
> {
191 let (remaining
, expression
) = parse_expression(i
)
193 .map_err(|_
| SpdxExpressionError
::Parse(i
.to_string()))?
;
195 if remaining
.is_empty() {
198 Err(SpdxExpressionError
::Parse(i
.to_string()))
202 pub fn licenses(&self) -> HashSet
<&SimpleExpression
> {
203 let mut expressions
= HashSet
::new();
206 ExpressionVariant
::Simple(expression
) => {
207 expressions
.insert(expression
);
209 ExpressionVariant
::With(expression
) => {
210 expressions
.insert(&expression
.license
);
212 ExpressionVariant
::And(left
, right
) | ExpressionVariant
::Or(left
, right
) => {
213 expressions
.extend(left
.licenses());
214 expressions
.extend(right
.licenses());
216 ExpressionVariant
::Parens(expression
) => {
217 expressions
.extend(expression
.licenses());
224 pub fn exceptions(&self) -> HashSet
<&str> {
225 let mut expressions
= HashSet
::new();
228 ExpressionVariant
::Simple(_
) => {}
229 ExpressionVariant
::With(expression
) => {
230 expressions
.insert(expression
.exception
.as_str());
232 ExpressionVariant
::And(left
, right
) | ExpressionVariant
::Or(left
, right
) => {
233 expressions
.extend(left
.exceptions());
234 expressions
.extend(right
.exceptions());
236 ExpressionVariant
::Parens(expression
) => {
237 expressions
.extend(expression
.exceptions());
247 use std
::iter
::FromIterator
;
249 use serde_json
::Value
;
254 fn display_simple_correctly() {
256 ExpressionVariant
::Simple(SimpleExpression
::new("MIT".to_string(), None
, false));
257 assert_eq
!(expression
.to_string(), "MIT".to_string());
261 fn display_licenseref_correctly() {
263 ExpressionVariant
::Simple(SimpleExpression
::new("license".to_string(), None
, true));
264 assert_eq
!(expression
.to_string(), "LicenseRef-license".to_string());
268 fn display_documentref_correctly() {
269 let expression
= ExpressionVariant
::Simple(SimpleExpression
::new(
270 "license".to_string(),
271 Some("document".to_string()),
275 expression
.to_string(),
276 "DocumentRef-document:LicenseRef-license".to_string()
281 fn display_with_expression_correctly() {
282 let expression
= ExpressionVariant
::With(WithExpression
::new(
283 SimpleExpression
::new("license".to_string(), None
, false),
284 "exception".to_string(),
286 assert_eq
!(expression
.to_string(), "license WITH exception".to_string());
290 fn display_and_expression_correctly() {
291 let expression
= ExpressionVariant
::And(
292 Box
::new(ExpressionVariant
::And(
293 Box
::new(ExpressionVariant
::Simple(SimpleExpression
::new(
294 "license1".to_string(),
298 Box
::new(ExpressionVariant
::Simple(SimpleExpression
::new(
299 "license2".to_string(),
304 Box
::new(ExpressionVariant
::Simple(SimpleExpression
::new(
305 "license3".to_string(),
311 expression
.to_string(),
312 "license1 AND license2 AND license3".to_string()
317 fn display_or_expression_correctly() {
318 let expression
= ExpressionVariant
::Or(
319 Box
::new(ExpressionVariant
::Or(
320 Box
::new(ExpressionVariant
::Simple(SimpleExpression
::new(
321 "license1".to_string(),
325 Box
::new(ExpressionVariant
::Simple(SimpleExpression
::new(
326 "license2".to_string(),
331 Box
::new(ExpressionVariant
::Simple(SimpleExpression
::new(
332 "license3".to_string(),
338 expression
.to_string(),
339 "license1 OR license2 OR license3".to_string()
344 fn get_licenses_correctly() {
345 let expression
= ExpressionVariant
::And(
346 Box
::new(ExpressionVariant
::Simple(SimpleExpression
::new(
347 "license1+".to_string(),
351 Box
::new(ExpressionVariant
::Parens(Box
::new(ExpressionVariant
::Or(
352 Box
::new(ExpressionVariant
::Parens(Box
::new(
353 ExpressionVariant
::With(WithExpression
::new(
354 SimpleExpression
::new("license2".to_string(), None
, false),
355 "exception1".to_string(),
358 Box
::new(ExpressionVariant
::And(
359 Box
::new(ExpressionVariant
::Simple(SimpleExpression
::new(
360 "license3+".to_string(),
364 Box
::new(ExpressionVariant
::With(WithExpression
::new(
365 SimpleExpression
::new("license4".to_string(), None
, false),
366 "exception2".to_string(),
373 expression
.licenses(),
375 &SimpleExpression
::new("license1+".to_string(), None
, false),
376 &SimpleExpression
::new("license2".to_string(), None
, false),
377 &SimpleExpression
::new("license3+".to_string(), None
, false),
378 &SimpleExpression
::new("license4".to_string(), None
, false),
383 fn get_exceptions_correctly() {
384 let expression
= ExpressionVariant
::And(
385 Box
::new(ExpressionVariant
::Simple(SimpleExpression
::new(
386 "license1+".to_string(),
390 Box
::new(ExpressionVariant
::Parens(Box
::new(ExpressionVariant
::Or(
391 Box
::new(ExpressionVariant
::Parens(Box
::new(
392 ExpressionVariant
::With(WithExpression
::new(
393 SimpleExpression
::new("license2".to_string(), None
, false),
394 "exception1".to_string(),
397 Box
::new(ExpressionVariant
::And(
398 Box
::new(ExpressionVariant
::Simple(SimpleExpression
::new(
399 "license3+".to_string(),
403 Box
::new(ExpressionVariant
::With(WithExpression
::new(
404 SimpleExpression
::new("license4".to_string(), None
, false),
405 "exception2".to_string(),
412 expression
.exceptions(),
413 HashSet
::from_iter(["exception1", "exception2"])
418 fn parse_simple_expression() {
419 let expression
= SimpleExpression
::parse("MIT").unwrap();
422 SimpleExpression
::new("MIT".to_string(), None
, false)
425 let expression
= SimpleExpression
::parse("MIT OR ISC");
426 assert
!(expression
.is_err());
428 let expression
= SimpleExpression
::parse("GPL-2.0-only WITH Classpath-exception-2.0");
429 assert
!(expression
.is_err());
433 fn serialize_simple_expression_correctly() {
434 let expression
= SimpleExpression
::parse("MIT").unwrap();
436 let value
= serde_json
::to_value(expression
).unwrap();
438 assert_eq
!(value
, Value
::String("MIT".to_string()));
442 fn deserialize_simple_expression_correctly() {
443 let expected
= SimpleExpression
::parse("LicenseRef-license1").unwrap();
445 let value
= Value
::String("LicenseRef-license1".to_string());
447 let actual
: SimpleExpression
= serde_json
::from_value(value
).unwrap();
449 assert_eq
!(actual
, expected
);