1#![allow(
2 clippy::result_large_err,
3 reason = "WasmerPackageError is large, but not often used"
4)]
5
6#[cfg(feature = "authoring")]
7use bytes::Buf;
8use bytes::Bytes;
9#[cfg(feature = "authoring")]
10use std::io::{BufRead, BufReader};
11use std::{
12 fs::File,
13 io::{Read, Seek},
14 path::Path,
15};
16use wasmer_types::Features;
17use webc::{Container, ContainerError, Version};
18
19use crate::WasmerPackageError;
20#[cfg(feature = "authoring")]
21use crate::package::Package;
22
23fn is_tarball(mut file: impl Read + Seek) -> bool {
25 const TAR_GZ_MAGIC_BYTES: [u8; 2] = [0x1F, 0x8B];
28
29 let mut buffer = [0_u8; 2];
30 let result = match file.read_exact(&mut buffer) {
31 Ok(_) => buffer == TAR_GZ_MAGIC_BYTES,
32 Err(_) => false,
33 };
34
35 let _ = file.rewind();
36
37 result
38}
39
40pub fn from_disk(path: impl AsRef<Path>) -> Result<Container, WasmerPackageError> {
41 let path = path.as_ref();
42
43 if path.is_dir() {
44 #[cfg(feature = "authoring")]
45 return parse_dir(path);
46 #[cfg(not(feature = "authoring"))]
47 return Err(ContainerError::FeatureNotEnabled {
48 feature: "authoring",
49 }
50 .into());
51 }
52
53 let mut f = File::open(path).map_err(|error| ContainerError::Open {
54 error,
55 path: path.to_path_buf(),
56 })?;
57
58 if is_tarball(&mut f) {
59 #[cfg(feature = "authoring")]
60 return parse_tarball(BufReader::new(f));
61 #[cfg(not(feature = "authoring"))]
62 return Err(ContainerError::FeatureNotEnabled {
63 feature: "authoring",
64 }
65 .into());
66 }
67
68 match webc::detect(&mut f) {
69 Ok(Version::V2) => parse_v2_mmap(f).map_err(Into::into),
70 Ok(Version::V3) => parse_v3_mmap(f).map_err(Into::into),
71 Ok(other) => {
72 let mut buffer = Vec::new();
74 f.rewind()
75 .and_then(|_| f.read_to_end(&mut buffer))
76 .map_err(|error| ContainerError::Read {
77 path: path.to_path_buf(),
78 error,
79 })?;
80
81 Container::from_bytes_and_version(buffer.into(), other).map_err(Into::into)
82 }
83 Err(e) => Err(ContainerError::Detect(e).into()),
84 }
85}
86
87pub fn is_container(bytes: &[u8]) -> bool {
89 is_tarball(std::io::Cursor::new(bytes)) || webc::detect(bytes).is_ok()
90}
91
92pub fn from_bytes(bytes: impl Into<Bytes>) -> Result<Container, WasmerPackageError> {
93 let bytes: Bytes = bytes.into();
94
95 if is_tarball(std::io::Cursor::new(&bytes)) {
96 #[cfg(feature = "authoring")]
97 return parse_tarball(bytes.reader());
98 #[cfg(not(feature = "authoring"))]
99 return Err(ContainerError::FeatureNotEnabled {
100 feature: "authoring",
101 }
102 .into());
103 }
104
105 let version = webc::detect(bytes.as_ref())?;
106 Container::from_bytes_and_version(bytes, version).map_err(Into::into)
107}
108
109#[allow(clippy::result_large_err)]
110#[cfg(feature = "authoring")]
111fn parse_tarball(reader: impl BufRead) -> Result<Container, WasmerPackageError> {
112 let pkg = Package::from_tarball(reader)?;
113 Ok(Container::new(pkg))
114}
115
116#[allow(clippy::result_large_err)]
117#[cfg(feature = "authoring")]
118fn parse_dir(path: &Path) -> Result<Container, WasmerPackageError> {
119 let wasmer_toml = path.join("wasmer.toml");
120 let pkg = Package::from_manifest(wasmer_toml)?;
121 Ok(Container::new(pkg))
122}
123
124#[allow(clippy::result_large_err)]
125fn parse_v2_mmap(f: File) -> Result<Container, ContainerError> {
126 let webc = webc::v2::read::OwnedReader::from_file(f)?;
129 Ok(Container::new(webc))
130}
131
132#[allow(clippy::result_large_err)]
133fn parse_v3_mmap(f: File) -> Result<Container, ContainerError> {
134 let webc = webc::v3::read::OwnedReader::from_file(f)?;
137 Ok(Container::new(webc))
138}
139
140pub fn features_to_wasm_annotations(features: &Features) -> Vec<String> {
146 let mut feature_strings = Vec::new();
147
148 if features.simd {
149 feature_strings.push("simd".to_string());
150 }
151 if features.bulk_memory {
152 feature_strings.push("bulk-memory".to_string());
153 }
154 if features.reference_types {
155 feature_strings.push("reference-types".to_string());
156 }
157 if features.multi_value {
158 feature_strings.push("multi-value".to_string());
159 }
160 if features.threads {
161 feature_strings.push("threads".to_string());
162 }
163 if features.exceptions {
164 feature_strings.push("exception-handling".to_string());
165 }
166 if features.memory64 {
167 feature_strings.push("memory64".to_string());
168 }
169 if features.wide_arithmetic {
170 feature_strings.push("wide-arithmetic".to_string());
171 }
172 if features.tail_call {
173 feature_strings.push("tail-call".to_string());
174 }
175 if features.multi_memory {
176 feature_strings.push("multi-memory".to_string());
177 }
178 feature_strings
181}
182
183pub fn wasm_annotations_to_features(feature_strings: &[String]) -> Features {
188 let mut features = Features::default();
189
190 features
192 .simd(false)
193 .bulk_memory(false)
194 .reference_types(false)
195 .multi_value(false)
196 .threads(false)
197 .exceptions(false)
198 .memory64(false);
199
200 for feature in feature_strings {
202 match feature.as_str() {
203 "simd" => {
204 features.simd(true);
205 }
206 "bulk-memory" => {
207 features.bulk_memory(true);
208 }
209 "reference-types" => {
210 features.reference_types(true);
211 }
212 "multi-value" => {
213 features.multi_value(true);
214 }
215 "threads" => {
216 features.threads(true);
217 }
218 "exception-handling" => {
219 features.exceptions(true);
220 }
221 "memory64" => {
222 features.memory64(true);
223 }
224 "relaxed-simd" => {
225 features.relaxed_simd(true);
226 }
227 "wide-arithmetic" => {
228 features.wide_arithmetic(true);
229 }
230 "extended-const" => {
231 features.extended_const(true);
232 }
233 "tail-call" => {
234 features.tail_call(true);
235 }
236 "multi-memory" => {
237 features.multi_memory(true);
238 }
239 _ => {}
241 }
242 }
243
244 features
245}
246
247#[cfg(all(test, feature = "execution", not(feature = "authoring")))]
248mod execution_only_tests {
249 use webc::ContainerError;
250
251 use super::from_bytes;
252 use crate::WasmerPackageError;
253
254 #[test]
255 fn rejects_authoring_formats() {
256 let error = from_bytes(vec![0x1f, 0x8b]).unwrap_err();
257 assert!(matches!(
258 error,
259 WasmerPackageError::ContainerError(ContainerError::FeatureNotEnabled {
260 feature: "authoring"
261 })
262 ));
263 }
264}