wasmer_cli/utils/
mod.rs

1//! Utility functions for the WebAssembly module
2
3pub(crate) mod package_wizard;
4pub(crate) mod prompts;
5pub(crate) mod render;
6pub(crate) mod timestamp;
7pub(crate) mod unpack;
8
9use std::path::{Path, PathBuf};
10
11use anyhow::{Context as _, Result, bail};
12use wasmer_wasix::runners::MappedDirectory;
13
14fn retrieve_alias_pathbuf(host_dir: &str, guest_dir: &str) -> Result<MappedDirectory> {
15    let host_dir_path = PathBuf::from(&host_dir).canonicalize()?;
16    if let Ok(pb_metadata) = host_dir_path.metadata() {
17        if !pb_metadata.is_dir() {
18            bail!("\"{}\" exists, but it is not a directory", &host_dir);
19        }
20    } else {
21        bail!("Directory \"{}\" does not exist", &host_dir);
22    }
23    Ok(MappedDirectory {
24        host: host_dir_path,
25        guest: guest_dir.to_string(),
26    })
27}
28
29/// Parses a volume from a string
30pub fn parse_volume(entry: &str) -> Result<MappedDirectory> {
31    // On Windows, colon is a valid path component (e.g. C:\)
32    if let Some((host_dir, guest_dir)) = entry.rsplit_once(":") {
33        retrieve_alias_pathbuf(host_dir, guest_dir)
34    } else {
35        retrieve_alias_pathbuf(entry, entry)
36    }
37}
38
39/// Parses a mapdir(legacy option) from a string.
40pub fn parse_mapdir(entry: &str) -> Result<MappedDirectory> {
41    // On Windows, colon is a valid path component (e.g. C:\)
42    if let Some((guest_dir, host_dir)) = entry.rsplit_once(":") {
43        retrieve_alias_pathbuf(host_dir, guest_dir)
44    } else {
45        retrieve_alias_pathbuf(entry, entry)
46    }
47}
48
49/// Parses an environment variable.
50pub fn parse_envvar(entry: &str) -> Result<(String, String)> {
51    let entry = entry.trim();
52
53    match entry.find('=') {
54        None => bail!(
55            "Environment variable must be of the form `<name>=<value>`; found `{}`",
56            &entry
57        ),
58
59        Some(0) => bail!(
60            "Environment variable is not well formed, the `name` is missing in `<name>=<value>`; got `{}`",
61            &entry
62        ),
63
64        Some(position) if position == entry.len() - 1 => bail!(
65            "Environment variable is not well formed, the `value` is missing in `<name>=<value>`; got `{}`",
66            &entry
67        ),
68
69        Some(position) => Ok((entry[..position].into(), entry[position + 1..].into())),
70    }
71}
72
73pub(crate) const DEFAULT_PACKAGE_MANIFEST_FILE: &str = "wasmer.toml";
74
75/// Load a package manifest from the manifest file.
76///
77/// Path can either be a directory, or a concrete file path.
78pub fn load_package_manifest(
79    path: &Path,
80) -> Result<Option<(PathBuf, wasmer_config::package::Manifest)>, anyhow::Error> {
81    let file_path = if path.is_file() {
82        path.to_owned()
83    } else {
84        path.join(DEFAULT_PACKAGE_MANIFEST_FILE)
85    };
86
87    let contents = match std::fs::read_to_string(&file_path) {
88        Ok(c) => c,
89        Err(err) if err.kind() == std::io::ErrorKind::NotFound => return Ok(None),
90        Err(err) => {
91            return Err(err).with_context(|| {
92                format!(
93                    "Could not read package manifest at '{}'",
94                    file_path.display()
95                )
96            });
97        }
98    };
99
100    let manifest = wasmer_config::package::Manifest::parse(&contents).with_context(|| {
101        format!(
102            "Could not parse package config at: '{}' - full config: {}",
103            file_path.display(),
104            contents
105        )
106    })?;
107
108    Ok(Some((file_path, manifest)))
109}
110
111/// Merge two yaml values by recursively merging maps from b into a.
112///
113/// Preserves old values that were not in b.
114pub(crate) fn merge_yaml_values(a: &serde_yaml::Value, b: &serde_yaml::Value) -> serde_yaml::Value {
115    use serde_yaml::Value as V;
116    match (a, b) {
117        (V::Mapping(a), V::Mapping(b)) => {
118            let mut m = a.clone();
119            for (k, v) in b.iter() {
120                let newval = if let Some(old) = a.get(k) {
121                    merge_yaml_values(old, v)
122                } else {
123                    v.clone()
124                };
125                m.insert(k.clone(), newval);
126            }
127            V::Mapping(m)
128        }
129        _ => b.clone(),
130    }
131}
132
133// pub(crate) fn shell(cmd: &str, args: &[&str]) -> anyhow::Result<Option<i32>> {
134//     let ecode = Command::new(cmd).args(args).spawn()?.wait()?;
135//     if ecode.success() {
136//         Ok(ecode.code())
137//     } else {
138//         Err(anyhow::format_err!(
139//             "failed to execute linux command (code={:?})",
140//             ecode.code()
141//         ))
142//     }
143// }
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn test_merge_yaml_values() {
151        use serde_yaml::Value;
152        let v1 = r#"
153a: a
154b:
155  b1: b1
156c: c
157        "#;
158        let v2 = r#"
159a: a1
160b:
161  b2: b2
162        "#;
163        let v3 = r#"
164a: a1
165b:
166  b1: b1
167  b2: b2
168c: c
169        "#;
170
171        let a: Value = serde_yaml::from_str(v1).unwrap();
172        let b: Value = serde_yaml::from_str(v2).unwrap();
173        let c: Value = serde_yaml::from_str(v3).unwrap();
174        let merged = merge_yaml_values(&a, &b);
175        assert_eq!(merged, c);
176    }
177
178    #[test]
179    fn test_parse_envvar() {
180        assert_eq!(
181            parse_envvar("A").unwrap_err().to_string(),
182            "Environment variable must be of the form `<name>=<value>`; found `A`"
183        );
184        assert_eq!(
185            parse_envvar("=A").unwrap_err().to_string(),
186            "Environment variable is not well formed, the `name` is missing in `<name>=<value>`; got `=A`"
187        );
188        assert_eq!(
189            parse_envvar("A=").unwrap_err().to_string(),
190            "Environment variable is not well formed, the `value` is missing in `<name>=<value>`; got `A=`"
191        );
192        assert_eq!(parse_envvar("A=B").unwrap(), ("A".into(), "B".into()));
193        assert_eq!(parse_envvar("   A=B\t").unwrap(), ("A".into(), "B".into()));
194        assert_eq!(
195            parse_envvar("A=B=C=D").unwrap(),
196            ("A".into(), "B=C=D".into())
197        );
198    }
199}