1use bytes::{Buf, Bytes};
2use std::{
3 fs::File,
4 io::{BufRead, BufReader, Read, Seek},
5 path::Path,
6};
7use webc::{Container, ContainerError, Version};
8
9use crate::package::{Package, WasmerPackageError};
10
11fn is_tarball(mut file: impl Read + Seek) -> bool {
13 const TAR_GZ_MAGIC_BYTES: [u8; 2] = [0x1F, 0x8B];
16
17 let mut buffer = [0_u8; 2];
18 let result = match file.read_exact(&mut buffer) {
19 Ok(_) => buffer == TAR_GZ_MAGIC_BYTES,
20 Err(_) => false,
21 };
22
23 let _ = file.rewind();
24
25 result
26}
27
28pub fn from_disk(path: impl AsRef<Path>) -> Result<Container, WasmerPackageError> {
29 let path = path.as_ref();
30
31 if path.is_dir() {
32 return parse_dir(path);
33 }
34
35 let mut f = File::open(path).map_err(|error| ContainerError::Open {
36 error,
37 path: path.to_path_buf(),
38 })?;
39
40 if is_tarball(&mut f) {
41 return parse_tarball(BufReader::new(f));
42 }
43
44 match webc::detect(&mut f) {
45 Ok(Version::V1) => parse_v1_mmap(f).map_err(Into::into),
46 Ok(Version::V2) => parse_v2_mmap(f).map_err(Into::into),
47 Ok(Version::V3) => parse_v3_mmap(f).map_err(Into::into),
48 Ok(other) => {
49 let mut buffer = Vec::new();
51 f.rewind()
52 .and_then(|_| f.read_to_end(&mut buffer))
53 .map_err(|error| ContainerError::Read {
54 path: path.to_path_buf(),
55 error,
56 })?;
57
58 Container::from_bytes_and_version(buffer.into(), other).map_err(Into::into)
59 }
60 Err(e) => Err(ContainerError::Detect(e).into()),
61 }
62}
63
64pub fn from_bytes(bytes: impl Into<Bytes>) -> Result<Container, WasmerPackageError> {
65 let bytes: Bytes = bytes.into();
66
67 if is_tarball(std::io::Cursor::new(&bytes)) {
68 return parse_tarball(bytes.reader());
69 }
70
71 let version = webc::detect(bytes.as_ref())?;
72 Container::from_bytes_and_version(bytes, version).map_err(Into::into)
73}
74
75#[allow(clippy::result_large_err)]
76fn parse_tarball(reader: impl BufRead) -> Result<Container, WasmerPackageError> {
77 let pkg = Package::from_tarball(reader)?;
78 Ok(Container::new(pkg))
79}
80
81#[allow(clippy::result_large_err)]
82fn parse_dir(path: &Path) -> Result<Container, WasmerPackageError> {
83 let wasmer_toml = path.join("wasmer.toml");
84 let pkg = Package::from_manifest(wasmer_toml)?;
85 Ok(Container::new(pkg))
86}
87
88#[allow(clippy::result_large_err)]
89fn parse_v1_mmap(f: File) -> Result<Container, ContainerError> {
90 let options = webc::v1::ParseOptions::default();
93 let webc = webc::v1::WebCMmap::from_file(f, &options)?;
94 Ok(Container::new(webc))
95}
96
97#[allow(clippy::result_large_err)]
98fn parse_v2_mmap(f: File) -> Result<Container, ContainerError> {
99 let webc = webc::v2::read::OwnedReader::from_file(f)?;
102 Ok(Container::new(webc))
103}
104
105#[allow(clippy::result_large_err)]
106fn parse_v3_mmap(f: File) -> Result<Container, ContainerError> {
107 let webc = webc::v3::read::OwnedReader::from_file(f)?;
110 Ok(Container::new(webc))
111}