1pub(crate) mod manifest;
3#[allow(clippy::module_inception)]
4pub(crate) mod package;
5pub(crate) mod strictness;
6pub(crate) mod volume;
7
8pub use self::{
9 manifest::ManifestError,
10 package::{Package, WalkBuilderFactory, include_everything_walker, wasmer_ignore_walker},
11 strictness::Strictness,
12 volume::{WasmerPackageVolume, fs::*, in_memory::*},
13};
14pub use crate::{AuthoringError, WasmerPackageError};
15
16#[cfg(test)]
17mod tests {
18 use sha2::Digest;
19 use shared_buffer::OwnedBuffer;
20 use tempfile::TempDir;
21
22 use webc::{
23 metadata::annotations::FileSystemMapping,
24 migration::{are_semantically_equivalent, v2_to_v3, v3_to_v2},
25 };
26
27 use crate::{package::Package, utils::from_bytes};
28
29 #[test]
30 fn migration_roundtrip() {
31 let temp = TempDir::new().unwrap();
32 let wasmer_toml = r#"
33 [package]
34 name = "some/package"
35 version = "0.0.0"
36 description = "Test package"
37 [fs]
38 "/first" = "first"
39 second = "nested/dir"
40 "second/child" = "third"
41 empty = "empty"
42 "#;
43 let manifest = temp.path().join("wasmer.toml");
44 std::fs::write(&manifest, wasmer_toml).unwrap();
45 let first = temp.path().join("first");
60 std::fs::create_dir_all(&first).unwrap();
61 std::fs::write(first.join("file.txt"), "File").unwrap();
62 let second = temp.path().join("nested").join("dir");
64 std::fs::create_dir_all(&second).unwrap();
65 std::fs::write(second.join("README.md"), "please").unwrap();
66 let another_dir = temp.path().join("nested").join("dir").join("another-dir");
67 std::fs::create_dir_all(&another_dir).unwrap();
68 std::fs::write(another_dir.join("empty.txt"), "").unwrap();
69 let third = temp.path().join("third");
71 std::fs::create_dir_all(&third).unwrap();
72 std::fs::write(third.join("file.txt"), "Hello, World!").unwrap();
73 let empty_dir = temp.path().join("empty");
75 std::fs::create_dir_all(empty_dir).unwrap();
76
77 let package = Package::from_manifest(manifest).unwrap();
78
79 let webc = package.serialize().unwrap();
80
81 let webc_v2 = v3_to_v2(webc.clone()).unwrap();
82
83 are_semantically_equivalent(webc_v2.clone(), webc.into()).unwrap();
84
85 let container = from_bytes(webc_v2.clone().into_bytes()).unwrap();
86 let manifest = container.manifest();
87 let fs_table = manifest.filesystem().unwrap().unwrap();
88 assert_eq!(
89 fs_table,
90 [
91 FileSystemMapping {
92 from: None,
93 volume_name: "atom".to_string(),
94 host_path: Some("/first".to_string()),
95 mount_path: "/first".to_string(),
96 },
97 FileSystemMapping {
98 from: None,
99 volume_name: "atom".to_string(),
100 host_path: Some("/nested/dir".to_string()),
101 mount_path: "/second".to_string(),
102 },
103 FileSystemMapping {
104 from: None,
105 volume_name: "atom".to_string(),
106 host_path: Some("/third".to_string()),
107 mount_path: "/second/child".to_string(),
108 },
109 FileSystemMapping {
110 from: None,
111 volume_name: "atom".to_string(),
112 host_path: Some("/empty".to_string()),
113 mount_path: "/empty".to_string(),
114 },
115 ]
116 );
117
118 let atom_volume = container.get_volume("atom").unwrap();
119 assert_eq!(
120 atom_volume.read_file("/first/file.txt").unwrap(),
121 (OwnedBuffer::from(b"File".as_slice()), None)
122 );
123 assert_eq!(
124 atom_volume.read_file("/nested/dir/README.md").unwrap(),
125 (OwnedBuffer::from(b"please".as_slice()), None),
126 );
127 assert_eq!(
128 atom_volume
129 .read_file("/nested/dir/another-dir/empty.txt")
130 .unwrap(),
131 (OwnedBuffer::from(b"".as_slice()), None)
132 );
133 assert_eq!(
134 atom_volume.read_file("/third/file.txt").unwrap(),
135 (OwnedBuffer::from(b"Hello, World!".as_slice()), None)
136 );
137 assert_eq!(
138 atom_volume.read_dir("/empty").unwrap().len(),
139 0,
140 "Directories should be included, even if empty"
141 );
142
143 let webc_v3 = v2_to_v3(webc_v2.clone()).unwrap();
145
146 are_semantically_equivalent(webc_v2, webc_v3.clone()).unwrap();
147
148 let container = from_bytes(webc_v3.into_bytes()).unwrap();
149 let manifest = container.manifest();
150 let fs_table = manifest.filesystem().unwrap().unwrap();
151 assert_eq!(
152 fs_table,
153 [
154 FileSystemMapping {
155 from: None,
156 volume_name: "/first".to_string(),
157 host_path: None,
158 mount_path: "/first".to_string(),
159 },
160 FileSystemMapping {
161 from: None,
162 volume_name: "/nested/dir".to_string(),
163 host_path: None,
164 mount_path: "/second".to_string(),
165 },
166 FileSystemMapping {
167 from: None,
168 volume_name: "/third".to_string(),
169 host_path: None,
170 mount_path: "/second/child".to_string(),
171 },
172 FileSystemMapping {
173 from: None,
174 volume_name: "/empty".to_string(),
175 host_path: None,
176 mount_path: "/empty".to_string(),
177 },
178 ]
179 );
180
181 let first_file_hash: [u8; 32] = sha2::Sha256::digest(b"File").into();
182 let readme_hash: [u8; 32] = sha2::Sha256::digest(b"please").into();
183 let empty_hash: [u8; 32] = sha2::Sha256::digest(b"").into();
184 let third_file_hash: [u8; 32] = sha2::Sha256::digest(b"Hello, World!").into();
185
186 let first_volume = container.get_volume("/first").unwrap();
187 assert_eq!(
188 first_volume.read_file("/file.txt").unwrap(),
189 (b"File".as_slice().into(), Some(first_file_hash)),
190 );
191
192 let nested_dir_volume = container.get_volume("/nested/dir").unwrap();
193 assert_eq!(
194 nested_dir_volume.read_file("README.md").unwrap(),
195 (b"please".as_slice().into(), Some(readme_hash)),
196 );
197 assert_eq!(
198 nested_dir_volume
199 .read_file("/another-dir/empty.txt")
200 .unwrap(),
201 (b"".as_slice().into(), Some(empty_hash))
202 );
203
204 let third_volume = container.get_volume("/third").unwrap();
205 assert_eq!(
206 third_volume.read_file("/file.txt").unwrap(),
207 (b"Hello, World!".as_slice().into(), Some(third_file_hash))
208 );
209
210 let empty_volume = container.get_volume("/empty").unwrap();
211 assert_eq!(
212 empty_volume.read_dir("/").unwrap().len(),
213 0,
214 "Directories should be included, even if empty"
215 );
216 }
217
218 #[test]
219 fn fs_entry_is_not_required_for_migration() {
220 let temp = TempDir::new().unwrap();
221 let wasmer_toml = r#"
222 [package]
223 name = "some/package"
224 version = "0.0.0"
225 description = "Test package"
226 "#;
227 let manifest = temp.path().join("wasmer.toml");
228 std::fs::write(&manifest, wasmer_toml).unwrap();
229 let package = Package::from_manifest(manifest).unwrap();
230
231 let webc = package.serialize().unwrap();
232
233 let webc_v2 = v3_to_v2(webc).unwrap();
234 let container = from_bytes(webc_v2.clone().into_bytes()).unwrap();
235 let manifest = container.manifest();
236 assert!(manifest.filesystem().unwrap().is_none());
237
238 let webc_v3 = v2_to_v3(webc_v2).unwrap();
240 let container = from_bytes(webc_v3.into_bytes()).unwrap();
241 let manifest = container.manifest();
242 assert!(manifest.filesystem().unwrap().is_none());
243 }
244
245 #[test]
246 fn container_unpacks_atoms() {
247 let temp = TempDir::new().unwrap();
248 let wasmer_toml = r#"
249 [package]
250 name = "some/package"
251 version = "0.0.0"
252 description = "Test package"
253 [[module]]
254 name = "foo"
255 source = "foo.wasm"
256 abi = "wasi"
257 [fs]
258 "/bar" = "bar"
259 "#;
260
261 let manifest = temp.path().join("wasmer.toml");
262 std::fs::write(&manifest, wasmer_toml).unwrap();
263
264 let atom_path = temp.path().join("foo.wasm");
265 std::fs::write(&atom_path, b"").unwrap();
266
267 let bar = temp.path().join("bar");
268 std::fs::create_dir(&bar).unwrap();
269
270 let webc = Package::from_manifest(&manifest)
271 .unwrap()
272 .serialize()
273 .unwrap();
274 let container = from_bytes(webc).unwrap();
275
276 let out_dir = temp.path().join("out");
277 container.unpack(&out_dir, false).unwrap();
278
279 let expected_entries = [
280 "bar", "metadata", "foo", "manifest.json",
284 ];
285 let entries = std::fs::read_dir(&out_dir)
286 .unwrap()
287 .map(|e| e.unwrap())
288 .collect::<Vec<_>>();
289
290 assert_eq!(expected_entries.len(), entries.len());
291 assert!(expected_entries.iter().all(|e| {
292 entries
293 .iter()
294 .any(|entry| entry.file_name().as_os_str() == *e)
295 }))
296 }
297}