wasm_runtime/
manifest.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5pub struct GlobalManifest {
6    pub version: String,
7    #[serde(default)]
8    pub languages: HashMap<String, RuntimeInfo>,
9}
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
12pub struct RuntimeInfo {
13    pub latest: String,
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub lts: Option<String>,
16    pub versions: Vec<String>,
17    pub source: String,
18    pub license: String,
19}
20
21#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
22pub struct RuntimeManifest {
23    pub language: String,
24    pub versions: HashMap<String, RuntimeVersion>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
28pub struct RuntimeVersion {
29    pub file: String,
30    pub size: u64,
31    pub sha256: String,
32    pub released: String,
33    #[serde(default)]
34    pub wasi: bool,
35    #[serde(default)]
36    pub features: Vec<String>,
37    pub url: String,
38}
39
40impl GlobalManifest {
41    pub fn new(version: String) -> Self {
42        Self {
43            version,
44            languages: HashMap::new(),
45        }
46    }
47
48    pub fn add_language(&mut self, name: String, info: RuntimeInfo) {
49        self.languages.insert(name, info);
50    }
51
52    pub fn get_language(&self, name: &str) -> Option<&RuntimeInfo> {
53        self.languages.get(name)
54    }
55}
56
57impl RuntimeInfo {
58    pub fn new(latest: String, source: String, license: String) -> Self {
59        Self {
60            latest,
61            lts: None,
62            versions: Vec::new(),
63            source,
64            license,
65        }
66    }
67
68    pub fn with_lts(mut self, lts: String) -> Self {
69        self.lts = Some(lts);
70        self
71    }
72
73    pub fn add_version(&mut self, version: String) {
74        if !self.versions.contains(&version) {
75            self.versions.push(version);
76        }
77    }
78}
79
80impl RuntimeManifest {
81    pub fn new(language: String) -> Self {
82        Self {
83            language,
84            versions: HashMap::new(),
85        }
86    }
87
88    pub fn add_version(&mut self, version: String, info: RuntimeVersion) {
89        self.versions.insert(version, info);
90    }
91
92    pub fn get_version(&self, version: &str) -> Option<&RuntimeVersion> {
93        self.versions.get(version)
94    }
95}
96
97impl RuntimeVersion {
98    pub fn new(file: String, size: u64, sha256: String, released: String, url: String) -> Self {
99        Self {
100            file,
101            size,
102            sha256,
103            released,
104            wasi: false,
105            features: Vec::new(),
106            url,
107        }
108    }
109
110    pub fn with_wasi(mut self, wasi: bool) -> Self {
111        self.wasi = wasi;
112        self
113    }
114
115    pub fn add_feature(&mut self, feature: String) {
116        if !self.features.contains(&feature) {
117            self.features.push(feature);
118        }
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_global_manifest() {
128        let mut manifest = GlobalManifest::new("1.0.0".to_string());
129        assert_eq!(manifest.version, "1.0.0");
130        assert_eq!(manifest.languages.len(), 0);
131
132        let runtime_info = RuntimeInfo::new(
133            "3.11.7".to_string(),
134            "https://github.com/pyodide/pyodide".to_string(),
135            "MIT".to_string(),
136        );
137        manifest.add_language("python".to_string(), runtime_info);
138        assert_eq!(manifest.languages.len(), 1);
139        assert!(manifest.get_language("python").is_some());
140    }
141
142    #[test]
143    fn test_runtime_info() {
144        let mut info = RuntimeInfo::new(
145            "20.2.0".to_string(),
146            "https://nodejs.org".to_string(),
147            "MIT".to_string(),
148        );
149        assert_eq!(info.latest, "20.2.0");
150        assert!(info.lts.is_none());
151
152        info = info.with_lts("18.19.0".to_string());
153        assert_eq!(info.lts, Some("18.19.0".to_string()));
154
155        info.add_version("20.2.0".to_string());
156        info.add_version("18.19.0".to_string());
157        assert_eq!(info.versions.len(), 2);
158
159        info.add_version("20.2.0".to_string());
160        assert_eq!(info.versions.len(), 2);
161    }
162
163    #[test]
164    fn test_runtime_manifest() {
165        let mut manifest = RuntimeManifest::new("python".to_string());
166        assert_eq!(manifest.language, "python");
167        assert_eq!(manifest.versions.len(), 0);
168
169        let version = RuntimeVersion::new(
170            "python-3.11.7.wasm".to_string(),
171            1024,
172            "abc123".to_string(),
173            "2024-01-01".to_string(),
174            "https://example.com/python-3.11.7.wasm".to_string(),
175        );
176        manifest.add_version("3.11.7".to_string(), version);
177        assert_eq!(manifest.versions.len(), 1);
178        assert!(manifest.get_version("3.11.7").is_some());
179    }
180
181    #[test]
182    fn test_runtime_version() {
183        let mut version = RuntimeVersion::new(
184            "python-3.11.7.wasm".to_string(),
185            1024,
186            "abc123".to_string(),
187            "2024-01-01".to_string(),
188            "https://example.com/python-3.11.7.wasm".to_string(),
189        );
190        assert_eq!(version.file, "python-3.11.7.wasm");
191        assert_eq!(version.size, 1024);
192        assert!(!version.wasi);
193
194        version = version.with_wasi(true);
195        assert!(version.wasi);
196
197        version.add_feature("async".to_string());
198        version.add_feature("filesystem".to_string());
199        assert_eq!(version.features.len(), 2);
200
201        version.add_feature("async".to_string());
202        assert_eq!(version.features.len(), 2);
203    }
204
205    #[test]
206    fn test_manifest_serialization() {
207        let manifest = GlobalManifest {
208            version: "1.0.0".to_string(),
209            languages: {
210                let mut map = HashMap::new();
211                map.insert(
212                    "python".to_string(),
213                    RuntimeInfo {
214                        latest: "3.11.7".to_string(),
215                        lts: None,
216                        versions: vec!["3.11.7".to_string()],
217                        source: "https://github.com/pyodide/pyodide".to_string(),
218                        license: "MIT".to_string(),
219                    },
220                );
221                map
222            },
223        };
224
225        let json = serde_json::to_string(&manifest).unwrap();
226        let parsed: GlobalManifest = serde_json::from_str(&json).unwrap();
227        assert_eq!(manifest, parsed);
228    }
229}