diff --git a/build.ps1 b/build.ps1 index 98b1d9f2..ec49a162 100644 --- a/build.ps1 +++ b/build.ps1 @@ -92,7 +92,7 @@ New-Item -ItemType Directory $target > $null $windows_projects = @("pal", "ntreg", "ntstatuserror", "ntuserinfo", "registry") $projects = @("dsc_lib", "dsc", "osinfo", "process", "test_group_resource", "y2j", "powershellgroup") -$pedantic_clean_projects = @("dsc_lib", "dsc", "osinfo", "process", "y2j", "pal", "ntstatuserror", "ntuserinfo", "test_group_resource", "sshdconfig") +$pedantic_unclean_projects = @("ntreg") if ($IsWindows) { $projects += $windows_projects @@ -108,14 +108,14 @@ foreach ($project in $projects) { if (Test-Path "./Cargo.toml") { if ($Clippy) { - if ($pedantic_clean_projcets -contains $project) { - Write-Verbose -Verbose "Running clippy with pedantic for $project" - cargo clippy @flags --% -- -Dwarnings -Dclippy::pedantic - } - else { + if ($pedantic_unclean_projects -contains $project) { Write-Verbose -Verbose "Running clippy for $project" cargo clippy @flags -- -Dwarnings } + else { + Write-Verbose -Verbose "Running clippy with pedantic for $project" + cargo clippy @flags --% -- -Dwarnings -Dclippy::pedantic + } } else { cargo build @flags diff --git a/dsc/src/resource_command.rs b/dsc/src/resource_command.rs index 0cae2db2..4a630596 100644 --- a/dsc/src/resource_command.rs +++ b/dsc/src/resource_command.rs @@ -18,10 +18,9 @@ pub fn get(dsc: &mut DscManager, resource: &str, input: &Option, stdin: let mut input = get_input(input, stdin); let mut resource = get_resource(dsc, resource); //TODO: add to debug stream: println!("handle_resource_get - {} implemented_as - {:?}", resource.type_name, resource.implemented_as); - if resource.requires.is_some() - { + if let Some(requires) = resource.requires { input = add_type_name_to_json(input, resource.type_name); - resource = get_resource(dsc, &resource.requires.clone().unwrap()); + resource = get_resource(dsc, &requires.clone()); } //TODO: add to debug stream: println!("handle_resource_get - input - {}", input); @@ -79,10 +78,9 @@ pub fn set(dsc: &mut DscManager, resource: &str, input: &Option, stdin: //TODO: add to debug stream: println!("handle_resource_set - {} implemented_as - {:?}", resource.type_name, resource.implemented_as); - if resource.requires.is_some() - { + if let Some(requires) = resource.requires { input = add_type_name_to_json(input, resource.type_name); - resource = get_resource(dsc, &resource.requires.clone().unwrap()); + resource = get_resource(dsc, &requires.clone()); } //TODO: add to debug stream: println!("handle_resource_get - input - {}", input); @@ -112,10 +110,9 @@ pub fn test(dsc: &mut DscManager, resource: &str, input: &Option, stdin: //TODO: add to debug stream: println!("handle_resource_test - {} implemented_as - {:?}", resource.type_name, resource.implemented_as); - if resource.requires.is_some() - { + if let Some(requires) = resource.requires { input = add_type_name_to_json(input, resource.type_name); - resource = get_resource(dsc, &resource.requires.clone().unwrap()); + resource = get_resource(dsc, &requires.clone()); } //TODO: add to debug stream: println!("handle_resource_test - input - {}", input); @@ -165,7 +162,13 @@ pub fn export(dsc: &mut DscManager, resource: &str, format: &Option (), + Err(err) => { + eprintln!("Error: {err}"); + exit(EXIT_DSC_ERROR); + } + } let json = match serde_json::to_string(&conf) { Ok(json) => json, diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 679e6a25..ebf22d23 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -17,7 +17,7 @@ use jsonschema::{JSONSchema, ValidationError}; use serde_yaml::Value; use std::process::exit; -pub fn config_get(configurator: Configurator, format: &Option) +pub fn config_get(configurator: &Configurator, format: &Option) { match configurator.invoke_get(ErrorAction::Continue, || { /* code */ }) { Ok(result) => { @@ -40,7 +40,7 @@ pub fn config_get(configurator: Configurator, format: &Option) } } -pub fn config_set(configurator: Configurator, format: &Option) +pub fn config_set(configurator: &Configurator, format: &Option) { match configurator.invoke_set(ErrorAction::Continue, || { /* code */ }) { Ok(result) => { @@ -63,7 +63,7 @@ pub fn config_set(configurator: Configurator, format: &Option) } } -pub fn config_test(configurator: Configurator, format: &Option) +pub fn config_test(configurator: &Configurator, format: &Option) { match configurator.invoke_test(ErrorAction::Continue, || { /* code */ }) { Ok(result) => { @@ -86,7 +86,7 @@ pub fn config_test(configurator: Configurator, format: &Option) } } -pub fn config_export(configurator: Configurator, format: &Option) +pub fn config_export(configurator: &Configurator, format: &Option) { match configurator.invoke_export(ErrorAction::Continue, || { /* code */ }) { Ok(result) => { @@ -116,15 +116,15 @@ pub fn config_export(configurator: Configurator, format: &Option) } pub fn config(subcommand: &ConfigSubCommand, format: &Option, stdin: &Option) { - if stdin.is_none() { + let Some(stdin) = stdin else { eprintln!("Configuration must be piped to STDIN"); exit(EXIT_INVALID_ARGS); - } + }; - let json: serde_json::Value = match serde_json::from_str(stdin.as_ref().unwrap()) { + let json: serde_json::Value = match serde_json::from_str(stdin.as_ref()) { Ok(json) => json, Err(_) => { - match serde_yaml::from_str::(stdin.as_ref().unwrap()) { + match serde_yaml::from_str::(stdin.as_ref()) { Ok(yaml) => { match serde_json::to_value(yaml) { Ok(json) => json, @@ -153,23 +153,24 @@ pub fn config(subcommand: &ConfigSubCommand, format: &Option, stdi match subcommand { ConfigSubCommand::Get => { - config_get(configurator, format); + config_get(&configurator, format); }, ConfigSubCommand::Set => { - config_set(configurator, format); + config_set(&configurator, format); }, ConfigSubCommand::Test => { - config_test(configurator, format); + config_test(&configurator, format); }, ConfigSubCommand::Validate => { validate_config(&json_string); }, ConfigSubCommand::Export => { - config_export(configurator, format); + config_export(&configurator, format); } } } +#[allow(clippy::too_many_lines)] pub fn validate_config(config: &str) { // first validate against the config schema let schema = match serde_json::to_value(get_schema(DscType::Configuration)) { @@ -198,7 +199,7 @@ pub fn validate_config(config: &str) { for e in err { error.push_str(&format!("\n{e} ")); } - eprintln!("{}", error); + eprintln!("{error}"); exit(EXIT_INVALID_INPUT); }; @@ -211,7 +212,11 @@ pub fn validate_config(config: &str) { }; // then validate each resource - for resource_block in config_value["resources"].as_array().unwrap().iter() { + let Some(resources) = config_value["resources"].as_array() else { + eprintln!("Error: Resources not specified"); + exit(EXIT_INVALID_INPUT); + }; + for resource_block in resources { let type_name = resource_block["type"].as_str().unwrap_or_else(|| { eprintln!("Error: Resource type not specified"); exit(EXIT_INVALID_INPUT); @@ -305,7 +310,7 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option, } }; let mut write_table = false; - let mut table = Table::new(vec!["Type", "Version", "Requires", "Description"]); + let mut table = Table::new(&["Type", "Version", "Requires", "Description"]); if format.is_none() && atty::is(Stream::Stdout) { // write as table if fornat is not specified and interactive write_table = true; @@ -313,11 +318,10 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option, for resource in dsc.find_resource(&resource_name.clone().unwrap_or_default()) { // if description is specified, skip if resource description does not contain it if description.is_some() || tags.is_some() { - if resource.manifest.is_none() { + let Some(ref resource_manifest) = resource.manifest else { continue; - } - - let resource_manifest = match serde_json::from_value::(resource.clone().manifest.unwrap().clone()) { + }; + let manifest = match serde_json::from_value::(resource_manifest.clone()) { Ok(resource_manifest) => resource_manifest, Err(err) => { eprintln!("Error in manifest for {0}: {err}", resource.type_name); @@ -325,25 +329,20 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option, } }; - if description.is_some() { - if resource_manifest.description.is_none() { - continue; - } - - if !resource_manifest.description.unwrap().to_lowercase().contains(&description.as_ref().unwrap().to_lowercase()) { - continue; - } + if description.is_some() && + (manifest.description.is_none() | !manifest.description.unwrap_or_default().to_lowercase().contains(&description.as_ref().unwrap_or(&String::new()).to_lowercase())) { + continue; } // if tags is specified, skip if resource tags do not contain the tags - if tags.is_some() { - if resource_manifest.tags.is_none() { + if let Some(tags) = tags { + let Some(manifest_tags) = manifest.tags else { continue; - } + }; let mut found = false; - for tag_to_find in tags.clone().unwrap() { - for tag in resource_manifest.tags.clone().unwrap() { + for tag_to_find in tags { + for tag in &manifest_tags { if tag.to_lowercase() == tag_to_find.to_lowercase() { found = true; break; @@ -385,7 +384,7 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option, table.print(); } }, - ResourceSubCommand::Get { resource, input, all } => { + ResourceSubCommand::Get { resource, input, all } => { if *all { resource_command::get_all(&mut dsc, resource, input, stdin, format); } else { resource_command::get(&mut dsc, resource, input, stdin, format); }; }, diff --git a/dsc/src/tablewriter.rs b/dsc/src/tablewriter.rs index 84eed959..4a6778b0 100644 --- a/dsc/src/tablewriter.rs +++ b/dsc/src/tablewriter.rs @@ -7,12 +7,22 @@ pub struct Table { } impl Table { - pub fn new(header: Vec<&str>) -> Table { + /// Create a new table. + /// + /// # Arguments + /// + /// * `header` - The header row + /// + /// # Returns + /// + /// * `Table` - The new table + #[must_use] + pub fn new(header: &[&str]) -> Table { let mut column_widths = Vec::new(); - for header_text in header.iter() { + for header_text in header { column_widths.push(header_text.len()); } - let header = header.iter().map(|s| s.to_string()).collect::>(); + let header = header.iter().map(|s| (*s).to_string()).collect::>(); Table { header, rows: Vec::new(), @@ -20,6 +30,11 @@ impl Table { } } + /// Add a row to the table. + /// + /// # Arguments + /// + /// * `row` - The row to add pub fn add_row(&mut self, row: Vec) { for (i, column) in row.iter().enumerate() { if column.len() > self.column_widths[i] { @@ -29,8 +44,9 @@ impl Table { self.rows.push(row); } + /// Print the table to the console. pub fn print(&self) { - let (width, _) = size().unwrap(); + let (width, _) = size().unwrap_or((80, 25)); // make header bright green println!("\x1b[1;32m"); let mut header_row = String::new(); diff --git a/dsc/src/util.rs b/dsc/src/util.rs index cdb429cd..9dcc34c3 100644 --- a/dsc/src/util.rs +++ b/dsc/src/util.rs @@ -21,7 +21,7 @@ use std::collections::HashMap; use std::process::exit; use syntect::{ easy::HighlightLines, - highlighting::{Style, ThemeSet}, + highlighting::ThemeSet, parsing::SyntaxSet, util::{as_24_bit_terminal_escaped, LinesWithEndings} }; @@ -34,6 +34,16 @@ pub const EXIT_INVALID_INPUT: i32 = 4; pub const EXIT_VALIDATION_FAILED: i32 = 5; pub const EXIT_CTRL_C: i32 = 6; +/// Get string representation of JSON value. +/// +/// # Arguments +/// +/// * `json` - The JSON to convert +/// +/// # Returns +/// +/// * `String` - The JSON as a string +#[must_use] pub fn serde_json_value_to_string(json: &serde_json::Value) -> String { match serde_json::to_string(&json) { @@ -45,6 +55,21 @@ pub fn serde_json_value_to_string(json: &serde_json::Value) -> String } } +/// Add fields to the JSON. +/// +/// # Arguments +/// +/// * `json` - The JSON to add the fields to +/// * `fields_to_add` - The fields to add +/// +/// # Returns +/// +/// * `String` - The JSON with the fields added +/// +/// # Errors +/// +/// * `DscError` - The JSON is invalid +#[allow(clippy::implicit_hasher)] pub fn add_fields_to_json(json: &str, fields_to_add: &HashMap) -> Result { let mut v = serde_json::from_str::(json)?; @@ -59,6 +84,17 @@ pub fn add_fields_to_json(json: &str, fields_to_add: &HashMap) - Ok(result) } +/// Add the type property value to the JSON. +/// +/// # Arguments +/// +/// * `json` - The JSON to add the type property to +/// * `type_name` - The type name to add +/// +/// # Returns +/// +/// * `String` - The JSON with the type property added +#[must_use] pub fn add_type_name_to_json(json: String, type_name: String) -> String { let mut map:HashMap = HashMap::new(); @@ -79,6 +115,16 @@ pub fn add_type_name_to_json(json: String, type_name: String) -> String } } +/// Get the JSON schema for requested type. +/// +/// # Arguments +/// +/// * `dsc_type` - The type of schema to get +/// +/// # Returns +/// +/// * `RootSchema` - The schema +#[must_use] pub fn get_schema(dsc_type: DscType) -> RootSchema { match dsc_type { DscType::GetResult => { @@ -111,6 +157,12 @@ pub fn get_schema(dsc_type: DscType) -> RootSchema { } } +/// Write the output to the console +/// +/// # Arguments +/// +/// * `json` - The JSON to write +/// * `format` - The format to use pub fn write_output(json: &str, format: &Option) { let mut is_json = true; if atty::is(Stream::Stdout) { @@ -153,16 +205,21 @@ pub fn write_output(json: &str, format: &Option) { let ps = SyntaxSet::load_defaults_newlines(); let ts = ThemeSet::load_defaults(); - let syntax = if is_json { - ps.find_syntax_by_extension("json").unwrap() + let Some(syntax) = (if is_json { + ps.find_syntax_by_extension("json") } else { - ps.find_syntax_by_extension("yaml").unwrap() + ps.find_syntax_by_extension("yaml") + }) else { + println!("{json}"); + return; }; let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]); for line in LinesWithEndings::from(&output) { - let ranges: Vec<(Style, &str)> = h.highlight_line(line, &ps).unwrap(); + let Ok(ranges) = h.highlight_line(line, &ps) else { + continue; + }; let escaped = as_24_bit_terminal_escaped(&ranges[..], false); print!("{escaped}"); } diff --git a/dsc_lib/src/configure/depends_on.rs b/dsc_lib/src/configure/depends_on.rs index 1b5373be..41efc655 100644 --- a/dsc_lib/src/configure/depends_on.rs +++ b/dsc_lib/src/configure/depends_on.rs @@ -7,6 +7,19 @@ use crate::configure::config_doc::Resource; use crate::configure::Configuration; use crate::DscError; +/// Gets the invocation order of resources based on their dependencies +/// +/// # Arguments +/// +/// * `config` - The configuration to get the invocation order for +/// +/// # Returns +/// +/// * `Result, DscError>` - The invocation order of resources +/// +/// # Errors +/// +/// * `DscError::Validation` - The configuration is invalid pub fn get_resource_invocation_order(config: &Configuration) -> Result, DscError> { let mut order: Vec = Vec::new(); let depends_on_regex = Regex::new(r"^\[resourceId\(\s*'(?[a-zA-Z0-9\.]+/[a-zA-Z0-9]+)'\s*,\s*'(?[a-zA-Z0-9 ]+)'\s*\)]$")?; @@ -17,17 +30,14 @@ pub fn get_resource_invocation_order(config: &Configuration) -> Result Result Result<(), DscError> { + let export_result = resource.export()?; for (i, instance) in export_result.actual_state.iter().enumerate() { let mut r = config_doc::Resource::new(); r.resource_type = resource.type_name.clone(); r.name = format!("{}-{i}", r.resource_type); - let props: HashMap = serde_json::from_value(instance.clone()).unwrap(); + let props: HashMap = serde_json::from_value(instance.clone())?; r.properties = Some(props); conf.resources.push(r); } + + Ok(()) } impl Configurator { @@ -190,6 +202,20 @@ impl Configurator { result.into_iter().collect() } + /// Invoke the export operation on a configuration. + /// + /// # Arguments + /// + /// * `error_action` - The error action to use. + /// * `progress_callback` - A callback to call when progress is made. + /// + /// # Returns + /// + /// * `ConfigurationExportResult` - The result of the export operation. + /// + /// # Errors + /// + /// This function will return an error if the underlying resource fails. pub fn invoke_export(&self, _error_action: ErrorAction, _progress_callback: impl Fn() + 'static) -> Result { let (config, messages, had_errors) = self.validate_config()?; @@ -215,7 +241,7 @@ impl Configurator { let Some(dsc_resource) = self.discovery.find_resource(&resource.resource_type).next() else { return Err(DscError::ResourceNotFound(resource.resource_type.clone())); }; - add_resource_export_results_to_configuration(&dsc_resource, &mut conf); + add_resource_export_results_to_configuration(&dsc_resource, &mut conf)?; } result.result = Some(conf); diff --git a/dsc_lib/src/dscerror.rs b/dsc_lib/src/dscerror.rs index 1e628c98..73d35b14 100644 --- a/dsc_lib/src/dscerror.rs +++ b/dsc_lib/src/dscerror.rs @@ -94,6 +94,8 @@ impl Default for StreamMessage { } impl StreamMessage { + /// Create a new message + #[must_use] pub fn new() -> Self { Self { message: String::new(), @@ -103,6 +105,19 @@ impl StreamMessage { resource_path: String::new(), } } + + /// Create a new error message + /// + /// # Arguments + /// + /// * `message` - The message to display + /// * `resource_type_name` - The name of the resource type + /// * `resource_path` - The path to the resource + /// + /// # Returns + /// + /// * `StreamMessage` - The new message + #[must_use] pub fn new_error(message: String, resource_type_name: Option, resource_path: Option) -> StreamMessage { StreamMessage { message, @@ -112,6 +127,19 @@ impl StreamMessage { resource_path: resource_path.unwrap_or("None".to_string()) } } + + /// Create a new warning message + /// + /// # Arguments + /// + /// * `message` - The message to display + /// * `resource_type_name` - The name of the resource type + /// * `resource_path` - The path to the resource + /// + /// # Returns + /// + /// * `StreamMessage` - The new message + #[must_use] pub fn new_warning(message: String, resource_type_name: Option, resource_path: Option) -> StreamMessage { StreamMessage { message, @@ -121,6 +149,17 @@ impl StreamMessage { resource_path: resource_path.unwrap_or("None".to_string()) } } + + /// Print the message to the console + /// + /// # Arguments + /// + /// * `error_format` - The format to use for error messages + /// * `warning_format` - The format to use for warning messages + /// + /// # Errors + /// + /// * `DscError` - If there is an error writing to the console pub fn print(&self, error_format:&StreamMessageType, warning_format:&StreamMessageType) -> Result<(), DscError>{ if self.message_type == StreamMessageType::Error { diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index e0ebc65d..26a61bc5 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -200,6 +200,21 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re } } +/// Invoke the validate operation against a command resource. +/// +/// # Arguments +/// +/// * `resource` - The resource manifest for the command resource. +/// * `cwd` - The current working directory. +/// * `config` - The configuration to validate in JSON. +/// +/// # Returns +/// +/// * `ValidateResult` - The result of the validate operation. +/// +/// # Errors +/// +/// Error is returned if the underlying command returns a non-zero exit code. pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) -> Result { // TODO: use schema to validate config if validate is not implemented let Some(validate) = resource.validate.as_ref() else { @@ -255,14 +270,27 @@ pub fn get_schema(resource: &ResourceManifest, cwd: &str) -> Result Result { - if resource.export.is_none() - { + let Some(export) = resource.export.as_ref() else { return Err(DscError::Operation(format!("Export is not supported by resource {}", &resource.resource_type))) - } + }; - let (exit_code, stdout, stderr) = invoke_command(&resource.export.clone().unwrap().executable, resource.export.clone().unwrap().args.clone(), None, Some(cwd))?; + let (exit_code, stdout, stderr) = invoke_command(&export.executable, export.args.clone(), None, Some(cwd))?; if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); } @@ -272,7 +300,7 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str) -> Result {r}, Result::Err(err) => { - return Err(DscError::Operation(format!("Failed to parse json from export {}|{}|{} -> {err}", &resource.export.clone().unwrap().executable, stdout, stderr))) + return Err(DscError::Operation(format!("Failed to parse json from export {}|{}|{} -> {err}", &export.executable, stdout, stderr))) } }; instances.push(instance); diff --git a/ntreg/src/registry_value.rs b/ntreg/src/registry_value.rs index 2249d857..c36e100a 100644 --- a/ntreg/src/registry_value.rs +++ b/ntreg/src/registry_value.rs @@ -153,7 +153,7 @@ fn get_data_value(value_information: PKEY_VALUE_FULL_INFORMATION) -> RegistryVal }; let data_ptr = unsafe { - (value_information as *const u8).add((*value_information).DataOffset as usize) as *const u8 + (value_information as *const u8).add((*value_information).DataOffset as usize) }; let data = unsafe { diff --git a/process/src/main.rs b/process/src/main.rs index 48e50076..7a1bffe6 100644 --- a/process/src/main.rs +++ b/process/src/main.rs @@ -48,15 +48,7 @@ fn main() { } exit(0); }, - "get" => { // used for testing only - print_input(); - exit(0); - }, - "set" => { // used for testing only - print_input(); - exit(0); - }, - "test" => { // used for testing only + "get" | "set" | "test" => { // used for testing only print_input(); exit(0); }, diff --git a/registry/src/config.rs b/registry/src/config.rs index 8ac7482f..1202ab77 100644 --- a/registry/src/config.rs +++ b/registry/src/config.rs @@ -24,7 +24,7 @@ pub enum RegistryValueData { #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] #[serde(rename = "Registry", deny_unknown_fields)] -pub struct RegistryConfig { +pub struct Registry { /// The ID of the resource. Value is ignored for input. #[serde(rename = "$id", skip_serializing_if = "Option::is_none")] pub id: Option, @@ -53,12 +53,12 @@ pub struct RegistryConfig { pub in_desired_state: Option, } -impl RegistryConfig { +impl Registry { pub fn to_json(&self) -> String { match serde_json::to_string(self) { Ok(json) => json, Err(e) => { - eprintln!("Failed to serialize to JSON: {}", e); + eprintln!("Failed to serialize to JSON: {e}"); String::new() } } @@ -67,7 +67,7 @@ impl RegistryConfig { const ID: &str = "https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json"; -impl Default for RegistryConfig { +impl Default for Registry { fn default() -> Self { Self { id: Some(ID.to_string()), diff --git a/registry/src/main.rs b/registry/src/main.rs index e70dbe6b..fb9d780c 100644 --- a/registry/src/main.rs +++ b/registry/src/main.rs @@ -12,7 +12,7 @@ use clap::Parser; use schemars::schema_for; use std::{io::{self, Read}, process::exit}; -use crate::config::RegistryConfig; +use crate::config::Registry; mod args; #[cfg(onecore)] @@ -26,6 +26,7 @@ const EXIT_INVALID_INPUT: i32 = 2; const EXIT_REGISTRY_ERROR: i32 = 3; const EXIT_JSON_SERIALIZATION_FAILED: i32 = 4; +#[allow(clippy::too_many_lines)] fn main() { #[cfg(debug_assertions)] check_debug(); @@ -39,31 +40,28 @@ fn main() { let input = match String::from_utf8(buffer) { Ok(input) => input, Err(e) => { - eprintln!("Invalid UTF-8 sequence: {}", e); + eprintln!("Invalid UTF-8 sequence: {e}"); exit(EXIT_INVALID_INPUT); } }; Some(input) }; - let mut config: RegistryConfig = Default::default(); + let mut config: Registry = Registry::default(); // check if input is valid for subcommand match args.subcommand { args::SubCommand::Config { subcommand: _ } => { - match input { - Some(input) => { + if let Some(input) = input { config = match serde_json::from_str(&input) { Ok(config) => config, Err(err) => { - eprintln!("Error JSON does not match schema: {}", err); + eprintln!("Error JSON does not match schema: {err}"); exit(EXIT_INVALID_INPUT); } }; - }, - None => { - eprintln!("Error: Input JSON via STDIN is required for config subcommand."); - exit(EXIT_INVALID_PARAMETER); - } + } else { + eprintln!("Error: Input JSON via STDIN is required for config subcommand."); + exit(EXIT_INVALID_PARAMETER); } } _ => { @@ -76,26 +74,26 @@ fn main() { match args.subcommand { args::SubCommand::Query { key_path, value_name, recurse } => { - eprintln!("Get key_path: {}, value_name: {:?}, recurse: {}", key_path, value_name, recurse); + eprintln!("Get key_path: {key_path}, value_name: {value_name:?}, recurse: {recurse}"); }, args::SubCommand::Set { key_path, value } => { - eprintln!("Set key_path: {}, value: {}", key_path, value); + eprintln!("Set key_path: {key_path}, value: {value}"); }, args::SubCommand::Test => { eprintln!("Test"); }, args::SubCommand::Remove { key_path, value_name, recurse } => { - eprintln!("Remove key_path: {}, value_name: {:?}, recurse: {}", key_path, value_name, recurse); + eprintln!("Remove key_path: {key_path}, value_name: {value_name:?}, recurse: {recurse}"); }, args::SubCommand::Find { key_path, find, recurse, keys_only, values_only } => { - eprintln!("Find key_path: {}, find: {}, recurse: {:?}, keys_only: {:?}, values_only: {:?}", key_path, find, recurse, keys_only, values_only); + eprintln!("Find key_path: {key_path}, find: {find}, recurse: {recurse:?}, keys_only: {keys_only:?}, values_only: {values_only:?}"); }, args::SubCommand::Config { subcommand } => { let json: String; match regconfighelper::validate_config(&config) { Ok(_) => {}, Err(err) => { - eprintln!("Error validating config: {}", err); + eprintln!("Error validating config: {err}"); exit(EXIT_INVALID_INPUT); } } @@ -111,7 +109,7 @@ fn main() { json = config; }, Err(err) => { - eprintln!("Error getting config: {}", err); + eprintln!("Error getting config: {err}"); exit(EXIT_REGISTRY_ERROR); } } @@ -122,7 +120,7 @@ fn main() { json = result; }, Err(err) => { - eprintln!("Error setting config: {}", err); + eprintln!("Error setting config: {err}"); exit(EXIT_REGISTRY_ERROR); } } @@ -133,7 +131,7 @@ fn main() { json = result; }, Err(err) => { - eprintln!("Error testing config: {}", err); + eprintln!("Error testing config: {err}"); exit(EXIT_REGISTRY_ERROR); } } @@ -144,17 +142,17 @@ fn main() { exit(EXIT_JSON_SERIALIZATION_FAILED); } - println!("{}", json); + println!("{json}"); }, args::SubCommand::Schema { pretty } => { - let schema = schema_for!(RegistryConfig); + let schema = schema_for!(Registry); let json = if pretty { serde_json::to_string_pretty(&schema).unwrap() } else { serde_json::to_string(&schema).unwrap() }; - println!("{}", json); + println!("{json}"); }, } @@ -167,15 +165,10 @@ fn check_debug() { eprintln!("attach debugger to pid {} and press any key to continue", std::process::id()); loop { let event = event::read().unwrap(); - match event { - event::Event::Key(_key) => { - break; - } - _ => { - eprintln!("Unexpected event: {:?}", event); - continue; - } + if let event::Event::Key(_key) = event { + break; } + eprintln!("Unexpected event: {event:?}"); } } } diff --git a/registry/src/regconfighelper.rs b/registry/src/regconfighelper.rs index 7da0fd09..2678d7dd 100644 --- a/registry/src/regconfighelper.rs +++ b/registry/src/regconfighelper.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::config::{EnsureKind, RegistryConfig, RegistryValueData}; +use crate::config::{EnsureKind, Registry, RegistryValueData}; use ntreg::{registry_key::RegistryKey, registry_value::RegistryValueData as NtRegistryValueData, registry_value::RegistryValue}; use ntstatuserror::{NtStatusError, NtStatusErrorKind}; use std::fmt::{Display, Formatter}; @@ -22,15 +22,14 @@ impl From for RegistryError { impl Display for RegistryError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - RegistryError::NtStatus(err) => write!(f, "{}", err), - RegistryError::Json(err) => write!(f, "{}", err), - RegistryError::Input(err) => write!(f, "{}", err), + RegistryError::NtStatus(err) => write!(f, "{err}"), + RegistryError::Json(err) | RegistryError::Input(err) => write!(f, "{err}"), } } } -pub fn config_get(config: &RegistryConfig) -> Result { - let mut reg_result = RegistryConfig::default(); +pub fn config_get(config: &Registry) -> Result { + let mut reg_result = Registry::default(); let reg_key = match RegistryKey::new(config.key_path.as_str()) { Ok(reg_key) => reg_key, @@ -75,8 +74,8 @@ pub fn config_get(config: &RegistryConfig) -> Result { } } -pub fn config_set(config: &RegistryConfig) -> Result { - let mut reg_result: RegistryConfig = RegistryConfig::default(); +pub fn config_set(config: &Registry) -> Result { + let mut reg_result: Registry = Registry::default(); let reg_key: RegistryKey; match &config.value_name { None => { @@ -142,7 +141,7 @@ fn get_parent_key_path(key_path: &str) -> Result<&str, RegistryError> { match key_path.rfind('\\') { Some(index) => Ok(&key_path[..index]), None => { - Err(RegistryError::Input(format!("Invalid key path: {}", key_path))) + Err(RegistryError::Input(format!("Invalid key path: {key_path}"))) } } } @@ -215,7 +214,7 @@ fn get_valid_parent_key_and_subkeys(key_path: &str) -> Result<(RegistryKey, Vec< Ok((parent_key, subkeys)) } -pub fn validate_config(config: &RegistryConfig) -> Result<(), RegistryError>{ +pub fn validate_config(config: &Registry) -> Result<(), RegistryError>{ if config.value_data.is_some() && config.value_name.is_none() { return Err(RegistryError::Input("value_name is required when value_data is specified.".to_string())); } @@ -223,7 +222,7 @@ pub fn validate_config(config: &RegistryConfig) -> Result<(), RegistryError>{ Ok(()) } -pub fn config_test(config: &RegistryConfig) -> Result { +pub fn config_test(config: &Registry) -> Result { if config.value_name.is_none() { Ok(test_key(config)?) } @@ -232,8 +231,8 @@ pub fn config_test(config: &RegistryConfig) -> Result { } } -fn test_value(config: &RegistryConfig) -> Result { - let mut reg_result: RegistryConfig = Default::default(); +fn test_value(config: &Registry) -> Result { + let mut reg_result: Registry = Registry::default(); let mut in_desired_state = true; let reg_key = match RegistryKey::new(config.key_path.as_str()) { @@ -293,7 +292,7 @@ fn test_value(config: &RegistryConfig) -> Result { Ok(reg_result.to_json()) } -fn reg_values_are_eq(config: &RegistryConfig, reg_value: &RegistryValue) -> Result { +fn reg_values_are_eq(config: &Registry, reg_value: &RegistryValue) -> Result { let mut in_desired_state = true; if !reg_value.name.eq(config.value_name.as_ref().unwrap().as_str()) { @@ -308,7 +307,7 @@ fn reg_values_are_eq(config: &RegistryConfig, reg_value: &RegistryValue) -> Resu } else { let reg_value_data = convert_ntreg_data(®_value.data)?; - if reg_value_data != config.value_data.to_owned().unwrap() { + if reg_value_data != config.value_data.clone().unwrap() { in_desired_state = false; } } @@ -316,8 +315,8 @@ fn reg_values_are_eq(config: &RegistryConfig, reg_value: &RegistryValue) -> Resu Ok(in_desired_state) } -fn test_key(config: &RegistryConfig) -> Result { - let mut reg_result: RegistryConfig = Default::default(); +fn test_key(config: &Registry) -> Result { + let mut reg_result: Registry = Registry::default(); let key_exists = match RegistryKey::new(config.key_path.as_str()) { Ok( _ ) => { @@ -386,7 +385,7 @@ fn test_registry_value_present() { } "#; - let config: RegistryConfig = serde_json::from_str(input_json).unwrap(); + let config: Registry = serde_json::from_str(input_json).unwrap(); let json = config_test(&config).unwrap(); assert_eq!(json, r#"{"$id":"https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json","keyPath":"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion","valueName":"ProgramFilesPath","valueData":{"ExpandString":"%ProgramFiles%"},"_inDesiredState":true}"#); } @@ -401,7 +400,7 @@ fn test_registry_value_absent() { } "#; - let config: RegistryConfig = serde_json::from_str(input_json).unwrap(); + let config: Registry = serde_json::from_str(input_json).unwrap(); let json = config_test(&config).unwrap(); assert_eq!(json, r#"{"$id":"https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json","keyPath":"HKLM\\Software\\Microsoft\\Windows\\CurrentVersion","_inDesiredState":true}"#); }