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