mod error;
use std::{process::Command, str::FromStr};
pub use error::RustupTargetError;
pub use target_lexicon::Triple;
pub fn list() -> Result<Vec<(Triple, bool)>, RustupTargetError> {
let output = Command::new("rustup")
.arg("target")
.arg("list")
.output()
.map_err(|e| RustupTargetError::ProcessFailed(e))?;
let out = extract_stdout(&output)?;
parse_rustup_triple_list(&out)
}
pub fn installed() -> Result<Vec<Triple>, RustupTargetError> {
Ok(list()?.into_iter().filter(|(_, inst)| *inst).map(|(t, _)| t).collect())
}
pub fn install(list: &[Triple]) -> Result<(), RustupTargetError> {
let mut cmd = Command::new("rustup");
cmd.arg("target").arg("add");
for triple in list {
cmd.arg(triple.to_string());
}
let output = cmd
.output()
.map_err(|e| RustupTargetError::ProcessFailed(e))?;
let _out = extract_stdout(&output)?;
Ok(())
}
fn extract_stdout(output: &std::process::Output) -> Result<String, RustupTargetError> {
let cleaned = strip_ansi_escapes::strip(&output.stdout).expect("Could not strip ansii escapes");
let out = String::from_utf8(cleaned).expect("Could not read utf8");
if !output.status.success() {
let err = String::from_utf8_lossy(&output.stderr).to_string();
return Err(RustupTargetError::RustupError {
exitcode: output.status.code(),
stderr: err,
stdout: out,
});
}
Ok(out)
}
fn parse_rustup_triple_list(list: &str) -> Result<Vec<(Triple, bool)>, RustupTargetError> {
let mut triples = Vec::new();
for mut line in list.lines() {
let installed = if line.ends_with(" (installed)") {
line = &line[..line.len() - 12];
true
} else {
false
};
let triple = match Triple::from_str(line) {
Err(e) => {
return Err(RustupTargetError::InvalidRustupTriple {
line: line.to_string(),
source: e,
})
}
Ok(t) => t,
};
triples.push((triple, installed));
}
Ok(triples)
}
#[test]
fn test_parse_list() {
let list = r###"aarch64-apple-darwin (installed)
aarch64-apple-ios (installed)
aarch64-apple-ios-sim (installed)
aarch64-fuchsiaarch64-linux-android (installed)
aarch64-pc-windows-msvc"###;
let triples = parse_rustup_triple_list(list).unwrap();
let re_composed = format!(
"{}",
triples
.iter()
.map(|(t, inst)| format!("{} - installed: {inst:?}", t.to_string()))
.collect::<Vec<_>>()
.join("\n")
);
insta::assert_snapshot!(re_composed, @r###"
aarch64-apple-darwin - installed: true
aarch64-apple-ios - installed: true
aarch64-apple-ios-sim - installed: true
aarch64-fuchsiaarch64-linux-android - installed: true
aarch64-pc-windows-msvc - installed: false
"###
);
}
#[test]
fn test_parse_list_wrong_arch() {
let list = r###"aarch64-apple-darwin (installed)
arch64-apple-ios (installed)
aarch64-pc-windows-msvc"###;
let err = parse_rustup_triple_list(list).unwrap_err();
insta::assert_snapshot!(err.to_string(),
@"Rustup returned an invalid triple 'arch64-apple-ios' due to 'Unrecognized architecture: arch64', please raise an issue at: https://github.com/akesson/rustup-target/issues");
}