Skip to content

Commit 3c4d168

Browse files
authored
fix: Handle plain strings in --sync-aliases (#25)
1 parent c6bd9ec commit 3c4d168

File tree

1 file changed

+74
-12
lines changed

1 file changed

+74
-12
lines changed

src/cargo_config.rs

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use std::fs;
22

3+
use anyhow::bail;
4+
use anyhow::Context;
35
use anyhow::Result;
46
use toml_edit::table;
57
use toml_edit::value;
68
use toml_edit::Array;
79
use toml_edit::Document;
10+
use toml_edit::Value;
811

912
use crate::metadata;
1013

@@ -16,23 +19,58 @@ pub fn sync_aliases() -> Result<()> {
1619
toml_str = fs::read_to_string(&config_path)?.parse()?;
1720
}
1821

22+
let binary_packages = metadata::get_binary_packages()?;
23+
let new_config = update_aliases_toml(&toml_str, binary_packages)
24+
.context("failed to update aliases in .cargo/config.toml")?;
25+
26+
if !config_path.parent().unwrap().exists() {
27+
fs::create_dir(config_path.parent().unwrap())?;
28+
}
29+
30+
fs::write(config_path, new_config)?;
31+
32+
return Ok(());
33+
}
34+
35+
fn update_aliases_toml(
36+
toml_str: &str,
37+
binary_packages: Vec<metadata::BinaryPackage>,
38+
) -> Result<String> {
1939
let mut doc = toml_str.parse::<Document>()?;
2040
if doc.get("alias").is_none() {
2141
doc["alias"] = table();
2242
}
2343

24-
let aliases = doc["alias"].as_table_mut().unwrap();
44+
// If the TOML structure is not as and we panic because of that, that makes for
45+
// poor user experience, so try to report errors for everything that could
46+
// go wrong.
47+
let aliases = doc["alias"]
48+
.as_table_mut()
49+
.context("alias key should be a table")?;
2550
let mut remove_keys: Vec<String> = vec![];
2651
for (key, value) in aliases.get_values() {
27-
if value.as_array().unwrap().get(0).unwrap().as_str().unwrap() == "bin" {
28-
remove_keys.push(key[0].get().to_owned());
52+
let [name] = key.as_slice() else {
53+
bail!("unexpected nested table: {key:?}")
54+
};
55+
// The value can be either a single string (implicitly split on spaces) or an
56+
// array of strings. We always create an array, but a user might use a
57+
// single string for other aliases, so we have to at least not crash on
58+
// such values.
59+
if let Value::Array(parts) = value {
60+
let first_part = parts
61+
.get(0)
62+
.with_context(|| format!("alias {name:?} is empty array"))?
63+
.as_str()
64+
.with_context(|| format!("alias {name:?} should be array of strings"))?;
65+
if first_part == "bin" {
66+
remove_keys.push(name.get().to_owned());
67+
}
2968
}
3069
}
3170
for key in remove_keys {
3271
aliases.remove(&key);
3372
}
3473

35-
let binary_packages = metadata::get_binary_packages()?;
3674
for binary_package in binary_packages {
3775
let mut bin = binary_package.package;
3876
if let Some(bin_target) = binary_package.bin_target {
@@ -48,14 +86,7 @@ pub fn sync_aliases() -> Result<()> {
4886
arr.push(bin.clone());
4987
doc["alias"][bin.replace("cargo-", "")] = value(arr);
5088
}
51-
52-
if !config_path.parent().unwrap().exists() {
53-
fs::create_dir(config_path.parent().unwrap())?;
54-
}
55-
56-
fs::write(config_path, doc.to_string())?;
57-
58-
return Ok(());
89+
return Ok(doc.to_string());
5990
}
6091

6192
/// Verifies in cargo-binstall is available in alias'.
@@ -74,3 +105,34 @@ pub fn binstall_alias_exists() -> Result<bool> {
74105
let aliases = doc["alias"].as_table_mut().unwrap();
75106
return Ok(aliases.contains_key("binstall"));
76107
}
108+
109+
#[cfg(test)]
110+
mod tests {
111+
use super::update_aliases_toml;
112+
113+
#[test]
114+
fn skips_bare_string_alias() {
115+
let original_toml = r#"
116+
[alias]
117+
xtask = "run --package xtask --"
118+
"#;
119+
let new_toml = update_aliases_toml(original_toml, Vec::new()).unwrap();
120+
assert_eq!(original_toml, new_toml);
121+
}
122+
123+
#[test]
124+
fn doesnt_panic_on_empty_array() {
125+
let result = update_aliases_toml("alias.mistake = []", Vec::new());
126+
// Currently an error, could be skipped instead, in that case the Ok(..) result
127+
// should be tested a bit.
128+
assert!(result.is_err());
129+
}
130+
131+
#[test]
132+
fn doesnt_panic_on_nested_keys() {
133+
let result = update_aliases_toml("alias.alias.alias = 'test'", Vec::new());
134+
// Currently an error, could be skipped instead, in that case the Ok(..) result
135+
// should be tested a bit.
136+
assert!(result.is_err());
137+
}
138+
}

0 commit comments

Comments
 (0)