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