cargo-wasi-src 0.1.28

Subcommand for Cargo to easily build code for `wasm32-wasi`
Documentation
use anyhow::{bail, Context, Result};
use std::fs::{self, File};
use std::path::{Path, PathBuf};

pub struct Cache {
    all_versions_root: PathBuf,
    root: PathBuf,
}

impl Cache {
    pub fn new() -> Result<Cache> {
        let all_versions_root = match dirs::cache_dir() {
            Some(root) => root.join("cargo-wasi"),
            None => match dirs::home_dir() {
                Some(home) => home.join(".cargo-wasi"),
                None => bail!("failed to find home directory, is $HOME set?"),
            },
        };
        let root = all_versions_root.join(env!("CARGO_PKG_VERSION"));
        Ok(Cache {
            all_versions_root,
            root,
        })
    }

    pub fn root(&self) -> &Path {
        &self.root
    }

    /// Returns the path that all versions of `cargo-wasi` store their cache at,
    /// for cleaning.
    pub fn all_versions_root(&self) -> &Path {
        &self.all_versions_root
    }

    /// Get the cache stamp with the given name.
    pub fn stamp(&self, name: impl AsRef<str>) -> Stamp {
        let name = name.as_ref();
        assert!(!name.contains('/'));
        assert!(!name.contains('\\'));
        Stamp {
            path: self.root().join("stamps").join(name),
        }
    }
}

/// A cache stamp.
///
/// Used to cache the positive results of existence checks, e.g. is the
/// `wasm32-wasi` target installed for a given Rust toolchain?
pub struct Stamp {
    path: PathBuf,
}

impl Stamp {
    /// Does the cache stamp file exist, and therefore we already know its
    /// associated check has already been performed on an earlier run and it
    /// succeeded? E.g. we already ensured that the `wasm32-wasi` target is
    /// installed.
    ///
    /// Note that this *can* be a false positive, since the user could have, for
    /// example, deleted the `wasm32-wasi` target through `rustup`.
    pub fn exists(&self) -> bool {
        self.path.exists()
    }

    /// Given that we know the stamp's associated check succeeded, or did the
    /// work necessary to ensure it will succeed on further checks, create the
    /// cached stamp file.
    pub fn create(self) -> Result<()> {
        let stamps_dir = self.path.parent().unwrap();
        fs::create_dir_all(stamps_dir).with_context(|| {
            format!("failed to create stamp directory: {}", stamps_dir.display())
        })?;
        File::create(&self.path)
            .with_context(|| format!("failed to create stamp file: {}", self.path.display()))?;
        Ok(())
    }

    /// If the cache stamp does not exist, invoke `f` to ensure its result, and
    /// then write the cache stamp file.
    ///
    /// `f` should return `Ok` if the cached check succeeded, and therefore the
    /// cache stamp file should be written so we don't try and re-ensure it next
    /// time, for example if the `wasm32-wasi` target was installed correctly.
    ///
    /// `f` should return `Err` if the cached check failed, and therefore should
    /// *not* write the cache stamp file so that we do attempt to ensure it
    /// again next time, for example if we failed to install the `wasm32-wasi`
    /// target.
    pub fn ensure(self, f: impl FnOnce() -> Result<()>) -> Result<()> {
        if self.exists() {
            return Ok(());
        }

        f()?;
        self.create()
    }
}