use std::path::PathBuf;
use crate::{tool_path::ToolPath, Cache};
use anyhow::Result;
use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
pub struct Config {
cache: Option<Cache>,
verbose: bool,
choice: ColorChoice,
}
impl Config {
pub fn new() -> Config {
Config {
cache: None,
verbose: false,
choice: if atty::is(atty::Stream::Stderr) {
ColorChoice::Auto
} else {
ColorChoice::Never
},
}
}
pub fn data_dir() -> Result<PathBuf, anyhow::Error> {
let dir = if let Ok(dir) = std::env::var("WASIX_DATA_DIR") {
dir.into()
} else if let Some(root) = dirs::data_dir() {
root.join("cargo-wasix")
} else if let Some(home) = dirs::home_dir() {
home.join(".cargo-wasix")
} else {
anyhow::bail!("Could not determine cargo-wasix data dir. set WASIX_DATA_DIR env var");
};
Ok(dir)
}
pub fn cache_dir() -> Result<PathBuf, anyhow::Error> {
let dir = if let Ok(dir) = std::env::var("WASIX_CACHE_DIR") {
dir.into()
} else if let Some(root) = dirs::cache_dir() {
root.join("cargo-wasix")
} else if let Ok(data) = Self::data_dir() {
data.join("cache")
} else if let Some(home) = dirs::home_dir() {
home.join(".cargo-wasix").join("cache")
} else {
anyhow::bail!("Could not determine cargo-wasix cache dir. set WASIX_CACHE_DIR env var");
};
Ok(dir)
}
pub fn toolchain_dir() -> Result<PathBuf, anyhow::Error> {
Self::data_dir().map(|d| d.join("toolchains"))
}
fn lockfile_path() -> Result<PathBuf, anyhow::Error> {
Self::data_dir().map(|p| p.join("rustup-lock"))
}
pub fn acquire_lock() -> Result<crate::utils::FileLock, anyhow::Error> {
crate::utils::flock(&Self::lockfile_path()?)
}
pub fn load_cache(&mut self) -> Result<()> {
assert!(self.cache.is_none());
self.cache = Some(Cache::new(Self::cache_dir()?)?);
Ok(())
}
pub fn cache(&self) -> &Cache {
self.cache.as_ref().expect("cache not loaded yet")
}
pub fn is_verbose(&self) -> bool {
self.verbose
}
pub fn verbose(&self, f: impl FnOnce()) {
if self.verbose {
f();
}
}
pub fn set_verbose(&mut self, verbose: bool) {
self.verbose = verbose;
}
pub fn status(&self, name: &str, rest: &str) {
let mut shell = StandardStream::stderr(self.choice);
drop(shell.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true)));
eprint!("{:>12}", name);
drop(shell.reset());
eprintln!(" {}", rest);
}
pub fn print_error(&self, err: &anyhow::Error) {
if let Some(code) = crate::utils::normal_process_exit_code(err) {
std::process::exit(code);
}
let mut shell = StandardStream::stderr(self.choice);
drop(shell.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true)));
eprint!("error");
drop(shell.reset());
eprintln!(": {}", err);
for cause in err.chain().skip(1) {
eprintln!();
drop(shell.set_color(ColorSpec::new().set_bold(true)));
eprint!("Caused by");
drop(shell.reset());
eprintln!(":");
eprintln!(" {}", cause.to_string().replace('\n', "\n "));
}
}
pub fn info(&self, msg: &str) {
let mut shell = StandardStream::stderr(self.choice);
drop(shell.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)).set_bold(true)));
eprint!("info");
drop(shell.reset());
eprintln!(": {}", msg);
}
fn get_tool(&self, tool: &str, version: Option<&str>) -> (PathBuf, bool) {
if let Some(s) = std::env::var_os(tool.to_uppercase().replace('-', "_")) {
(s.into(), true)
} else {
let mut cache_path = self.cache().root().join(tool);
if let Some(v) = version {
cache_path.push(v);
cache_path.push(tool)
}
(cache_path, false)
}
}
pub fn get_wasm_opt(&self) -> ToolPath {
let (path, is_overridden) = self.get_tool("wasm-opt", None);
if !is_overridden {
let mut bin = ["bin", "wasm-opt"].iter().collect::<PathBuf>();
bin.set_extension(std::env::consts::EXE_EXTENSION);
let bin_path = path.join(&bin);
let mut sub_paths = vec![bin];
if cfg!(target_os = "macos") {
let mut dylib = ["lib", "libbinaryen"].iter().collect::<PathBuf>();
dylib.set_extension(std::env::consts::DLL_EXTENSION);
sub_paths.push(dylib);
}
ToolPath::Cached {
bin_path,
base: path,
sub_paths,
}
} else {
ToolPath::Overridden(path)
}
}
}