scarb 0.5.2

The Cairo package manager
Documentation
use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::process::Command as StdCommand;
use std::{fs, iter};

use assert_fs::TempDir;
use once_cell::sync::Lazy;
use snapbox::cmd::cargo_bin;
use snapbox::cmd::Command as SnapboxCommand;

use scarb::core::Config;
use scarb::ui::Verbosity;

use crate::support::fsx::{AssertFsUtf8Ext, PathUtf8Ext};

pub struct Scarb {
    cache: EnvPath,
    config: EnvPath,
    log: OsString,
    scarb_bin: &'static Path,
}

impl Scarb {
    pub fn new() -> Self {
        Self {
            cache: EnvPath::temp_dir(),
            config: EnvPath::temp_dir(),
            log: "scarb=trace".into(),
            scarb_bin: cargo_bin!("scarb"),
        }
    }

    pub fn from_config(config: &Config) -> Self {
        Self {
            cache: EnvPath::borrow(config.dirs().cache_dir.path_unchecked().as_std_path()),
            config: EnvPath::borrow(config.dirs().config_dir.path_unchecked().as_std_path()),
            log: config.log_filter_directive().to_os_string(),
            scarb_bin: cargo_bin!("scarb"),
        }
    }

    pub fn quick_snapbox() -> SnapboxCommand {
        Self::new().snapbox()
    }

    pub fn snapbox(self) -> SnapboxCommand {
        SnapboxCommand::from_std(self.std())
    }

    pub fn std(self) -> StdCommand {
        let mut cmd = StdCommand::new(self.scarb_bin);
        cmd.env("SCARB_LOG", self.log);
        cmd.env("SCARB_CACHE", self.cache.path());
        cmd.env("SCARB_CONFIG", self.config.path());
        cmd
    }

    pub fn isolate_from_extensions(self) -> Self {
        static ISOLATED_BIN: Lazy<PathBuf> = Lazy::new(|| {
            let source_bin = cargo_bin!("scarb");

            let output_dir = Path::new(env!("CARGO_TARGET_TMPDIR")).join("isolated_scarb");
            fs::create_dir_all(&output_dir).unwrap();

            let output_bin = output_dir.join(source_bin.file_name().unwrap());
            fs::copy(source_bin, &output_bin).unwrap();

            output_bin
        });

        Self {
            scarb_bin: &*ISOLATED_BIN,
            ..self
        }
    }

    pub fn test_config(manifest: impl AssertFsUtf8Ext) -> Config {
        let cache_dir = TempDir::new().unwrap();
        let config_dir = TempDir::new().unwrap();

        Config::builder(manifest.utf8_path())
            .global_cache_dir_override(Some(cache_dir.try_as_utf8().unwrap()))
            .global_config_dir_override(Some(config_dir.try_as_utf8().unwrap()))
            .path_env_override(Some(iter::empty::<PathBuf>()))
            .ui_verbosity(Verbosity::Verbose)
            .log_filter_directive(Some("scarb=trace"))
            .build()
            .unwrap()
    }
}

enum EnvPath {
    Managed(TempDir),
    Unmanaged(PathBuf),
}

impl EnvPath {
    fn temp_dir() -> Self {
        Self::Managed(TempDir::new().unwrap())
    }

    fn borrow(path: impl AsRef<Path>) -> Self {
        Self::Unmanaged(path.as_ref().to_path_buf())
    }

    fn path(&self) -> &Path {
        match self {
            EnvPath::Managed(t) => t.path(),
            EnvPath::Unmanaged(p) => p,
        }
    }
}