1use std::path::PathBuf;
2
3use crate::{tool_path::ToolPath, Cache};
4use anyhow::Result;
5use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor};
6
7pub struct Config {
8 cache: Option<Cache>,
9 verbose: bool,
10 choice: ColorChoice,
11 pub is_offline: bool,
12}
13
14impl Config {
15 pub fn new() -> Config {
16 Config {
17 cache: None,
18 verbose: false,
19 choice: if atty::is(atty::Stream::Stderr) {
20 ColorChoice::Auto
21 } else {
22 ColorChoice::Never
23 },
24 is_offline: std::env::var("CARGO_WASIX_OFFLINE").is_ok_and(|v| v == "1" || v == "true"),
26 }
27 }
28
29 pub fn data_dir() -> Result<PathBuf, anyhow::Error> {
30 let dir = if let Ok(dir) = std::env::var("WASIX_DATA_DIR") {
31 dir.into()
32 } else if let Some(root) = dirs::data_dir() {
33 root.join("cargo-wasix")
34 } else if let Some(home) = dirs::home_dir() {
35 home.join(".cargo-wasix")
36 } else {
37 anyhow::bail!("Could not determine cargo-wasix data dir. set WASIX_DATA_DIR env var");
38 };
39
40 Ok(dir)
41 }
42
43 pub fn cache_dir() -> Result<PathBuf, anyhow::Error> {
44 let dir = if let Ok(dir) = std::env::var("WASIX_CACHE_DIR") {
45 dir.into()
46 } else if let Some(root) = dirs::cache_dir() {
47 root.join("cargo-wasix")
48 } else if let Ok(data) = Self::data_dir() {
49 data.join("cache")
50 } else if let Some(home) = dirs::home_dir() {
51 home.join(".cargo-wasix").join("cache")
52 } else {
53 anyhow::bail!("Could not determine cargo-wasix cache dir. set WASIX_CACHE_DIR env var");
54 };
55
56 Ok(dir)
57 }
58
59 pub fn toolchain_dir() -> Result<PathBuf, anyhow::Error> {
60 Self::data_dir().map(|d| d.join("toolchains"))
61 }
62
63 fn lockfile_path() -> Result<PathBuf, anyhow::Error> {
64 Self::data_dir().map(|p| p.join("rustup-lock"))
65 }
66
67 pub fn acquire_lock() -> Result<crate::utils::FileLock, anyhow::Error> {
68 crate::utils::flock(&Self::lockfile_path()?)
69 }
70
71 pub fn load_cache(&mut self) -> Result<()> {
72 assert!(self.cache.is_none());
73 self.cache = Some(Cache::new(Self::cache_dir()?)?);
74 Ok(())
75 }
76
77 pub fn cache(&self) -> &Cache {
78 self.cache.as_ref().expect("cache not loaded yet")
79 }
80
81 pub fn is_verbose(&self) -> bool {
82 self.verbose
83 }
84
85 pub fn verbose(&self, f: impl FnOnce()) {
86 if self.verbose {
87 f();
88 }
89 }
90
91 pub fn set_verbose(&mut self, verbose: bool) {
92 self.verbose = verbose;
93 }
94
95 pub fn status(&self, name: &str, rest: &str) {
96 let mut shell = StandardStream::stderr(self.choice);
97 drop(shell.set_color(ColorSpec::new().set_fg(Some(Color::Green)).set_bold(true)));
98 eprint!("{:>12}", name);
99 drop(shell.reset());
100 eprintln!(" {}", rest);
101 }
102
103 pub fn print_error(&self, err: &anyhow::Error) {
104 if let Some(code) = crate::utils::normal_process_exit_code(err) {
105 std::process::exit(code);
106 }
107 let mut shell = StandardStream::stderr(self.choice);
108 drop(shell.set_color(ColorSpec::new().set_fg(Some(Color::Red)).set_bold(true)));
109 eprint!("error");
110 drop(shell.reset());
111 eprintln!(": {}", err);
112 for cause in err.chain().skip(1) {
113 eprintln!();
114 drop(shell.set_color(ColorSpec::new().set_bold(true)));
115 eprint!("Caused by");
116 drop(shell.reset());
117 eprintln!(":");
118 eprintln!(" {}", cause.to_string().replace('\n', "\n "));
119 }
120 }
121
122 pub fn info(&self, msg: &str) {
123 let mut shell = StandardStream::stderr(self.choice);
124 drop(shell.set_color(ColorSpec::new().set_fg(Some(Color::Cyan)).set_bold(true)));
125 eprint!("info");
126 drop(shell.reset());
127 eprintln!(": {}", msg);
128 }
129
130 pub fn warn(&self, msg: &str) {
131 let mut shell = StandardStream::stderr(self.choice);
132 drop(shell.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)).set_bold(true)));
133 eprint!("warn");
134 drop(shell.reset());
135 eprintln!(": {}", msg);
136 }
137
138 fn get_tool(&self, tool: &str, version: Option<&str>) -> (PathBuf, bool) {
148 if let Some(s) = std::env::var_os(tool.to_uppercase().replace('-', "_")) {
149 (s.into(), true)
150 } else {
151 let mut cache_path = self.cache().root().join(tool);
152 if let Some(v) = version {
153 cache_path.push(v);
154 cache_path.push(tool)
155 }
156 (cache_path, false)
157 }
158 }
159
160 pub fn get_wasm_opt(&self) -> ToolPath {
166 let (path, is_overridden) = self.get_tool("wasm-opt", None);
167 if !is_overridden {
168 let mut bin = ["bin", "wasm-opt"].iter().collect::<PathBuf>();
169 bin.set_extension(std::env::consts::EXE_EXTENSION);
170
171 let bin_path = path.join(&bin);
172 let mut sub_paths = vec![bin];
173
174 if cfg!(target_os = "macos") {
176 let mut dylib = ["lib", "libbinaryen"].iter().collect::<PathBuf>();
177 dylib.set_extension(std::env::consts::DLL_EXTENSION);
178 sub_paths.push(dylib);
179 }
180
181 ToolPath::Cached {
182 bin_path,
183 base: path,
184 sub_paths,
185 }
186 } else {
187 ToolPath::Overridden(path)
188 }
189 }
190}