11use std:: { error:: Error , path:: PathBuf } ;
22
33use clap:: { Args , 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 cscs:: api_client:: client:: { EdfSpec as EdfSpecEnum , ScriptSpec as ScriptSpecEnum } ,
910 util:: types:: DockerImageUrl ,
1011} ;
@@ -27,8 +28,37 @@ pub enum CliCommands {
2728 } ,
2829 #[ clap( about = "Create a new project configuration file" ) ]
2930 Init {
30- #[ clap( help = "Destination folder to create config in (default = current directory)" ) ]
31+ #[ clap( help = "destination folder to create config in (default = current directory)" ) ]
3132 destination : Option < PathBuf > ,
33+ #[ clap( help = "project name to use" ) ]
34+ name : Option < String > ,
35+ } ,
36+ #[ clap( about = "Manage configuration" ) ]
37+ Config {
38+ #[ command( subcommand) ]
39+ command : ConfigCommands ,
40+ } ,
41+ }
42+
43+ #[ derive( Subcommand , Debug ) ]
44+ pub enum ConfigCommands {
45+ #[ clap( about = "Set config values" ) ]
46+ Set {
47+ #[ clap(
48+ short,
49+ long,
50+ action,
51+ help = "whether to change the global config or the project local one"
52+ ) ]
53+ global : bool ,
54+ #[ clap( help = "Config key path, e.g. `cscs.current_system`" ) ]
55+ key_path : String ,
56+ #[ clap( help = "Value to set" , value_parser = parse_toml_value) ]
57+ value : toml_edit:: Value ,
58+ } ,
59+ Get {
60+ #[ clap( help = "Config key path, e.g. `cscs.current_system`" ) ]
61+ key_path : String ,
3262 } ,
3363}
3464
@@ -251,6 +281,17 @@ Data directory: {data_dir_path}"
251281 )
252282}
253283
284+ pub fn set_config < V : Into < toml_edit:: Value > > ( key_path : String , value : V , global : bool ) -> Result < ( ) > {
285+ let mut config = Config :: new ( ) ?;
286+ config. set ( & key_path, value, global) ?;
287+ Ok ( ( ) )
288+ }
289+
290+ pub fn get_config ( key_path : String ) -> Result < String > {
291+ let config = Config :: new ( ) ?;
292+ config. get ( & key_path)
293+ }
294+
254295fn parse_key_val < T , U > ( s : & str ) -> Result < ( T , U ) , Box < dyn Error + Send + Sync + ' static > >
255296where
256297 T : std:: str:: FromStr ,
@@ -275,3 +316,22 @@ where
275316 . ok_or_else ( || format ! ( "invalid KEY:value: no `:` found in `{s}`" ) ) ?;
276317 Ok ( ( s[ ..pos] . parse ( ) ?, s[ pos + 1 ..] . parse ( ) ?) )
277318}
319+
320+ pub fn parse_toml_value ( value_str : & str ) -> Result < toml_edit:: Value , toml_edit:: TomlError > {
321+ match value_str. parse ( ) {
322+ Ok ( value) => Ok ( value) ,
323+ Err ( _) if is_bare_string ( value_str) => Ok ( value_str. into ( ) ) ,
324+ Err ( err) => Err ( err) ,
325+ }
326+ }
327+ fn is_bare_string ( value_str : & str ) -> bool {
328+ // leading whitespace isn't ignored when parsing TOML value expression, but
329+ // "\n[]" doesn't look like a bare string.
330+ let trimmed = value_str. trim_ascii ( ) . as_bytes ( ) ;
331+ if let ( Some ( & first) , Some ( & last) ) = ( trimmed. first ( ) , trimmed. last ( ) ) {
332+ // string, array, or table constructs?
333+ !matches ! ( first, b'"' | b'\'' | b'[' | b'{' ) && !matches ! ( last, b'"' | b'\'' | b']' | b'}' )
334+ } else {
335+ true // empty or whitespace only
336+ }
337+ }
0 commit comments