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::V2) => parse_v2_mmap(f).map_err(Into::into),
52 Ok(Version::V3) => parse_v3_mmap(f).map_err(Into::into),
53 Ok(other) => {
54 let mut buffer = Vec::new();
56 f.rewind()
57 .and_then(|_| f.read_to_end(&mut buffer))
58 .map_err(|error| ContainerError::Read {
59 path: path.to_path_buf(),
60 error,
61 })?;
62
63 Container::from_bytes_and_version(buffer.into(), other).map_err(Into::into)
64 }
65 Err(e) => Err(ContainerError::Detect(e).into()),
66 }
67}
68
69pub fn is_container(bytes: &[u8]) -> bool {
71 is_tarball(std::io::Cursor::new(bytes)) || webc::detect(bytes).is_ok()
72}
73
74pub fn from_bytes(bytes: impl Into<Bytes>) -> Result<Container, WasmerPackageError> {
75 let bytes: Bytes = bytes.into();
76
77 if is_tarball(std::io::Cursor::new(&bytes)) {
78 return parse_tarball(bytes.reader());
79 }
80
81 let version = webc::detect(bytes.as_ref())?;
82 Container::from_bytes_and_version(bytes, version).map_err(Into::into)
83}
84
85#[allow(clippy::result_large_err)]
86fn parse_tarball(reader: impl BufRead) -> Result<Container, WasmerPackageError> {
87 let pkg = Package::from_tarball(reader)?;
88 Ok(Container::new(pkg))
89}
90
91#[allow(clippy::result_large_err)]
92fn parse_dir(path: &Path) -> Result<Container, WasmerPackageError> {
93 let wasmer_toml = path.join("wasmer.toml");
94 let pkg = Package::from_manifest(wasmer_toml)?;
95 Ok(Container::new(pkg))
96}
97
98#[allow(clippy::result_large_err)]
99fn parse_v2_mmap(f: File) -> Result<Container, ContainerError> {
100 let webc = webc::v2::read::OwnedReader::from_file(f)?;
103 Ok(Container::new(webc))
104}
105
106#[allow(clippy::result_large_err)]
107fn parse_v3_mmap(f: File) -> Result<Container, ContainerError> {
108 let webc = webc::v3::read::OwnedReader::from_file(f)?;
111 Ok(Container::new(webc))
112}
113
114pub fn features_to_wasm_annotations(features: &Features) -> Vec<String> {
120 let mut feature_strings = Vec::new();
121
122 if features.simd {
123 feature_strings.push("simd".to_string());
124 }
125 if features.bulk_memory {
126 feature_strings.push("bulk-memory".to_string());
127 }
128 if features.reference_types {
129 feature_strings.push("reference-types".to_string());
130 }
131 if features.multi_value {
132 feature_strings.push("multi-value".to_string());
133 }
134 if features.threads {
135 feature_strings.push("threads".to_string());
136 }
137 if features.exceptions {
138 feature_strings.push("exception-handling".to_string());
139 }
140 if features.memory64 {
141 feature_strings.push("memory64".to_string());
142 }
143 if features.wide_arithmetic {
144 feature_strings.push("wide-arithmetic".to_string());
145 }
146 if features.tail_call {
147 feature_strings.push("tail-call".to_string());
148 }
149 if features.multi_memory {
150 feature_strings.push("multi-memory".to_string());
151 }
152 feature_strings
155}
156
157pub fn wasm_annotations_to_features(feature_strings: &[String]) -> Features {
162 let mut features = Features::default();
163
164 features
166 .simd(false)
167 .bulk_memory(false)
168 .reference_types(false)
169 .multi_value(false)
170 .threads(false)
171 .exceptions(false)
172 .memory64(false);
173
174 for feature in feature_strings {
176 match feature.as_str() {
177 "simd" => {
178 features.simd(true);
179 }
180 "bulk-memory" => {
181 features.bulk_memory(true);
182 }
183 "reference-types" => {
184 features.reference_types(true);
185 }
186 "multi-value" => {
187 features.multi_value(true);
188 }
189 "threads" => {
190 features.threads(true);
191 }
192 "exception-handling" => {
193 features.exceptions(true);
194 }
195 "memory64" => {
196 features.memory64(true);
197 }
198 "relaxed-simd" => {
199 features.relaxed_simd(true);
200 }
201 "wide-arithmetic" => {
202 features.wide_arithmetic(true);
203 }
204 "extended-const" => {
205 features.extended_const(true);
206 }
207 "tail-call" => {
208 features.tail_call(true);
209 }
210 "multi-memory" => {
211 features.multi_memory(true);
212 }
213 _ => {}
215 }
216 }
217
218 features
219}