11use std:: fs;
22
3+ use anyhow:: bail;
4+ use anyhow:: Context ;
35use anyhow:: Result ;
46use toml_edit:: table;
57use toml_edit:: value;
68use toml_edit:: Array ;
79use toml_edit:: Document ;
10+ use toml_edit:: Value ;
811
912use 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