wasm_metadata/
dependencies.rs1use std::fmt::{self, Display};
2use std::io::{read_to_string, Read};
3use std::str::FromStr;
4
5use anyhow::{ensure, Result};
6use auditable_serde::VersionInfo;
7use flate2::read::{ZlibDecoder, ZlibEncoder};
8use flate2::Compression;
9use serde::Serialize;
10use wasm_encoder::{ComponentSection, CustomSection, Encode, Section};
11use wasmparser::CustomSectionReader;
12
13#[derive(Debug, Clone, PartialEq)]
15pub struct Dependencies {
16 version_info: VersionInfo,
17 custom_section: CustomSection<'static>,
18}
19
20impl Dependencies {
21 pub(crate) fn parse_custom_section(reader: &CustomSectionReader<'_>) -> Result<Self> {
23 ensure!(
24 reader.name() == ".dep-v0",
25 "The `dependencies` custom section should have a name of '.dep-v0'"
26 );
27 let decompressed_data = read_to_string(ZlibDecoder::new(reader.data()))?;
28 let dependency_tree = auditable_serde::VersionInfo::from_str(&decompressed_data)?;
29
30 Ok(Self {
31 version_info: dependency_tree,
32 custom_section: CustomSection {
33 name: ".dep-v0".into(),
34 data: reader.data().to_owned().into(),
35 },
36 })
37 }
38
39 pub fn new(dependency_tree: auditable_serde::VersionInfo) -> Self {
41 let data = serde_json::to_string(&dependency_tree).unwrap();
42
43 let mut ret_vec = Vec::new();
44 let mut encoder = ZlibEncoder::new(data.as_bytes(), Compression::fast());
45 encoder.read_to_end(&mut ret_vec).unwrap();
46
47 Self {
48 version_info: dependency_tree,
49 custom_section: CustomSection {
50 name: ".dep-v0".into(),
51 data: ret_vec.into(),
52 },
53 }
54 }
55
56 pub fn version_info(&self) -> &VersionInfo {
58 &self.version_info
59 }
60}
61
62impl Serialize for Dependencies {
63 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
64 where
65 S: serde::Serializer,
66 {
67 serializer.serialize_str(&self.to_string())
68 }
69}
70
71impl Display for Dependencies {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 let data = serde_json::to_string(&self.version_info).unwrap();
77 write!(f, "{}", data)
78 }
79}
80
81impl ComponentSection for Dependencies {
82 fn id(&self) -> u8 {
83 ComponentSection::id(&self.custom_section)
84 }
85}
86
87impl Section for Dependencies {
88 fn id(&self) -> u8 {
89 Section::id(&self.custom_section)
90 }
91}
92
93impl Encode for Dependencies {
94 fn encode(&self, sink: &mut Vec<u8>) {
95 self.custom_section.encode(sink);
96 }
97}
98
99#[cfg(test)]
100mod test {
101 use super::*;
102 use auditable_serde::{Source, VersionInfo};
103 use std::str::FromStr;
104 use wasm_encoder::Component;
105 use wasmparser::Payload;
106
107 #[test]
108 fn roundtrip() {
109 let json_str = r#"{"packages":[{"name":"adler","version":"0.2.3","source":"registry"}]}"#;
110 let info = VersionInfo::from_str(json_str).unwrap();
111 assert_eq!(&info.packages[0].name, "adler");
112 let mut component = Component::new();
113 component.section(&Dependencies::new(info));
114 let component = component.finish();
115
116 let mut parsed = false;
117 for section in wasmparser::Parser::new(0).parse_all(&component) {
118 if let Payload::CustomSection(reader) = section.unwrap() {
119 let dependencies = Dependencies::parse_custom_section(&reader).unwrap();
120 assert_eq!(dependencies.to_string(), json_str);
121 parsed = true;
122 }
123 }
124 assert!(parsed);
125 }
126
127 #[test]
128 fn serialize() {
129 let json_str = r#"{"packages":[{"name":"adler","version":"0.2.3","source":"registry"}]}"#;
130 let info = VersionInfo::from_str(json_str).unwrap();
131 let dependencies = Dependencies::new(info);
132 assert_eq!(dependencies.version_info().packages[0].name, "adler");
133 assert_eq!(
134 dependencies.version_info().packages[0].version.to_string(),
135 "0.2.3"
136 );
137 assert_eq!(
138 dependencies.version_info().packages[0].source,
139 Source::Registry,
140 );
141 }
142}