11use std:: { error:: Error , path:: PathBuf } ;
22
33use clap:: { Parser , Subcommand , builder:: TypedValueParser } ;
4+ use color_eyre:: Result ;
45use strum:: VariantNames ;
56
67use crate :: {
7- config:: { ComputePlatform , get_config_dir, get_data_dir, get_project_local_config_file} ,
8+ config:: { ComputePlatform , Config , get_config_dir, get_data_dir, get_project_local_config_file} ,
89 util:: types:: DockerImageUrl ,
910} ;
1011
@@ -26,8 +27,37 @@ pub enum CliCommands {
2627 } ,
2728 #[ clap( about = "Create a new project configuration file" ) ]
2829 Init {
29- #[ clap( help = "Destination folder to create config in (default = current directory)" ) ]
30+ #[ clap( help = "destination folder to create config in (default = current directory)" ) ]
3031 destination : Option < PathBuf > ,
32+ #[ clap( help = "project name to use" ) ]
33+ name : Option < String > ,
34+ } ,
35+ #[ clap( about = "Manage configuration" ) ]
36+ Config {
37+ #[ command( subcommand) ]
38+ command : ConfigCommands ,
39+ } ,
40+ }
41+
42+ #[ derive( Subcommand , Debug ) ]
43+ pub enum ConfigCommands {
44+ #[ clap( about = "Set config values" ) ]
45+ Set {
46+ #[ clap(
47+ short,
48+ long,
49+ action,
50+ help = "whether to change the global config or the project local one"
51+ ) ]
52+ global : bool ,
53+ #[ clap( help = "Config key path, e.g. `cscs.current_system`" ) ]
54+ key_path : String ,
55+ #[ clap( help = "Value to set" , value_parser = parse_toml_value) ]
56+ value : toml_edit:: Value ,
57+ } ,
58+ Get {
59+ #[ clap( help = "Config key path, e.g. `cscs.current_system`" ) ]
60+ key_path : String ,
3161 } ,
3262}
3363
@@ -177,6 +207,17 @@ Data directory: {data_dir_path}"
177207 )
178208}
179209
210+ pub fn set_config < V : Into < toml_edit:: Value > > ( key_path : String , value : V , global : bool ) -> Result < ( ) > {
211+ let mut config = Config :: new ( ) ?;
212+ config. set ( & key_path, value, global) ?;
213+ Ok ( ( ) )
214+ }
215+
216+ pub fn get_config ( key_path : String ) -> Result < String > {
217+ let config = Config :: new ( ) ?;
218+ config. get ( & key_path)
219+ }
220+
180221fn parse_key_val < T , U > ( s : & str ) -> Result < ( T , U ) , Box < dyn Error + Send + Sync + ' static > >
181222where
182223 T : std:: str:: FromStr ,
@@ -201,3 +242,22 @@ where
201242 . ok_or_else ( || format ! ( "invalid KEY:value: no `:` found in `{s}`" ) ) ?;
202243 Ok ( ( s[ ..pos] . parse ( ) ?, s[ pos + 1 ..] . parse ( ) ?) )
203244}
245+
246+ pub fn parse_toml_value ( value_str : & str ) -> Result < toml_edit:: Value , toml_edit:: TomlError > {
247+ match value_str. parse ( ) {
248+ Ok ( value) => Ok ( value) ,
249+ Err ( _) if is_bare_string ( value_str) => Ok ( value_str. into ( ) ) ,
250+ Err ( err) => Err ( err) ,
251+ }
252+ }
253+ fn is_bare_string ( value_str : & str ) -> bool {
254+ // leading whitespace isn't ignored when parsing TOML value expression, but
255+ // "\n[]" doesn't look like a bare string.
256+ let trimmed = value_str. trim_ascii ( ) . as_bytes ( ) ;
257+ if let ( Some ( & first) , Some ( & last) ) = ( trimmed. first ( ) , trimmed. last ( ) ) {
258+ // string, array, or table constructs?
259+ !matches ! ( first, b'"' | b'\'' | b'[' | b'{' ) && !matches ! ( last, b'"' | b'\'' | b']' | b'}' )
260+ } else {
261+ true // empty or whitespace only
262+ }
263+ }
0 commit comments