wasm_metadata/oci_annotations/
licenses.rs1use std::borrow::Cow;
2use std::fmt::{self, Display};
3use std::str::FromStr;
4
5use anyhow::{ensure, Error, Result};
6use serde::Serialize;
7use wasm_encoder::{ComponentSection, CustomSection, Encode, Section};
8use wasmparser::CustomSectionReader;
9
10#[derive(Debug, Clone, PartialEq)]
12pub struct Licenses(CustomSection<'static>);
13
14impl Licenses {
15 pub fn new(s: &str) -> Result<Self> {
17 Ok(spdx::Expression::parse(s)?.into())
18 }
19
20 pub(crate) fn parse_custom_section(reader: &CustomSectionReader<'_>) -> Result<Self> {
22 ensure!(
23 reader.name() == "licenses",
24 "The `licenses` custom section should have a name of 'license'"
25 );
26 let data = String::from_utf8(reader.data().to_owned())?;
27 Self::new(&data)
28 }
29}
30
31impl FromStr for Licenses {
32 type Err = Error;
33
34 fn from_str(s: &str) -> Result<Self, Self::Err> {
35 Self::new(s)
36 }
37}
38
39impl From<spdx::Expression> for Licenses {
40 fn from(expression: spdx::Expression) -> Self {
41 Self(CustomSection {
42 name: "licenses".into(),
43 data: Cow::Owned(expression.to_string().into_bytes()),
44 })
45 }
46}
47
48impl Serialize for Licenses {
49 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
50 where
51 S: serde::Serializer,
52 {
53 serializer.serialize_str(&self.to_string())
54 }
55}
56
57impl Display for Licenses {
58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 let data = String::from_utf8(self.0.data.to_vec()).unwrap();
62 write!(f, "{data}")
63 }
64}
65
66impl ComponentSection for Licenses {
67 fn id(&self) -> u8 {
68 ComponentSection::id(&self.0)
69 }
70}
71
72impl Section for Licenses {
73 fn id(&self) -> u8 {
74 Section::id(&self.0)
75 }
76}
77
78impl Encode for Licenses {
79 fn encode(&self, sink: &mut Vec<u8>) {
80 self.0.encode(sink);
81 }
82}
83
84#[cfg(test)]
85mod test {
86 use super::*;
87 use wasm_encoder::Component;
88 use wasmparser::Payload;
89
90 #[test]
91 fn roundtrip() {
92 let mut component = Component::new();
93 component.section(
94 &Licenses::new("Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT").unwrap(),
95 );
96 let component = component.finish();
97
98 let mut parsed = false;
99 for section in wasmparser::Parser::new(0).parse_all(&component) {
100 if let Payload::CustomSection(reader) = section.unwrap() {
101 let description = Licenses::parse_custom_section(&reader).unwrap();
102 assert_eq!(
103 description.to_string(),
104 "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
105 );
106 parsed = true;
107 }
108 }
109 assert!(parsed);
110 }
111
112 #[test]
113 fn serialize() {
114 let description =
115 Licenses::new("Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT").unwrap();
116 let json = serde_json::to_string(&description).unwrap();
117 assert_eq!(
118 r#""Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT""#,
119 json
120 );
121 }
122}