config check: mostly functional now
This commit is contained in:
		
							parent
							
								
									264cd364ed
								
							
						
					
					
						commit
						45f5eeee57
					
				
					 1 changed files with 52 additions and 21 deletions
				
			
		|  | @ -80,6 +80,7 @@ pub fn interactive_check(config: KV) -> Result<(), Box<dyn Error>> { | |||
|     for group in CONFIG_SPEC.groups { | ||||
|         group_header( group.name, group.description ); | ||||
|         let todo = needs_info(&config, group); | ||||
|         // TODO: add distinction between edit all / edit necessary only
 | ||||
|         let choices = if !todo.is_empty() { | ||||
|             println!( "{}The following fields need adjustment: {}{}", ANSI_NOTICE, todo.join(", "), ANSI_RESET ); | ||||
|             &["[E]dit (default)", "[L]ist all", "[S]kip group"] | ||||
|  | @ -118,24 +119,36 @@ fn check_field( config: &KV, grpname: &str, field: &CfgField, ok: &mut bool ) -> | |||
|     let key = field.full_key(grpname); | ||||
|     let value = config.get(&key).ok(); | ||||
|     let mut actions: Vec<&'static str> = Vec::new(); | ||||
|     if value.is_some() { actions.push("[K]eep as-is"); } | ||||
|     if field.is_password() { | ||||
|         match value { | ||||
|             Some(_) => { | ||||
|                 println!("  current: {}", HIDDEN_PASSWORD); | ||||
|             }, | ||||
|             None => { | ||||
|                 println!("  current: {}", EMPTY_VALUE); | ||||
|                 actions.push("[K]eep as-is"); // we allow leaving the password empty if you don't want to set it
 | ||||
|             }, | ||||
|         }; | ||||
|         //todo!()
 | ||||
|         // TODO: [K]eep as-is (if exists), [R]emove, [I]nput
 | ||||
|     } else { | ||||
|         println!( "  current: {}", value.unwrap_or(EMPTY_VALUE.to_string()) ); | ||||
|         // TODO: [K]eep as-is, [R]eset to default, [I]nput, [L]ong (multiline) input
 | ||||
|     // TODO: adjust order: empty password should offer input first
 | ||||
|     // TODO: RandomId should offer generating as [R]egenerate
 | ||||
|     if value.is_some() | field.is_password() | field.is_optional() { actions.push("[K]eep as-is"); } | ||||
|     if field.default_description().is_some() { actions.push("[R]eset to default"); } | ||||
|     if field.is_password() { actions.push( "[R]emove" ); } | ||||
|     actions.push( "[I]nput new value" ); | ||||
|     if !field.is_password() { actions.push( "[L]ong (multiline) input" ) }; | ||||
| 
 | ||||
|     match prompt_action(&actions) { | ||||
|         'K' => { | ||||
|             // we allow leaving a password empty, but that means config is incomplete
 | ||||
|             if value.is_none() & !field.is_optional() { *ok &= false; } | ||||
|         }, | ||||
|         'R' => { | ||||
|             match field.default_value()? { | ||||
|                 Some(value) => { config.set(&key, &value)?; }, | ||||
|                 // password again
 | ||||
|                 None => { config.delete(&key)?; *ok &= false; }, | ||||
|             } | ||||
|         }, | ||||
|         'I' => { | ||||
|             let value = if field.is_password() { prompt_password() } else { prompt_single_line() }; | ||||
|             config.set(&key, &value)?; | ||||
|         }, | ||||
|         'L' => { | ||||
|             let value = prompt_multiline(); | ||||
|             config.set(&key, &value)?; | ||||
|         }, | ||||
|         _ => { return Err("Wat.".into()); } | ||||
|     } | ||||
|     // Action -
 | ||||
|     Ok(()) | ||||
| } | ||||
| 
 | ||||
|  | @ -157,7 +170,7 @@ fn show_field( config: &KV, grpname: &str, field: &CfgField ) { | |||
|     if field.is_silent() { return } | ||||
|     let key = field.full_key(grpname); | ||||
|     println!( "{}{}{} - {}", ANSI_FIELD, &key, ANSI_RESET, field.description() ); | ||||
|     println!( "  default: {}", field.default().unwrap_or(EMPTY_VALUE.to_string())); | ||||
|     println!( "  default: {}", field.default_description().unwrap_or(EMPTY_VALUE.to_string())); | ||||
|     let value = config.get(&key).ok() | ||||
|         .map(|s| if field.is_password() { HIDDEN_PASSWORD.to_string() } else { s }) | ||||
|         .unwrap_or(EMPTY_VALUE.to_string()); | ||||
|  | @ -228,7 +241,10 @@ fn prompt_multiline( ) -> String { | |||
| 
 | ||||
| /// Read a password without echoing it.
 | ||||
| fn prompt_password( ) -> String { | ||||
|     rpassword::prompt_password("New password (not shown) : ").unwrap() | ||||
|     let pass = rpassword::prompt_password("New password (not shown) : ").unwrap(); | ||||
|     // disabled echo means the newline also isn't shown
 | ||||
|     println!(""); | ||||
|     pass | ||||
| } | ||||
| 
 | ||||
| /* ***** config structure definition ***** */ | ||||
|  | @ -244,7 +260,7 @@ enum CfgField<'a> { | |||
|     /// Empty by default, required, will prompt without echo.
 | ||||
|     Password { key: &'a str, description: &'a str }, | ||||
|     /// Empty by default, can be user-provided, or will be generated randomly.
 | ||||
|     RandomId { key: &'a str, generator: fn() -> String, generator_description: &'a str, description: &'a str }, | ||||
|     RandomId { key: &'a str, generator: fn() -> Result<String,Box<dyn Error>>, generator_description: &'a str, description: &'a str }, | ||||
| } | ||||
| 
 | ||||
| /// A group of related config fields. The final key of an inner value will be
 | ||||
|  | @ -273,6 +289,11 @@ impl<'a> CfgField<'a> { | |||
|         matches!( self, CfgField::Password { .. } ) | ||||
|     } | ||||
| 
 | ||||
|     /// Optional fields are allowed to be (and stay) empty.
 | ||||
|     fn is_optional(&self) -> bool { | ||||
|         matches!( self, CfgField::Optional { .. } ) | ||||
|     } | ||||
| 
 | ||||
|     /// Reports if the field needs changing or is ok as-is.
 | ||||
|     ///
 | ||||
|     /// (Because of the default population at boot, Silent and Default must
 | ||||
|  | @ -315,7 +336,7 @@ impl<'a> CfgField<'a> { | |||
| 
 | ||||
|     /// Gets a description of the default value if one exists.  
 | ||||
|     /// (Either the value itself, or a description of the generator making a default value.)
 | ||||
|     fn default(&self) -> Option<String> { | ||||
|     fn default_description(&self) -> Option<String> { | ||||
|         match self { | ||||
|             CfgField::Silent { default, .. } => Some(default.to_string()), | ||||
|             CfgField::Default { default, .. } => Some(default.to_string()), | ||||
|  | @ -323,4 +344,14 @@ impl<'a> CfgField<'a> { | |||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /// Gets the actual default value, which may involve running a generator.
 | ||||
|     fn default_value(&self) -> Result<Option<String>, Box<dyn Error>> { | ||||
|         match self { | ||||
|             CfgField::Silent { default, .. } => Ok(Some(default.to_string())), | ||||
|             CfgField::Default { default, .. } => Ok(Some(default.to_string())), | ||||
|             CfgField::RandomId { generator, .. } => generator().map(Some), | ||||
|             _ => Ok(None), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 nobody
						nobody