1#![allow(
2 clippy::result_large_err,
3 reason = "WasmerPackageError is large, but not often used"
4)]
5
6use bytes::{Buf, Bytes};
7use std::{
8 fs::File,
9 io::{BufRead, BufReader, Read, Seek},
10 path::Path,
11};
12use wasmer_types::Features;
13use webc::{Container, ContainerError, Version};
14
15use crate::package::{Package, WasmerPackageError};
16
17fn is_tarball(mut file: impl Read + Seek) -> bool {
19 const TAR_GZ_MAGIC_BYTES: [u8; 2] = [0x1F, 0x8B];
22
23 let mut buffer = [0_u8; 2];
24 let result = match file.read_exact(&mut buffer) {
25 Ok(_) => buffer == TAR_GZ_MAGIC_BYTES,
26 Err(_) => false,
27 };
28
29 let _ = file.rewind();
30
31 result
32}
33
34pub fn from_disk(path: impl AsRef<Path>) -> Result<Container, WasmerPackageError> {
35 let path = path.as_ref();
36
37 if path.is_dir() {
38 return parse_dir(path);
39 }
40
41 let mut f = File::open(path).map_err(|error| ContainerError::Open {
42 error,
43 path: path.to_path_buf(),
44 })?;
45
46 if is_tarball(&mut f) {
47 return parse_tarball(BufReader::new(f));
48 }
49
50 match webc::detect(&mut f) {
51 Ok(Version::V1) => parse_v1_mmap(f).map_err(Into::into),
52 Ok(Version::V2) => parse_v2_mmap(f).map_err(Into::into),
53 Ok(Version::V3) => parse_v3_mmap(f).map_err(Into::into),
54 Ok(other) => {
55 let mut buffer = Vec::new();
57 f.rewind()
58 .and_then(|_| f.read_to_end(&mut buffer))
59 .map_err(|error| ContainerError::Read {
60 path: path.to_path_buf(),
61 error,
62 })?;
63
64 Container::from_bytes_and_version(buffer.into(), other).map_err(Into::into)
65 }
66 Err(e) => Err(ContainerError::Detect(e).into()),
67 }
68}
69
70pub fn is_container(bytes: &[u8]) -> bool {
72 is_tarball(std::io::Cursor::new(bytes)) || webc::detect(bytes).is_ok()
73}
74
75pub fn from_bytes(bytes: impl Into<Bytes>) -> Result<Container, WasmerPackageError> {
76 let bytes: Bytes = bytes.into();
77
78 if is_tarball(std::io::Cursor::new(&bytes)) {
79 return parse_tarball(bytes.reader());
80 }
81
82 let version = webc::detect(bytes.as_ref())?;
83 Container::from_bytes_and_version(bytes, version).map_err(Into::into)
84}
85
86#[allow(clippy::result_large_err)]
87fn parse_tarball(reader: impl BufRead) -> Result<Container, WasmerPackageError> {
88 let pkg = Package::from_tarball(reader)?;
89 Ok(Container::new(pkg))
90}
91
92#[allow(clippy::result_large_err)]
93fn parse_dir(path: &Path) -> Result<Container, WasmerPackageError> {
94 let wasmer_toml = path.join("wasmer.toml");
95 let pkg = Package::from_manifest(wasmer_toml)?;
96 Ok(Container::new(pkg))
97}
98
99#[allow(clippy::result_large_err)]
100fn parse_v1_mmap(f: File) -> Result<Container, ContainerError> {
101 let options = webc::v1::ParseOptions::default();
104 let webc = webc::v1::WebCMmap::from_file(f, &options)?;
105 Ok(Container::new(webc))
106}
107
108#[allow(clippy::result_large_err)]
109fn parse_v2_mmap(f: File) -> Result<Container, ContainerError> {
110 let webc = webc::v2::read::OwnedReader::from_file(f)?;
113 Ok(Container::new(webc))
114}
115
116#[allow(clippy::result_large_err)]
117fn parse_v3_mmap(f: File) -> Result<Container, ContainerError> {
118 let webc = webc::v3::read::OwnedReader::from_file(f)?;
121 Ok(Container::new(webc))
122}
123
124pub fn features_to_wasm_annotations(features: &Features) -> Vec<String> {
130 let mut feature_strings = Vec::new();
131
132 if features.simd {
133 feature_strings.push("simd".to_string());
134 }
135 if features.bulk_memory {
136 feature_strings.push("bulk-memory".to_string());
137 }
138 if features.reference_types {
139 feature_strings.push("reference-types".to_string());
140 }
141 if features.multi_value {
142 feature_strings.push("multi-value".to_string());
143 }
144 if features.threads {
145 feature_strings.push("threads".to_string());
146 }
147 if features.exceptions {
148 feature_strings.push("exception-handling".to_string());
149 }
150 if features.memory64 {
151 feature_strings.push("memory64".to_string());
152 }
153 feature_strings
157}
158
159pub fn wasm_annotations_to_features(feature_strings: &[String]) -> Features {
164 let mut features = Features::default();
165
166 features
168 .simd(false)
169 .bulk_memory(false)
170 .reference_types(false)
171 .multi_value(false)
172 .threads(false)
173 .exceptions(false)
174 .memory64(false);
175
176 for feature in feature_strings {
178 match feature.as_str() {
179 "simd" => {
180 features.simd(true);
181 }
182 "bulk-memory" => {
183 features.bulk_memory(true);
184 }
185 "reference-types" => {
186 features.reference_types(true);
187 }
188 "multi-value" => {
189 features.multi_value(true);
190 }
191 "threads" => {
192 features.threads(true);
193 }
194 "exception-handling" => {
195 features.exceptions(true);
196 }
197 "memory64" => {
198 features.memory64(true);
199 }
200 _ => {}
202 }
203 }
204
205 features
206}