From 5d4e4979b42004134d2f95c20ac9d99d74042eaa Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 3 Apr 2026 15:03:42 -0700 Subject: [PATCH 01/22] Misc fixes for firewall and types --- .../src/types/fully_qualified_type_name.rs | 3 --- resources/windows_firewall/src/firewall.rs | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/dsc-lib/src/types/fully_qualified_type_name.rs b/lib/dsc-lib/src/types/fully_qualified_type_name.rs index 6e73a7f36..a4a2eb88e 100644 --- a/lib/dsc-lib/src/types/fully_qualified_type_name.rs +++ b/lib/dsc-lib/src/types/fully_qualified_type_name.rs @@ -1,9 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -// TODO: Remove when updating to rustc 1.94; false positive from thiserror + rust-i18n t!() macro -#![allow(unused_assignments)] - use std::fmt::{Display, Formatter}; use std::hash::Hash; use std::ops::Deref; diff --git a/resources/windows_firewall/src/firewall.rs b/resources/windows_firewall/src/firewall.rs index bf75cd2aa..3a9ca93e3 100644 --- a/resources/windows_firewall/src/firewall.rs +++ b/resources/windows_firewall/src/firewall.rs @@ -69,6 +69,8 @@ impl FirewallStore { .cast() .map_err(|error| t!("firewall.ruleEnumerationFailed", error = error.to_string()).to_string())?; results.push(rule); + + unsafe { windows::Win32::System::Variant::VariantClear(&mut variant[0]) }; } Ok(results) @@ -169,6 +171,10 @@ fn profiles_from_mask(mask: i32) -> Vec { } fn profiles_to_mask(values: &[String]) -> Result { + if values.is_empty() { + return Ok(NET_FW_PROFILE2_ALL.0); + } + let mut mask = 0; for value in values { match value.to_ascii_lowercase().as_str() { @@ -197,6 +203,10 @@ fn join_csv(value: &[String]) -> String { } fn interface_types_to_string(values: &[String]) -> Result { + if values.is_empty() { + return Ok("All".to_string()); + } + let mut normalized = Vec::new(); for value in values { match value.to_ascii_lowercase().as_str() { @@ -269,6 +279,12 @@ fn apply_rule_properties(rule: &INetFwRule, desired: &FirewallRule, existing_pro // the existing rule's protocol (if updating an existing rule). let effective_protocol = desired.protocol.or(existing_protocol); + // If effective_protocol is None, read the current protocol from the rule. + let effective_protocol = match effective_protocol { + Some(protocol) => Some(protocol), + None => Some(unsafe { rule.Protocol() }.map_err(&err)?), + }; + // Reject port specifications for protocols that don't support them (e.g. ICMP). // This must be checked regardless of whether the protocol itself was changed, // because the caller may only be setting local_ports or remote_ports. From 0c230f3d31c7d603dcf4e6b75a0c74a666b071e7 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Mon, 6 Apr 2026 12:31:10 -0700 Subject: [PATCH 02/22] fix safety of VariantClear --- resources/windows_firewall/src/firewall.rs | 37 +++++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/resources/windows_firewall/src/firewall.rs b/resources/windows_firewall/src/firewall.rs index 3a9ca93e3..80001f83e 100644 --- a/resources/windows_firewall/src/firewall.rs +++ b/resources/windows_firewall/src/firewall.rs @@ -8,11 +8,37 @@ use windows::Win32::Foundation::{S_FALSE, VARIANT_BOOL}; use windows::Win32::NetworkManagement::WindowsFirewall::*; use windows::Win32::System::Com::{CLSCTX_INPROC_SERVER, CoCreateInstance, CoInitializeEx, CoUninitialize, IDispatch, COINIT_APARTMENTTHREADED}; use windows::Win32::System::Ole::IEnumVARIANT; -use windows::Win32::System::Variant::VARIANT; +use windows::Win32::System::Variant::{VARIANT, VariantClear}; use crate::types::{FirewallError, FirewallRule, FirewallRuleList, RuleAction, RuleDirection}; use crate::util::matches_any_filter; +/// RAII wrapper for VARIANT that automatically calls VariantClear on drop +struct SafeVariant(VARIANT); + +impl SafeVariant { + fn new() -> Self { + Self(VARIANT::default()) + } + + fn as_mut_ptr(&mut self) -> *mut VARIANT { + &mut self.0 + } + + fn as_ref(&self) -> &VARIANT { + &self.0 + } +} + +impl Drop for SafeVariant { + fn drop(&mut self) { + let hr = unsafe { VariantClear(&mut self.0) }; + if hr.is_err() { + eprintln!("Warning: VariantClear failed with HRESULT: {:08x}", hr.0); + } + } +} + struct ComGuard; impl ComGuard { @@ -55,22 +81,23 @@ impl FirewallStore { let mut results = Vec::new(); loop { let mut fetched = 0u32; - let mut variant = [VARIANT::default()]; - let hr = unsafe { enum_variant.Next(&mut variant, &mut fetched) }; + let mut safe_variant = SafeVariant::new(); + let variant_slice = unsafe { std::slice::from_raw_parts_mut(safe_variant.as_mut_ptr(), 1) }; + let hr = unsafe { enum_variant.Next(variant_slice, &mut fetched) }; if hr == S_FALSE || fetched == 0 { break; } hr.ok() .map_err(|error| t!("firewall.ruleEnumerationFailed", error = error.to_string()).to_string())?; - let dispatch = IDispatch::try_from(&variant[0]) + let dispatch = IDispatch::try_from(safe_variant.as_ref()) .map_err(|error: windows::core::Error| t!("firewall.ruleEnumerationFailed", error = error.to_string()).to_string())?; let rule: INetFwRule = dispatch .cast() .map_err(|error| t!("firewall.ruleEnumerationFailed", error = error.to_string()).to_string())?; results.push(rule); - unsafe { windows::Win32::System::Variant::VariantClear(&mut variant[0]) }; + // SafeVariant will automatically call VariantClear when it goes out of scope } Ok(results) From 16e26839bbe28e0a729ff40a2949066afdb36189 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Mon, 6 Apr 2026 12:53:28 -0700 Subject: [PATCH 03/22] fix copilot feedback --- lib/dsc-lib/src/types/fully_qualified_type_name.rs | 3 +++ resources/windows_firewall/src/firewall.rs | 5 ++--- resources/windows_firewall/src/main.rs | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/dsc-lib/src/types/fully_qualified_type_name.rs b/lib/dsc-lib/src/types/fully_qualified_type_name.rs index a4a2eb88e..6e73a7f36 100644 --- a/lib/dsc-lib/src/types/fully_qualified_type_name.rs +++ b/lib/dsc-lib/src/types/fully_qualified_type_name.rs @@ -1,6 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +// TODO: Remove when updating to rustc 1.94; false positive from thiserror + rust-i18n t!() macro +#![allow(unused_assignments)] + use std::fmt::{Display, Formatter}; use std::hash::Hash; use std::ops::Deref; diff --git a/resources/windows_firewall/src/firewall.rs b/resources/windows_firewall/src/firewall.rs index 80001f83e..5c9556178 100644 --- a/resources/windows_firewall/src/firewall.rs +++ b/resources/windows_firewall/src/firewall.rs @@ -32,9 +32,8 @@ impl SafeVariant { impl Drop for SafeVariant { fn drop(&mut self) { - let hr = unsafe { VariantClear(&mut self.0) }; - if hr.is_err() { - eprintln!("Warning: VariantClear failed with HRESULT: {:08x}", hr.0); + if let Err(e) = unsafe { VariantClear(&mut self.0) } { + crate::write_error(&format!("Warning: VariantClear failed with HRESULT: {:#010x}", e.code().0)); } } } diff --git a/resources/windows_firewall/src/main.rs b/resources/windows_firewall/src/main.rs index c18093a95..3036ed2f1 100644 --- a/resources/windows_firewall/src/main.rs +++ b/resources/windows_firewall/src/main.rs @@ -19,7 +19,7 @@ const EXIT_INVALID_ARGS: i32 = 1; const EXIT_INVALID_INPUT: i32 = 2; const EXIT_FIREWALL_ERROR: i32 = 3; -fn write_error(message: &str) { +pub(crate) fn write_error(message: &str) { eprintln!("{}", serde_json::json!({ "error": message })); } From b3b4b8b013f590b51ef0c19f64fa12398aaee098 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 10:02:58 -0500 Subject: [PATCH 04/22] (MAINT) Fix invoking clippy for build Prior to this change, the clippy invocation in `Build-RustProject` never actually invoked clippy because the check required _all_ projects to be set to require clippy with this check: ```powershell $Clippy -and !$Project.ClippyUnclean ``` However, this _never_ evaluated to true because the function invokes against the full list of packages. This change addresses the problem by: 1. Defining a new self-contained `Test-Clippy` function, which encapsulates the logic for arranging and invoking clippy against DSC rust projects. It now invokes clippy twice, once for general (non-pedantic) projects and again for pedantic projects. This can potentially be optimized in the future by adding linting configuration to project cargo manifests. 1. Updates the `Build-RustProject` function to use `Test-Clippy`. 1. Defines a new field in `.project.data.json`, `RustPackageName`, which is needed to specify specific packages for `cargo` commands when the containing folder for a crate has a different name from the value of the `package.name` field in a cargo manifest. This change required updating the validating JSON schema for the data files and updating the type definition in build helpers to match. 1. Adds a new instance method, `ToPackageFlags()`, to `DscProjectDefinition` to enable quickly emitting the `-p ` pair for use in invoking cargo commands. Now that the clippy checks are applying, the build will fail locally and in CI. Follow up changes will be made to separately fix each crate. --- .vscode/schemas/definitions.json | 7 + helpers.build.psm1 | 140 +++++++++++++++++-- resources/WindowsUpdate/.project.data.json | 1 + resources/dscecho/.project.data.json | 1 + resources/osinfo/.project.data.json | 1 + resources/process/.project.data.json | 1 + resources/registry/.project.data.json | 1 + resources/runcommandonset/.project.data.json | 1 + resources/sshdconfig/.project.data.json | 1 + 9 files changed, 144 insertions(+), 10 deletions(-) diff --git a/.vscode/schemas/definitions.json b/.vscode/schemas/definitions.json index e7ce77e2f..0838b90b5 100644 --- a/.vscode/schemas/definitions.json +++ b/.vscode/schemas/definitions.json @@ -23,6 +23,7 @@ "Kind": { "$ref": "#/$defs/ProjectKind" }, "SupportedPlatformOS": { "$ref": "#/$defs/ProjectSupportedPlatformOS" }, "IsRust": { "$ref": "#/$defs/ProjectIsRust" }, + "RustPackageName": { "$ref": "#/$defs/ProjectRustPackageName" }, "TestOnly": { "$ref": "#/$defs/ProjectTestOnly" }, "SkipTest": { "$ref": "#/$defs/ProjectSkipTest" }, "ClippyUnclean": { "$ref": "#/$defs/ProjectClippyUnclean" }, @@ -70,6 +71,7 @@ "Kind": { "$ref": "#/$defs/ProjectKind" }, "SupportedPlatformOS": { "$ref": "#/$defs/ProjectSupportedPlatformOS" }, "IsRust": { "$ref": "#/$defs/ProjectIsRust" }, + "RustPackageName": { "$ref": "#/$defs/ProjectRustPackageName" }, "TestOnly": { "$ref": "#/$defs/ProjectTestOnly" }, "SkipTest": { "$ref": "#/$defs/ProjectSkipTest" }, "ClippyUnclean": { "$ref": "#/$defs/ProjectClippyUnclean" }, @@ -190,6 +192,11 @@ "title": "Is Rust Crate", "markdownDescription": "Indicates whether the project is a Rust crate." }, + "ProjectRustPackageName": { + "type": "string", + "title": "Rust package name", + "markdownDescription": "Defines the name of the rust project for the `-p` option on cargo commands." + }, "ProjectTestOnly": { "type": "boolean", "default": false, diff --git a/helpers.build.psm1 b/helpers.build.psm1 index 430a2373b..571a6cc8b 100644 --- a/helpers.build.psm1 +++ b/helpers.build.psm1 @@ -28,6 +28,7 @@ class DscProjectDefinition { [string] $RelativePath [string] $Kind [bool] $IsRust + [string] $RustPackageName [bool] $ClippyUnclean [bool] $ClippyPedanticUnclean [bool] $SkipTestProject @@ -38,6 +39,17 @@ class DscProjectDefinition { [DscProjectCopyFiles] $CopyFiles [DscProjectSkipTest] $SkipTest + [string[]] ToPackageFlags() { + if (-not $this.IsRust) { + return $null + } + + return @( + '-p' + $this.RustPackageName ?? $this.Name + ) + } + [string] ToJson([bool]$forBuild) { $json = $this.ToData($forBuild) | ConvertTo-Json -Depth 5 -EnumsAsStrings return ($json -replace "`r`n", "`n") @@ -68,6 +80,9 @@ class DscProjectDefinition { } if ($this.IsRust) { $data.IsRust = $true + if (-not [string]::IsNullOrEmpty($this.RustPackageName)) { + $data.RustPackageName = $this.RustPackageName + } if ($this.ClippyPedanticUnclean) { $data.ClippyPedanticUnclean = $true } @@ -1416,6 +1431,113 @@ function Export-GrammarBinding { } } } + +function Test-Clippy { + [CmdletBinding()] + param( + [DscProjectDefinition[]]$Project, + [ValidateSet('current','aarch64-pc-windows-msvc','x86_64-pc-windows-msvc','aarch64-apple-darwin','x86_64-apple-darwin','aarch64-unknown-linux-gnu','aarch64-unknown-linux-musl','x86_64-unknown-linux-gnu','x86_64-unknown-linux-musl')] + $Architecture = 'current', + [switch]$Release, + [switch]$NoModifyWorkspace + ) + + begin { + $flags = @($Release ? '-r' : $null) + if ($Architecture -ne 'current') { + $flags += '--target' + $flags += $Architecture + $memberGroup = if ($Architecture -match 'linux') { + 'Linux' + } elseif ($Architecture -match 'darwin') { + 'macOS' + } elseif ($Architecture -match 'windows') { + 'Windows' + } else { + throw "Unsupported architecture '$Architecture'" + } + } else { + $memberGroup = if ($IsLinux) { + 'Linux' + } elseif ($IsMacOS) { + 'macOS' + } elseif ($IsWindows) { + 'Windows' + } + } + $workspaceParams = @{ + MemberGroup = $memberGroup + } + if ($VerbosePreference -eq 'Continue') { + $workspaceParams.Verbose = $true + } + if (-not $NoModifyWorkspace) { + Set-DefaultWorkspaceMemberGroup @workspaceParams + } + } + + process { + $clippyFlags = @( + '--%' + '--' + '-Dwarnings' # Treat warnings as errors + '--no-deps' # Only lint DSC projects, not dependencies + ) + # Collect projects to lint into two groups: + # - Normal projects get linting without pedantic checks + # - Pedantic projects get stricter linting + [DscProjectDefinition[]]$normalProjects = @() + [DscProjectDefinition[]]$pedanticProjects = @() + foreach ($p in $Project) { + if ( + -not $p.IsRust -or + $p.ClippyUnclean -or + (Test-ShouldSkipProject -Project $p -Architecture $Architecture) + ) { + continue + } + + if ($p.ClippyPedanticUnclean) { + $pedanticProjects += $p + } else { + $normalProjects += $p + } + } + $normalProjectsExitCode = 0 + $pedanticProjectsExitCode = 0 + if ($normalProjects.count -gt 0) { + [string[]]$projectFlags = $normalProjects.ToPackageFlags() + Write-Verbose "Linting rust projects with clippy: $($normalProjects.Name | ConvertTo-Json)" + Write-Verbose "Invoking clippy: cargo clippy $flags $projectFlags $clippyFlags" + cargo clippy @flags @projectFlags @clippyFlags + + if ($null -ne $LASTEXITCODE -and $LASTEXITCODE -ne 0) { + $normalProjectsExitCode = $LASTEXITCODE + } + } + if ($pedanticProjects.count -gt 0) { + [string[]]$projectFlags = $pedanticProjects.ToPackageFlags() + $clippyFlags += '-Dclippy::pedantic' + Write-Verbose "Linting rust projects with clippy (pedantic): $($pedanticProjects.Name | ConvertTo-Json)" + Write-Verbose "Invoking clippy: cargo clippy $flags $projectFlags $clippyFlags" + cargo clippy @flags @projectFlags @clippyFlags + + if ($null -ne $LASTEXITCODE -and $LASTEXITCODE -ne 0) { + $pedanticProjectsExitCode = $LASTEXITCODE + } + } + if ($normalProjectsExitCode -or $pedanticProjectsExitCode) { + throw "Clippy failed for at least one project" + } + } + + clean { + if (-not $NoModifyWorkspace) { + Reset-DefaultWorkspaceMemberGroup + } + } +} + function Build-RustProject { [CmdletBinding()] param( @@ -1475,18 +1597,16 @@ function Build-RustProject { cargo clean } - if ($Clippy -and !$Project.ClippyUnclean) { - $clippyFlags = @() - if (!$Project.ClippyPedanticUnclean) { - cargo clippy @clippyFlags --% -- -Dclippy::pedantic --no-deps -Dwarnings - } else { - cargo clippy @clippyFlags --% -- -Dwarnings --no-deps - } - - if ($null -ne $LASTEXITCODE -and $LASTEXITCODE -ne 0) { - throw "Last exit code is $LASTEXITCODE, clippy failed for at least one project" + if ($Clippy) { + $clippyParams = @{ + Project = $Project + Architecture = $Architecture + Release = $Release + NoModifyWorkspace = $true } + Test-Clippy @clippyParams } + $members = Get-DefaultWorkspaceMemberGroup Write-Verbose -Verbose "Building rust projects: [$members]" Write-Verbose "Invoking cargo:`n`tcargo build $flags" diff --git a/resources/WindowsUpdate/.project.data.json b/resources/WindowsUpdate/.project.data.json index c447c1c56..05b2dbfd0 100644 --- a/resources/WindowsUpdate/.project.data.json +++ b/resources/WindowsUpdate/.project.data.json @@ -2,6 +2,7 @@ "Name": "windowsupdate", "Kind": "Resource", "IsRust": true, + "RustPackageName": "dsc-resource-windows-update", "SupportedPlatformOS": "Windows", "Binaries": [ "wu_dsc" diff --git a/resources/dscecho/.project.data.json b/resources/dscecho/.project.data.json index 429167b4a..2e86b4470 100644 --- a/resources/dscecho/.project.data.json +++ b/resources/dscecho/.project.data.json @@ -2,6 +2,7 @@ "Name": "dscecho", "Kind": "Resource", "IsRust": true, + "RustPackageName": "dsc-resource-echo", "Binaries": [ "dscecho" ], diff --git a/resources/osinfo/.project.data.json b/resources/osinfo/.project.data.json index ad1292e2f..f0765c163 100644 --- a/resources/osinfo/.project.data.json +++ b/resources/osinfo/.project.data.json @@ -2,6 +2,7 @@ "Name": "osinfo", "Kind": "Resource", "IsRust": true, + "RustPackageName": "dsc-resource-osinfo", "Binaries": [ "osinfo" ], diff --git a/resources/process/.project.data.json b/resources/process/.project.data.json index 0f47a6fb7..53986b109 100644 --- a/resources/process/.project.data.json +++ b/resources/process/.project.data.json @@ -2,6 +2,7 @@ "Name": "process", "Kind": "Resource", "IsRust": true, + "RustPackageName": "dsc-resource-process", "Binaries": [ "process" ], diff --git a/resources/registry/.project.data.json b/resources/registry/.project.data.json index bdeaa0726..2ff010f4f 100644 --- a/resources/registry/.project.data.json +++ b/resources/registry/.project.data.json @@ -2,6 +2,7 @@ "Name": "registry", "Kind": "Resource", "IsRust": true, + "RustPackageName": "dsc-resource-registry", "SupportedPlatformOS": "Windows", "Binaries": [ "registry" diff --git a/resources/runcommandonset/.project.data.json b/resources/runcommandonset/.project.data.json index 15a44ce14..8509dd2c1 100644 --- a/resources/runcommandonset/.project.data.json +++ b/resources/runcommandonset/.project.data.json @@ -2,6 +2,7 @@ "Name": "runcommandonset", "Kind": "Resource", "IsRust": true, + "RustPackageName": "dsc-resource-run_command_on_set", "Binaries": [ "runcommandonset" ], diff --git a/resources/sshdconfig/.project.data.json b/resources/sshdconfig/.project.data.json index 33510799e..ab5d83ec1 100644 --- a/resources/sshdconfig/.project.data.json +++ b/resources/sshdconfig/.project.data.json @@ -2,6 +2,7 @@ "Name": "sshdconfig", "Kind": "Resource", "IsRust": true, + "RustPackageName": "dsc-resource-sshdconfig", "Binaries": [ "sshdconfig" ], From 54f1ec8c1a689382a66d4a67d35331f5d5c5371c Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 10:23:27 -0500 Subject: [PATCH 05/22] (MAINT) Fix clippy for `dsc-lib-jsonschema` --- lib/dsc-lib-jsonschema/build.rs | 4 +-- .../src/dsc_repo/dsc_repo_schema.rs | 14 ++++++++--- lib/dsc-lib-jsonschema/src/dsc_repo/mod.rs | 8 +++--- .../src/schema_utility_extensions.rs | 25 ++++++++----------- .../transforms/vscodify_refs_and_defs.rs | 2 +- 5 files changed, 26 insertions(+), 27 deletions(-) diff --git a/lib/dsc-lib-jsonschema/build.rs b/lib/dsc-lib-jsonschema/build.rs index 59ac916f6..812a42907 100644 --- a/lib/dsc-lib-jsonschema/build.rs +++ b/lib/dsc-lib-jsonschema/build.rs @@ -519,9 +519,7 @@ fn main() { let version_data = read_to_string(data_path) .expect("Failed to read .versions.json file"); let version_info: VersionInfo = serde_json::from_str(&version_data) - .expect(format!( - "Failed to parse version data from .versions.json:\n---FILE TEXT START---\n{version_data}\n---FILE TEXT END---\n" - ).as_str()); + .unwrap_or_else(|_| panic!("Failed to parse version data from .versions.json:\n---FILE TEXT START---\n{version_data}\n---FILE TEXT END---\n")); let contents = format_file_content(&version_info); fs::write( diff --git a/lib/dsc-lib-jsonschema/src/dsc_repo/dsc_repo_schema.rs b/lib/dsc-lib-jsonschema/src/dsc_repo/dsc_repo_schema.rs index a5ba4f4b9..eb18a111c 100644 --- a/lib/dsc-lib-jsonschema/src/dsc_repo/dsc_repo_schema.rs +++ b/lib/dsc-lib-jsonschema/src/dsc_repo/dsc_repo_schema.rs @@ -79,6 +79,7 @@ pub trait DscRepoSchema : JsonSchema { /// function overrides this default when exporting schemas for various versions and forms. /// /// [`generate_exportable_schema()`]: DscRepoSchema::generate_exportable_schema + #[must_use] fn default_export_schema_id_uri() -> String { Self::get_schema_id_uri( RecognizedSchemaVersion::VNext, @@ -91,12 +92,14 @@ pub trait DscRepoSchema : JsonSchema { /// /// Use this to define the `$schema` keyword when deriving or manually implementing the /// [`schemars::JsonSchema`] trait. + #[must_use] fn default_export_meta_schema_uri() -> String { "https://json-schema.org/draft/2020-12/schema".to_string() } /// Generates the JSON schema for a given version and form. This function is /// useful for exporting the JSON Schema to disk. + #[must_use] fn generate_exportable_schema( schema_version: RecognizedSchemaVersion, schema_form: SchemaForm @@ -105,6 +108,7 @@ pub trait DscRepoSchema : JsonSchema { } /// Generates the JSON Schema for a given version, form, and URI prefix. + #[must_use] fn generate_schema( schema_version: RecognizedSchemaVersion, schema_form: SchemaForm, @@ -167,6 +171,7 @@ pub trait DscRepoSchema : JsonSchema { } /// Returns the path for a schema relative to the `schemas` folder. + #[must_use] fn get_schema_relative_path( schema_version: RecognizedSchemaVersion, schema_form: SchemaForm @@ -176,12 +181,12 @@ pub trait DscRepoSchema : JsonSchema { path.push(schema_version.to_string()); let form_folder = schema_form.to_folder_prefix(); - let form_folder = form_folder.trim_end_matches("/"); + let form_folder = form_folder.trim_end_matches('/'); if !form_folder.is_empty() { path.push(form_folder); } - for segment in Self::SCHEMA_FOLDER_PATH.split("/") { + for segment in Self::SCHEMA_FOLDER_PATH.split('/') { path.push(segment); } @@ -218,7 +223,7 @@ pub trait DscRepoSchema : JsonSchema { fn set_enhanced_schema_id_uri(schema: &mut Schema, schema_version: RecognizedSchemaVersion) { if let Some(id_uri) = Self::get_enhanced_schema_id_uri(schema_version) { schema.set_id(&id_uri); - }; + } } /// Returns the URI for the canonical (non-bundled) form of the schema with the default @@ -262,7 +267,7 @@ pub trait DscRepoSchema : JsonSchema { fn set_bundled_schema_id_uri(schema: &mut Schema, schema_version: RecognizedSchemaVersion) { if let Some(id_uri) = Self::get_bundled_schema_id_uri(schema_version) { schema.set_id(&id_uri); - }; + } } /// Returns the list of recognized schema URIs for the struct or enum. @@ -328,6 +333,7 @@ pub trait DscRepoSchema : JsonSchema { /// /// - If the value is `true`, all schema forms are valid for the type. /// - If the value is `false`, only [`SchemaForm::Canonical`] is valid for the type. + #[must_use] fn get_valid_schema_forms() -> Vec { if Self::SCHEMA_SHOULD_BUNDLE { vec![SchemaForm::VSCode, SchemaForm::Bundled, SchemaForm::Canonical] diff --git a/lib/dsc-lib-jsonschema/src/dsc_repo/mod.rs b/lib/dsc-lib-jsonschema/src/dsc_repo/mod.rs index 303d258a1..eb12d1e30 100644 --- a/lib/dsc-lib-jsonschema/src/dsc_repo/mod.rs +++ b/lib/dsc-lib-jsonschema/src/dsc_repo/mod.rs @@ -193,18 +193,16 @@ pub(crate) fn get_default_schema_form(should_bundle: bool) -> SchemaForm { /// Retrieves the version segment from the `$id` keyword of a DSC repo schema. pub(crate) fn get_schema_id_version(schema: &Schema) -> Option { - let Some(root_id) = schema.get_id() else { - return None; - }; + let root_id = schema.get_id()?; // Remove the URI prefix and leading slash to get the URI relative to the `schemas` folder let schema_folder_relative_id = root_id .trim_start_matches(&SchemaUriPrefix::AkaDotMs.to_string()) .trim_start_matches(&SchemaUriPrefix::Github.to_string()) - .trim_start_matches("/"); + .trim_start_matches('/'); // The version segment is the first segment of the relative URI schema_folder_relative_id - .split("/") + .split('/') .collect::>() .first() .map(std::string::ToString::to_string) diff --git a/lib/dsc-lib-jsonschema/src/schema_utility_extensions.rs b/lib/dsc-lib-jsonschema/src/schema_utility_extensions.rs index 431e4700d..57fd02040 100644 --- a/lib/dsc-lib-jsonschema/src/schema_utility_extensions.rs +++ b/lib/dsc-lib-jsonschema/src/schema_utility_extensions.rs @@ -1153,8 +1153,8 @@ pub trait SchemaUtilityExtensions { /// use dsc_lib_jsonschema::schema_utility_extensions::SchemaUtilityExtensions; /// use schemars::json_schema; /// - /// let foo_id = &"https://contoso.com/schemas/properties/foo.json".to_string(); - /// let bar_id = &"https://contoso.com/schemas/properties/bar.json".to_string(); + /// let foo_id = "https://contoso.com/schemas/properties/foo.json"; + /// let bar_id = "https://contoso.com/schemas/properties/bar.json"; /// let schema = json_schema!({ /// "$id": "https://contoso.com/schemas/example.json", /// "properties": { @@ -1177,7 +1177,7 @@ pub trait SchemaUtilityExtensions { /// None /// ); /// ``` - fn get_bundled_schema_resource_defs_key(&self, id: &String) -> Option<&String>; + fn get_bundled_schema_resource_defs_key(&self, id: &str) -> Option<&String>; /// Inserts a subschema entry into the `$defs` keyword for the [`Schema`]. If an entry for the /// given key already exists, this function returns the old value as a map. /// @@ -1925,21 +1925,18 @@ impl SchemaUtilityExtensions for Schema { None } - fn get_bundled_schema_resource_defs_key(&self, id: &String) -> Option<&String> { - let Some(defs) = self.get_defs() else { - return None; - }; + fn get_bundled_schema_resource_defs_key(&self, id: &str) -> Option<&String> { + let defs = self.get_defs()?; for (def_key, def_value) in defs { let Ok(def_subschema) = Schema::try_from(def_value.clone()) else { continue; }; - if let Some(def_id) = def_subschema.get_id() { - if def_id == id.as_str() { + if let Some(def_id) = def_subschema.get_id() + && def_id == id { return Some(def_key); } - } } None @@ -1973,7 +1970,7 @@ impl SchemaUtilityExtensions for Schema { for bundled_id in lookup_schema.get_bundled_schema_resource_ids(true) { let Some(bundled_key) = lookup_schema.get_bundled_schema_resource_defs_key( - &bundled_id.to_string() + bundled_id ) else { continue; }; @@ -2148,7 +2145,7 @@ impl SchemaUtilityExtensions for Schema { references } fn get_references_to_bundled_schema_resource(&self, resource_id: &str) -> HashSet<&str> { - let Some(def_key) = self.get_bundled_schema_resource_defs_key(&resource_id.to_string()) else { + let Some(def_key) = self.get_bundled_schema_resource_defs_key(resource_id) else { return HashSet::new(); }; @@ -2167,7 +2164,7 @@ impl SchemaUtilityExtensions for Schema { self.get_references() .into_iter() - .filter(|reference| matching_references.contains(&&reference.to_string())) + .filter(|reference| matching_references.contains(&reference.to_string())) .collect() } fn replace_references(&mut self, find_value: &str, new_value: &str) { @@ -2244,7 +2241,7 @@ impl SchemaUtilityExtensions for Schema { let lookup_schema = self.clone(); let bundled_ids = lookup_schema.get_bundled_schema_resource_ids(true); for bundled_id in bundled_ids { - let Some(defs_key) = lookup_schema.get_bundled_schema_resource_defs_key(&bundled_id.to_string()) else { + let Some(defs_key) = lookup_schema.get_bundled_schema_resource_defs_key(bundled_id) else { continue; }; let reference_lookup = format!("#/$defs/{defs_key}"); diff --git a/lib/dsc-lib-jsonschema/src/vscode/transforms/vscodify_refs_and_defs.rs b/lib/dsc-lib-jsonschema/src/vscode/transforms/vscodify_refs_and_defs.rs index b89144ac8..b09d24927 100644 --- a/lib/dsc-lib-jsonschema/src/vscode/transforms/vscodify_refs_and_defs.rs +++ b/lib/dsc-lib-jsonschema/src/vscode/transforms/vscodify_refs_and_defs.rs @@ -94,7 +94,7 @@ pub fn vscodify_refs_and_defs(schema: &mut Schema) { let lookup_schema = schema.clone(); for bundled_resource_id in lookup_schema.get_bundled_schema_resource_ids(true) { let Some(def_key) = lookup_schema - .get_bundled_schema_resource_defs_key(&bundled_resource_id.to_string()) else { + .get_bundled_schema_resource_defs_key(bundled_resource_id) else { continue; }; From 40e76eb40faf5ba2eaa504e458f01920309ce045 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 13:12:08 -0500 Subject: [PATCH 06/22] (MAINT) Fix clippy for `dsc-lib` --- lib/dsc-lib/src/configure/depends_on.rs | 5 +- lib/dsc-lib/src/configure/mod.rs | 101 ++++++++--------- lib/dsc-lib/src/configure/parameters.rs | 12 +- .../src/discovery/command_discovery.rs | 17 ++- lib/dsc-lib/src/discovery/discovery_trait.rs | 2 +- lib/dsc-lib/src/discovery/mod.rs | 3 +- .../src/dscresources/command_resource.rs | 103 +++++++----------- lib/dsc-lib/src/dscresources/dscresource.rs | 39 +++---- lib/dsc-lib/src/functions/contains.rs | 28 ++--- .../src/functions/data_uri_to_string.rs | 4 +- lib/dsc-lib/src/lib.rs | 1 - lib/dsc-lib/src/types/date_version.rs | 65 +++++------ lib/dsc-lib/src/types/exit_code.rs | 10 +- lib/dsc-lib/src/types/exit_codes_map.rs | 10 +- .../src/types/fully_qualified_type_name.rs | 11 +- lib/dsc-lib/src/types/resource_version.rs | 31 +++--- lib/dsc-lib/src/types/resource_version_req.rs | 27 +++-- lib/dsc-lib/src/types/semantic_version.rs | 18 +-- lib/dsc-lib/src/types/semantic_version_req.rs | 22 ++-- lib/dsc-lib/src/types/wildcard_type_name.rs | 6 +- 20 files changed, 234 insertions(+), 281 deletions(-) diff --git a/lib/dsc-lib/src/configure/depends_on.rs b/lib/dsc-lib/src/configure/depends_on.rs index bed2d43ec..90c1dc56f 100644 --- a/lib/dsc-lib/src/configure/depends_on.rs +++ b/lib/dsc-lib/src/configure/depends_on.rs @@ -37,8 +37,8 @@ pub fn get_resource_invocation_order(config: &Configuration, parser: &mut Statem let mut dependency_already_in_order = true; // Skip dependency validation for copy loop resources here - it will be handled in unroll_and_push // where the copy context is properly set up for copyIndex() expressions in dependsOn - if resource.copy.is_none() { - if let Some(depends_on) = resource.depends_on.clone() { + if resource.copy.is_none() + && let Some(depends_on) = resource.depends_on.clone() { for dependency in depends_on { let statement = parser.parse_and_execute(&dependency, context)?; let Some(string_result) = statement.as_str() else { @@ -62,7 +62,6 @@ pub fn get_resource_invocation_order(config: &Configuration, parser: &mut Statem dependency_already_in_order = false; } } - } if order.iter().any(|r| r.name == resource.name && r.resource_type == resource.resource_type) { // if dependencies were already in the order, then this might be a circular dependency diff --git a/lib/dsc-lib/src/configure/mod.rs b/lib/dsc-lib/src/configure/mod.rs index 19af3094f..a00a0c884 100644 --- a/lib/dsc-lib/src/configure/mod.rs +++ b/lib/dsc-lib/src/configure/mod.rs @@ -241,11 +241,11 @@ fn add_metadata(dsc_resource: &DscResource, mut properties: Option = Map::new(); - if let Some(resource_metadata) = resource_metadata { - if !resource_metadata.other.is_empty() { + if let Some(resource_metadata) = resource_metadata + && !resource_metadata.other.is_empty() { metadata.extend(resource_metadata.other); } - } + let mut dsc_value = Map::new(); dsc_value.insert("context".to_string(), Value::String("configuration".to_string())); metadata.insert("Microsoft.DSC".to_string(), Value::Object(dsc_value)); @@ -284,11 +284,11 @@ fn add_metadata(dsc_resource: &DscResource, mut properties: Option) -> Option { - if let Some(directives) = resource_directives { - if let Some(require_adapter) = &directives.require_adapter { + if let Some(directives) = resource_directives + && let Some(require_adapter) = &directives.require_adapter { return Some(require_adapter.clone()); } - } + None } @@ -298,19 +298,22 @@ fn check_security_context(metadata: Option<&Metadata>, directive_security_contex } let mut security_context_required: Option<&SecurityContextKind> = None; - if let Some(metadata) = &metadata { - if let Some(microsoft_dsc) = &metadata.microsoft { - if let Some(required_security_context) = µsoft_dsc.security_context { - warn!("{}", t!("configure.mod.securityContextInMetadataDeprecated")); - security_context_required = Some(required_security_context); - } + if let Some(metadata) = &metadata + && let Some(microsoft_dsc) = &metadata.microsoft + && let Some(required_security_context) = µsoft_dsc.security_context { + warn!("{}", t!("configure.mod.securityContextInMetadataDeprecated")); + security_context_required = Some(required_security_context); } - } - if let Some(directive_security_context) = &directive_security_context { - if security_context_required.is_some() && security_context_required != Some(directive_security_context) { - return Err(DscError::SecurityContext(t!("configure.mod.conflictingSecurityContext", metadata = security_context_required.unwrap(), directive = directive_security_context).to_string())); - } + if let Some(directive_security_context) = directive_security_context { + if let Some(security_context_required) = security_context_required + && security_context_required != directive_security_context { + return Err(DscError::SecurityContext(t!( + "configure.mod.conflictingSecurityContext", + metadata = security_context_required, + directive = directive_security_context + ).to_string())); + } security_context_required = Some(directive_security_context); } @@ -395,7 +398,7 @@ fn get_metadata_from_result(mut context: Option<&mut Context>, properties: &mut // set the current process env vars to the new values for (key, value) in env_vars { unsafe { - std::env::set_var(&key.to_string(), &value); + std::env::set_var(&key, &value); } } } @@ -485,18 +488,16 @@ impl Configurator { fn get_properties(&mut self, resource: &Resource, resource_kind: &Kind) -> Result>, DscError> { // Restore copy loop context from resource metadata under Microsoft.DSC/copyLoops if present - if let Some(metadata) = &resource.metadata { - if let Some(microsoft) = &metadata.microsoft { - if let Some(copy_loops) = µsoft.copy_loops { - for (loop_name, value) in copy_loops { - if let Some(index) = value.as_i64() { - self.context.copy.insert(loop_name.to_string(), index); - self.context.copy_current_loop_name.clone_from(loop_name); - } + if let Some(metadata) = &resource.metadata + && let Some(microsoft) = &metadata.microsoft + && let Some(copy_loops) = µsoft.copy_loops { + for (loop_name, value) in copy_loops { + if let Some(index) = value.as_i64() { + self.context.copy.insert(loop_name.to_string(), index); + self.context.copy_current_loop_name.clone_from(loop_name); } } } - } let result = match resource_kind { Kind::Group => { @@ -544,7 +545,7 @@ impl Configurator { let adapter = get_require_adapter_from_directive(&resource.directives); find_resource_or_error!(dsc_resource, discovery, resource, adapter); let properties = self.get_properties(&resource, &dsc_resource.kind)?; - let filter = add_metadata(&dsc_resource, properties, resource.metadata.clone())?; + let filter = add_metadata(dsc_resource, properties, resource.metadata.clone())?; let start_datetime = chrono::Local::now(); let mut get_result = match dsc_resource.get(&filter) { Ok(result) => result, @@ -650,7 +651,7 @@ impl Configurator { } }; - let desired = add_metadata(&dsc_resource, properties, resource.metadata.clone())?; + let desired = add_metadata(dsc_resource, properties, resource.metadata.clone())?; trace!("{}", t!("configure.mod.desired", state = desired)); let start_datetime; @@ -742,16 +743,13 @@ impl Configurator { // Process metadata - only add whatIf if we have ResourceWhatIf variant let mut execution_information = ExecutionInformation::new_with_duration(&start_datetime, &end_datetime); let mut other_metadata = Map::new(); - if self.context.execution_type == ExecutionKind::WhatIf { - if let Some(delete_res) = delete_what_if_metadata { - if let Some(metadata) = delete_res.metadata { - if let Some(what_if) = metadata.what_if { - execution_information.what_if = Some(what_if.clone()); - other_metadata.insert("whatIf".to_string(), what_if); - } - } + if self.context.execution_type == ExecutionKind::WhatIf + && let Some(delete_res) = delete_what_if_metadata + && let Some(metadata) = delete_res.metadata + && let Some(what_if) = metadata.what_if { + execution_information.what_if = Some(what_if.clone()); + other_metadata.insert("whatIf".to_string(), what_if); } - } let mut metadata = Metadata { microsoft: Some( @@ -827,7 +825,7 @@ impl Configurator { find_resource_or_error!(dsc_resource, discovery, resource, adapter); let properties = self.get_properties(&resource, &dsc_resource.kind)?; debug!("resource_type {}", &resource.resource_type); - let expected = add_metadata(&dsc_resource, properties, resource.metadata.clone())?; + let expected = add_metadata(dsc_resource, properties, resource.metadata.clone())?; trace!("{}", t!("configure.mod.expectedState", state = expected)); let start_datetime = chrono::Local::now(); let mut test_result = match dsc_resource.test(&expected) { @@ -917,9 +915,9 @@ impl Configurator { find_resource_or_error!(dsc_resource, discovery, resource, adapter); let properties = self.get_properties(resource, &dsc_resource.kind)?; debug!("resource_type {}", &resource.resource_type); - let input = add_metadata(&dsc_resource, properties, resource.metadata.clone())?; + let input = add_metadata(dsc_resource, properties, resource.metadata.clone())?; trace!("{}", t!("configure.mod.exportInput", input = input)); - let export_result = match add_resource_export_results_to_configuration(&dsc_resource, &mut conf, input.as_str()) { + let export_result = match add_resource_export_results_to_configuration(dsc_resource, &mut conf, input.as_str()) { Ok(result) => result, Err(e) => { progress.set_failure(get_failure_from_error(&e)); @@ -1064,11 +1062,10 @@ impl Configurator { } self.context.parameters.insert(name.clone(), (value.clone(), constraint.parameter_type.clone())); - if let Some(parameters) = &mut self.config.parameters { - if let Some(parameter) = parameters.get_mut(&name) { + if let Some(parameters) = &mut self.config.parameters + && let Some(parameter) = parameters.get_mut(&name) { parameter.default_value = Some(value); } - } } else { return Err(DscError::Validation(t!("configure.mod.parameterNotDefined", name = name).to_string())); @@ -1205,31 +1202,25 @@ impl Configurator { fn validate_config(&mut self) -> Result<(), DscError> { let config: Configuration = serde_json::from_str(self.json.as_str())?; let config_security_context = if let Some(directives) = &config.directives { - if let Some(security_context) = &directives.security_context { - Some(security_context.clone()) - } else { - None - } + directives.security_context.clone() } else { None }; check_security_context(config.metadata.as_ref(), config_security_context.as_ref())?; - if let Some(directives) = &config.directives { - if let Some(version_req) = &directives.version { + if let Some(directives) = &config.directives + && let Some(version_req) = &directives.version { let dsc_version = SemanticVersion::parse(env!("CARGO_PKG_VERSION"))?; if !version_req.matches(&dsc_version) { return Err(DscError::Validation(t!("configure.mod.versionNotSatisfied", required_version = version_req, current_version = env!("CARGO_PKG_VERSION")).to_string())); } } - } let mut resource_discovery_mode = ResourceDiscoveryMode::PreDeployment; - if let Some(directives) = &config.directives { - if let Some(resource_discovery_directive) = &directives.resource_discovery { + if let Some(directives) = &config.directives + && let Some(resource_discovery_directive) = &directives.resource_discovery { resource_discovery_mode = resource_discovery_directive.clone(); } - } if resource_discovery_mode == ResourceDiscoveryMode::DuringDeployment { debug!("{}", t!("configure.mod.skippingResourceDiscovery")); diff --git a/lib/dsc-lib/src/configure/parameters.rs b/lib/dsc-lib/src/configure/parameters.rs index b9357da13..7bb482d88 100644 --- a/lib/dsc-lib/src/configure/parameters.rs +++ b/lib/dsc-lib/src/configure/parameters.rs @@ -61,11 +61,12 @@ impl Display for SecureObject { /// `true` if the value is a secure value, `false` otherwise. #[must_use] pub fn is_secure_value(value: &Value) -> bool { - if let Some(obj) = value.as_object() { - if obj.len() == 1 && (obj.contains_key("secureString") || obj.contains_key("secureObject")) { + if let Some(obj) = value.as_object() + && obj.len() == 1 + && (obj.contains_key("secureString") || obj.contains_key("secureObject")) { return true; } - } + false } @@ -89,7 +90,7 @@ pub fn import_parameters(parameters: &Value) -> Result, D result }, Err(_) => { - let simple_input = match serde_json::from_value::(parameters.clone()) { + match serde_json::from_value::(parameters.clone()) { Ok(simple_input) => { trace!("{}", t!("configure.parameters.importingParametersFromInput")); simple_input.parameters @@ -97,8 +98,7 @@ pub fn import_parameters(parameters: &Value) -> Result, D Err(e) => { return Err(DscError::Parser(t!("configure.parameters.invalidParamsFormat", error = e).to_string())); } - }; - simple_input + } } }; Ok(parameters) diff --git a/lib/dsc-lib/src/discovery/command_discovery.rs b/lib/dsc-lib/src/discovery/command_discovery.rs index 2efa4925f..181613680 100644 --- a/lib/dsc-lib/src/discovery/command_discovery.rs +++ b/lib/dsc-lib/src/discovery/command_discovery.rs @@ -45,6 +45,7 @@ pub struct ManifestList { pub extensions: Option>, } +#[allow(clippy::large_enum_variant)] #[derive(Clone, Deserialize, JsonSchema)] #[schemars(transform = idiomaticize_externally_tagged_enum)] pub enum ImportedManifest { @@ -509,7 +510,7 @@ impl ResourceDiscovery for CommandDiscovery { for filter in required_resource_types { if let Some(required_adapter) = filter.require_adapter() { - if !adapters.contains(&required_adapter) { + if !adapters.contains(required_adapter) { return Err(DscError::AdapterNotFound(required_adapter.to_string())); } // otherwise insert at the front of the list @@ -558,12 +559,10 @@ fn filter_resources(found_resources: &mut DiscoveryResourceCache, required_resou debug!("{}", t!("discovery.commandDiscovery.foundResourceWithVersion", resource = resource.type_name, version = resource.version)); break; } - } else { - if matches_adapter_requirement(resource, filter) { - found_resources.entry(filter.resource_type().clone()).or_default().push(resource.clone()); - required_resources.insert(filter.clone(), true); - break; - } + } else if matches_adapter_requirement(resource, filter) { + found_resources.entry(filter.resource_type().clone()).or_default().push(resource.clone()); + required_resources.insert(filter.clone(), true); + break; } if required_resources.values().all(|&v| v) { return; @@ -639,7 +638,7 @@ pub fn load_manifest(path: &Path) -> Result, DscError> { debug!("{}", t!("discovery.commandDiscovery.conditionNotMet", path = path.to_string_lossy(), condition = resource.condition.unwrap_or_default(), resource = resource.type_name)); return Ok(vec![]); } - let resource = load_adapted_resource_manifest(&path, &resource)?; + let resource = load_adapted_resource_manifest(path, &resource)?; return Ok(vec![ImportedManifest::Resource(resource)]); } if DSC_RESOURCE_EXTENSIONS.iter().any(|ext| file_name_lowercase.ends_with(ext)) { @@ -711,7 +710,7 @@ pub fn load_manifest(path: &Path) -> Result, DscError> { debug!("{}", t!("discovery.commandDiscovery.conditionNotMet", path = path.to_string_lossy(), condition = resource.condition.as_ref() : {:?}, resource = resource.type_name)); continue; } - let resource = load_adapted_resource_manifest(&path, resource)?; + let resource = load_adapted_resource_manifest(path, resource)?; resources.push(ImportedManifest::Resource(resource)); } } diff --git a/lib/dsc-lib/src/discovery/discovery_trait.rs b/lib/dsc-lib/src/discovery/discovery_trait.rs index ea501bd42..561e49128 100644 --- a/lib/dsc-lib/src/discovery/discovery_trait.rs +++ b/lib/dsc-lib/src/discovery/discovery_trait.rs @@ -56,7 +56,7 @@ impl DiscoveryFilter { /// /// - `type_name` - The [`FullyQualifiedTypeName`] of the extension. /// - `require_version` - An optional [`SemanticVersionReq`] specifying the semantic version - /// requirement for the extension. + /// requirement for the extension. /// /// # Returns /// diff --git a/lib/dsc-lib/src/discovery/mod.rs b/lib/dsc-lib/src/discovery/mod.rs index 1faf04978..6446d9996 100644 --- a/lib/dsc-lib/src/discovery/mod.rs +++ b/lib/dsc-lib/src/discovery/mod.rs @@ -104,10 +104,9 @@ impl Discovery { .collect() } - #[must_use] pub fn find_resource(&mut self, filter: &DiscoveryFilter) -> Result, DscError> { if self.refresh_cache || self.resources.is_empty() { - self.find_resources(&[filter.clone()], ProgressFormat::None)?; + self.find_resources(std::slice::from_ref(filter), ProgressFormat::None)?; } let type_name = filter.resource_type(); diff --git a/lib/dsc-lib/src/dscresources/command_resource.rs b/lib/dsc-lib/src/dscresources/command_resource.rs index d43f20851..8a3b2e646 100644 --- a/lib/dsc-lib/src/dscresources/command_resource.rs +++ b/lib/dsc-lib/src/dscresources/command_resource.rs @@ -55,18 +55,14 @@ pub fn invoke_get(resource: &DscResource, filter: &str, target_resource: Option< None => resource.type_name.clone(), }; validate_security_context(&get.require_security_context, &resource_type, "get")?; - let path = if let Some(target_resource) = target_resource { - Some(target_resource.path.clone()) - } else { - None - }; + let path = target_resource.map(|target_resource| target_resource.path.clone()); let command_resource_info = CommandResourceInfo { type_name: resource_type.clone(), path, }; let args = process_get_args(get.args.as_ref(), filter, &command_resource_info); if !filter.is_empty() { - verify_json_from_manifest(&resource, filter, target_resource)?; + verify_json_from_manifest(resource, filter, target_resource)?; command_input = get_command_input(get.input.as_ref(), filter)?; } @@ -74,7 +70,7 @@ pub fn invoke_get(resource: &DscResource, filter: &str, target_resource: Option< let (_exit_code, stdout, stderr) = invoke_command(&get.executable, args, command_input.stdin.as_deref(), Some(&resource.directory), command_input.env, manifest.exit_codes.as_ref())?; if resource.kind == Kind::Resource { debug!("{}", t!("dscresources.commandResource.verifyOutputUsing", resource = &resource.type_name, executable = &get.executable)); - verify_json_from_manifest(&resource, &stdout, target_resource)?; + verify_json_from_manifest(resource, &stdout, target_resource)?; } let result: GetResult = if let Ok(group_response) = serde_json::from_str::>(&stdout) { @@ -118,11 +114,7 @@ pub fn invoke_set(resource: &DscResource, desired: &str, skip_test: bool, execut Some(r) => r.type_name.clone(), None => resource.type_name.clone(), }; - let path = if let Some(target_resource) = target_resource { - Some(target_resource.path.clone()) - } else { - None - }; + let path = target_resource.map(|target_resource| target_resource.path.clone()); let command_resource_info = CommandResourceInfo { type_name: resource_type.clone(), path, @@ -136,22 +128,24 @@ pub fn invoke_set(resource: &DscResource, desired: &str, skip_test: bool, execut ExecutionKind::WhatIf => { operation_type = "whatif".to_string(); // Check if set supports native what-if - let has_native_whatif = manifest.set.as_ref() - .map_or(false, |set| { - let (_, supports_whatif) = process_set_delete_args(set.args.as_ref(), "", &command_resource_info, execution_type); - supports_whatif - }); + let has_native_whatif = manifest.set.as_ref().is_some_and(|set| { + let (_, supports_whatif) = process_set_delete_args( + set.args.as_ref(), + "", + &command_resource_info, + execution_type + ); + supports_whatif + }); if has_native_whatif { &manifest.set + } else if manifest.what_if.is_some() { + warn!("{}", t!("dscresources.commandResource.whatIfWarning", resource = &resource_type)); + &manifest.what_if } else { - if manifest.what_if.is_some() { - warn!("{}", t!("dscresources.commandResource.whatIfWarning", resource = &resource_type)); - &manifest.what_if - } else { - is_synthetic_what_if = true; - &manifest.set - } + is_synthetic_what_if = true; + &manifest.set } } }; @@ -159,7 +153,7 @@ pub fn invoke_set(resource: &DscResource, desired: &str, skip_test: bool, execut return Err(DscError::NotImplemented("set".to_string())); }; validate_security_context(&set.require_security_context, &resource_type, "set")?; - verify_json_from_manifest(&resource, desired, target_resource)?; + verify_json_from_manifest(resource, desired, target_resource)?; // if resource doesn't implement a pre-test, we execute test first to see if a set is needed if !skip_test && set.pre_test != Some(true) { @@ -199,16 +193,12 @@ pub fn invoke_set(resource: &DscResource, desired: &str, skip_test: bool, execut let Some(get) = &manifest.get else { return Err(DscError::NotImplemented("get".to_string())); }; - let resource_type = match target_resource.clone() { + let resource_type = match target_resource { Some(r) => r.type_name.clone(), None => resource.type_name.clone(), }; validate_security_context(&get.require_security_context, &resource_type, "get")?; - let path = if let Some(target_resource) = target_resource { - Some(target_resource.path.clone()) - } else { - None - }; + let path = target_resource.map(|target_resource| target_resource.path.clone()); let command_resource_info = CommandResourceInfo { type_name: resource_type.clone(), path, @@ -221,7 +211,7 @@ pub fn invoke_set(resource: &DscResource, desired: &str, skip_test: bool, execut if resource.kind == Kind::Resource { debug!("{}", t!("dscresources.commandResource.setVerifyGet", resource = &resource.type_name, executable = &get.executable)); - verify_json_from_manifest(&resource, &stdout, target_resource)?; + verify_json_from_manifest(resource, &stdout, target_resource)?; } let pre_state_value: Value = if exit_code == 0 { @@ -271,7 +261,7 @@ pub fn invoke_set(resource: &DscResource, desired: &str, skip_test: bool, execut if resource.kind == Kind::Resource { debug!("{}", t!("dscresources.commandResource.setVerifyOutput", operation = operation_type, resource = &resource.type_name, executable = &set.executable)); - verify_json_from_manifest(&resource, &stdout, target_resource)?; + verify_json_from_manifest(resource, &stdout, target_resource)?; } let actual_value: Value = match serde_json::from_str(&stdout){ @@ -299,7 +289,7 @@ pub fn invoke_set(resource: &DscResource, desired: &str, skip_test: bool, execut if resource.kind == Kind::Resource { debug!("{}", t!("dscresources.commandResource.setVerifyOutput", operation = operation_type, resource = &resource.type_name, executable = &set.executable)); - verify_json_from_manifest(&resource, actual_line, target_resource)?; + verify_json_from_manifest(resource, actual_line, target_resource)?; } let actual_value: Value = serde_json::from_str(actual_line)?; @@ -361,18 +351,14 @@ pub fn invoke_test(resource: &DscResource, expected: &str, target_resource: Opti return invoke_synthetic_test(resource, expected, target_resource); }; - verify_json_from_manifest(&resource, expected, target_resource)?; + verify_json_from_manifest(resource, expected, target_resource)?; - let resource_type = match target_resource.clone() { + let resource_type = match target_resource { Some(r) => r.type_name.clone(), None => resource.type_name.clone(), }; validate_security_context(&test.require_security_context, &resource_type, "test")?; - let path = if let Some(target_resource) = target_resource { - Some(target_resource.path.clone()) - } else { - None - }; + let path = target_resource.map(|target_resource| target_resource.path.clone()); let command_resource_info = CommandResourceInfo { type_name: resource_type.clone(), path, @@ -394,7 +380,7 @@ pub fn invoke_test(resource: &DscResource, expected: &str, target_resource: Opti Some(ReturnKind::State) => { if resource.kind == Kind::Resource { debug!("{}", t!("dscresources.commandResource.testVerifyOutput", resource = &resource.type_name, executable = &test.executable)); - verify_json_from_manifest(&resource, &stdout, target_resource)?; + verify_json_from_manifest(resource, &stdout, target_resource)?; } let actual_value: Value = match serde_json::from_str(&stdout){ @@ -422,7 +408,7 @@ pub fn invoke_test(resource: &DscResource, expected: &str, target_resource: Opti if resource.kind == Kind::Resource { debug!("{}", t!("dscresources.commandResource.testVerifyOutput", resource = &resource.type_name, executable = &test.executable)); - verify_json_from_manifest(&resource, actual_value, target_resource)?; + verify_json_from_manifest(resource, actual_value, target_resource)?; } let actual_value: Value = serde_json::from_str(actual_value)?; @@ -522,18 +508,14 @@ pub fn invoke_delete(resource: &DscResource, filter: &str, target_resource: Opti return Err(DscError::NotImplemented("delete".to_string())); }; - verify_json_from_manifest(&resource, filter, target_resource)?; + verify_json_from_manifest(resource, filter, target_resource)?; let resource_type = match target_resource { Some(r) => r.type_name.clone(), None => resource.type_name.clone(), }; validate_security_context(&delete.require_security_context, &resource_type, "delete")?; - let path = if let Some(target_resource) = target_resource { - Some(target_resource.path.clone()) - } else { - None - }; + let path = target_resource.map(|target_resource| target_resource.path.clone()); let command_resource_info = CommandResourceInfo { type_name: resource_type.clone(), path, @@ -541,7 +523,7 @@ pub fn invoke_delete(resource: &DscResource, filter: &str, target_resource: Opti let (args, supports_whatif) = process_set_delete_args(delete.args.as_ref(), filter, &command_resource_info, execution_type); if execution_type == &ExecutionKind::WhatIf && !supports_whatif { // perform a synthetic what-if by calling test and wrapping the TestResult in DeleteResultKind::SyntheticWhatIf - let test_result = invoke_test(resource, filter, target_resource.clone())?; + let test_result = invoke_test(resource, filter, target_resource)?; return Ok(DeleteResultKind::SyntheticWhatIf(test_result)); } let command_input = get_command_input(delete.input.as_ref(), filter)?; @@ -586,11 +568,7 @@ pub fn invoke_validate(resource: &DscResource, config: &str, target_resource: Op Some(r) => r.type_name.clone(), None => resource.type_name.clone(), }; - let path = if let Some(target_resource) = target_resource { - Some(target_resource.path.clone()) - } else { - None - }; + let path = target_resource.map(|target_resource| target_resource.path.clone()); let command_resource_info = CommandResourceInfo { type_name: resource_type.clone(), path, @@ -688,18 +666,14 @@ pub fn invoke_export(resource: &DscResource, input: Option<&str>, target_resourc None => resource.type_name.clone(), }; validate_security_context(&export.require_security_context, &resource_type, "export")?; - let path = if let Some(target_resource) = target_resource { - Some(target_resource.path.clone()) - } else { - None - }; + let path = target_resource.map(|target_resource| target_resource.path.clone()); let command_resource_info = CommandResourceInfo { type_name: resource_type.clone(), path, }; if let Some(input) = input { if !input.is_empty() { - verify_json_from_manifest(&resource, input, target_resource)?; + verify_json_from_manifest(resource, input, target_resource)?; command_input = get_command_input(export.input.as_ref(), input)?; } @@ -721,7 +695,7 @@ pub fn invoke_export(resource: &DscResource, input: Option<&str>, target_resourc }; if resource.kind == Kind::Resource { debug!("{}", t!("dscresources.commandResource.exportVerifyOutput", resource = &resource.type_name, executable = &export.executable)); - verify_json_from_manifest(&resource, line, target_resource)?; + verify_json_from_manifest(resource, line, target_resource)?; } instances.push(instance); } @@ -876,15 +850,14 @@ async fn run_process_async(executable: &str, args: Option>, input: O if code != 0 { // Only use manifest-provided exit code mappings when the map is not empty/default, // so that default mappings do not suppress stderr-based diagnostics. - if !exit_codes.is_empty_or_default() { - if let Some(error_message) = exit_codes.get_code(code) { + if !exit_codes.is_empty_or_default() + && let Some(error_message) = exit_codes.get_code(code) { return Err(DscError::CommandExitFromManifest( executable.to_string(), code, error_message.clone() )); } - } return Err(DscError::Command(executable.to_string(), code, stderr_result)); } diff --git a/lib/dsc-lib/src/dscresources/dscresource.rs b/lib/dsc-lib/src/dscresources/dscresource.rs index b748090bf..ef2eacc78 100644 --- a/lib/dsc-lib/src/dscresources/dscresource.rs +++ b/lib/dsc-lib/src/dscresources/dscresource.rs @@ -287,7 +287,10 @@ impl DscResource { return adapter.schema(); } - return Err(DscError::NotSupported(t!("dscresources.dscresource.invokeSchemaNotSupported", resource = self.type_name).to_string())); + Err(DscError::NotSupported(t!( + "dscresources.dscresource.invokeSchemaNotSupported", + resource = self.type_name + ).to_string())) } fn get_adapter_resource(configurator: &mut Configurator, adapter: &FullyQualifiedTypeName) -> Result { @@ -399,12 +402,12 @@ impl Invoke for DscResource { warn!("{}", t!("dscresources.dscresource.deprecationMessage", resource = self.type_name, message = deprecation_message)); } if let Some(adapter) = &self.require_adapter { - return self.invoke_get_with_adapter(adapter, &self, filter); + return self.invoke_get_with_adapter(adapter, self, filter); } match &self.implemented_as { Some(ImplementedAs::Command) => { - command_resource::invoke_get(&self, filter, self.target_resource.as_deref()) + command_resource::invoke_get(self, filter, self.target_resource.as_deref()) }, _ => { Err(DscError::NotImplemented(t!("dscresources.dscresource.customResourceNotSupported").to_string())) @@ -418,12 +421,12 @@ impl Invoke for DscResource { warn!("{}", t!("dscresources.dscresource.deprecationMessage", resource = self.type_name, message = deprecation_message)); } if let Some(adapter) = &self.require_adapter { - return self.invoke_set_with_adapter(adapter, &self, desired, skip_test, execution_type); + return self.invoke_set_with_adapter(adapter, self, desired, skip_test, execution_type); } match &self.implemented_as { Some(ImplementedAs::Command) => { - command_resource::invoke_set(&self, desired, skip_test, execution_type, self.target_resource.as_deref()) + command_resource::invoke_set(self, desired, skip_test, execution_type, self.target_resource.as_deref()) }, _ => { Err(DscError::NotImplemented(t!("dscresources.dscresource.customResourceNotSupported").to_string())) @@ -437,7 +440,7 @@ impl Invoke for DscResource { warn!("{}", t!("dscresources.dscresource.deprecationMessage", resource = self.type_name, message = deprecation_message)); } if let Some(adapter) = &self.require_adapter { - return self.invoke_test_with_adapter(adapter, &self, expected); + return self.invoke_test_with_adapter(adapter, self, expected); } match &self.implemented_as { @@ -473,7 +476,7 @@ impl Invoke for DscResource { Ok(test_result) } else { - command_resource::invoke_test(&self, expected, self.target_resource.as_deref()) + command_resource::invoke_test(self, expected, self.target_resource.as_deref()) } }, _ => { @@ -488,12 +491,12 @@ impl Invoke for DscResource { warn!("{}", t!("dscresources.dscresource.deprecationMessage", resource = self.type_name, message = deprecation_message)); } if let Some(adapter) = &self.require_adapter { - return self.invoke_delete_with_adapter(adapter, &self, filter, execution_type); + return self.invoke_delete_with_adapter(adapter, self, filter, execution_type); } match &self.implemented_as { Some(ImplementedAs::Command) => { - command_resource::invoke_delete(&self, filter, self.target_resource.as_deref(), execution_type) + command_resource::invoke_delete(self, filter, self.target_resource.as_deref(), execution_type) }, _ => { Err(DscError::NotImplemented(t!("dscresources.dscresource.customResourceNotSupported").to_string())) @@ -512,7 +515,7 @@ impl Invoke for DscResource { match &self.implemented_as { Some(ImplementedAs::Command) => { - command_resource::invoke_validate(&self, config, self.target_resource.as_deref()) + command_resource::invoke_validate(self, config, self.target_resource.as_deref()) }, _ => { Err(DscError::NotImplemented(t!("dscresources.dscresource.customResourceNotSupported").to_string())) @@ -529,12 +532,12 @@ impl Invoke for DscResource { return Ok(serde_json::to_string(schema)?); } if let Some(adapter) = &self.require_adapter { - return self.invoke_schema_with_adapter(adapter, &self); + return self.invoke_schema_with_adapter(adapter, self); } match &self.implemented_as { Some(ImplementedAs::Command) => { - command_resource::get_schema(&self, self.target_resource.as_deref()) + command_resource::get_schema(self, self.target_resource.as_deref()) }, _ => { Err(DscError::NotImplemented(t!("dscresources.dscresource.customResourceNotSupported").to_string())) @@ -548,10 +551,10 @@ impl Invoke for DscResource { warn!("{}", t!("dscresources.dscresource.deprecationMessage", resource = self.type_name, message = deprecation_message)); } if let Some(adapter) = &self.require_adapter { - return self.invoke_export_with_adapter(adapter, &self, input); + return self.invoke_export_with_adapter(adapter, self, input); } - command_resource::invoke_export(&self, Some(input), self.target_resource.as_deref()) + command_resource::invoke_export(self, Some(input), self.target_resource.as_deref()) } fn resolve(&self, input: &str) -> Result { @@ -563,7 +566,7 @@ impl Invoke for DscResource { return Err(DscError::NotSupported(t!("dscresources.dscresource.invokeResolveNotSupported", resource = self.type_name).to_string())); } - command_resource::invoke_resolve(&self, input) + command_resource::invoke_resolve(self, input) } } @@ -615,10 +618,8 @@ pub fn redact(value: &Value) -> Value { /// # Errors /// * `DscError` - The adapter manifest is not found or invalid pub fn get_adapter_input_kind(adapter: &DscResource) -> Result { - if let Some(manifest) = &adapter.manifest { - if let Some(adapter_operation) = &manifest.adapter { - return Ok(adapter_operation.input_kind.clone()); - } + if let Some(manifest) = &adapter.manifest && let Some(adapter_operation) = &manifest.adapter { + return Ok(adapter_operation.input_kind.clone()); } Err(DscError::Operation(t!("dscresources.dscresource.adapterManifestNotFound", adapter = adapter.type_name).to_string())) } diff --git a/lib/dsc-lib/src/functions/contains.rs b/lib/dsc-lib/src/functions/contains.rs index 4604bd676..df0871f9c 100644 --- a/lib/dsc-lib/src/functions/contains.rs +++ b/lib/dsc-lib/src/functions/contains.rs @@ -44,19 +44,15 @@ impl Function for Contains { if let Some(array) = args[0].as_array() { for item in array { if let Some(item_str) = item.as_str() { - if let Some(string) = &string_to_find { - if item_str == string { - found = true; - break; - } - } - } else if let Some(item_num) = item.as_i64() { - if let Some(number) = number_to_find { - if item_num == number { - found = true; - break; - } + if let Some(string) = &string_to_find && item_str == string { + found = true; + break; } + } else if let Some(item_num) = item.as_i64() + && let Some(number) = number_to_find + && item_num == number { + found = true; + break; } } return Ok(Value::Bool(found)); @@ -71,11 +67,9 @@ impl Function for Contains { found = true; break; } - } else if let Some(number) = number_to_find { - if key == &number.to_string() { - found = true; - break; - } + } else if let Some(number) = number_to_find && key == &number.to_string() { + found = true; + break; } } return Ok(Value::Bool(found)); diff --git a/lib/dsc-lib/src/functions/data_uri_to_string.rs b/lib/dsc-lib/src/functions/data_uri_to_string.rs index 3533bbdf4..7cc480a87 100644 --- a/lib/dsc-lib/src/functions/data_uri_to_string.rs +++ b/lib/dsc-lib/src/functions/data_uri_to_string.rs @@ -75,14 +75,14 @@ impl Function for DataUriToString { for part in &parts[1..] { if *part == "base64" { has_base64 = true; - } else if part.starts_with("charset=") { + } else if let Some(charset_from_part) = part.strip_prefix("charset=") { if charset.is_some() { return Err(DscError::FunctionArg( "dataUriToString".to_string(), t!("functions.dataUriToString.invalidDataUri").to_string(), )); } - charset = Some(&part[8..]); + charset = Some(charset_from_part); } else { return Err(DscError::FunctionArg( "dataUriToString".to_string(), diff --git a/lib/dsc-lib/src/lib.rs b/lib/dsc-lib/src/lib.rs index 5dce75744..aa7299a15 100644 --- a/lib/dsc-lib/src/lib.rs +++ b/lib/dsc-lib/src/lib.rs @@ -51,7 +51,6 @@ impl DscManager { /// /// * `name` - The name of the resource to find, can have wildcards. /// - #[must_use] pub fn find_resource(&mut self, filter: &DiscoveryFilter) -> Result, DscError> { self.discovery.find_resource(filter) } diff --git a/lib/dsc-lib/src/types/date_version.rs b/lib/dsc-lib/src/types/date_version.rs index 458d76fe0..70652e347 100644 --- a/lib/dsc-lib/src/types/date_version.rs +++ b/lib/dsc-lib/src/types/date_version.rs @@ -6,6 +6,7 @@ use std::{ fmt::Display, + hash::Hash, str::FromStr, sync::OnceLock, }; @@ -42,7 +43,7 @@ use crate::schemas::dsc_repo::DscRepoSchema; /// /// If the date version is for a prerelease, the prerelease segment must be a string of ASCII /// alphabetic characters (`[a-zA-Z]`). -#[derive(Debug, Clone, Hash, Serialize, Deserialize, DscRepoSchema)] +#[derive(Debug, Clone, Serialize, Deserialize, DscRepoSchema)] #[serde(try_from = "String", into = "String")] #[dsc_repo_schema(base_name = "dateVersion", folder_path = "definitions")] pub struct DateVersion(NaiveDate, Option); @@ -451,7 +452,7 @@ impl DateVersion { }; let mut errors: Vec = vec![]; - if year > 9999 || year < 1000 { + if !(1000..=9999).contains(&year) { errors.push(DateVersionError::InvalidYear { year }); } @@ -526,46 +527,39 @@ impl Datelike for DateVersion { self.as_ref().iso_week() } fn with_year(&self, year: i32) -> Option { - match self.as_ref().with_year(year) { - None => None, - Some(new_date) => Some(Self(new_date, self.1.clone())) - } + self.as_ref() + .with_year(year) + .map(|new_date| Self(new_date, self.1.clone())) } fn with_month(&self, month: u32) -> Option { - match self.as_ref().with_month(month) { - None => None, - Some(new_date) => Some(Self(new_date, self.1.clone())) - } + self.as_ref() + .with_month(month) + .map(|new_date| Self(new_date, self.1.clone())) } fn with_month0(&self, month0: u32) -> Option { - match self.as_ref().with_month0(month0) { - None => None, - Some(new_date) => Some(Self(new_date, self.1.clone())) - } + self.as_ref() + .with_month0(month0) + .map(|new_date| Self(new_date, self.1.clone())) } fn with_day(&self, day: u32) -> Option { - match self.as_ref().with_day(day) { - None => None, - Some(new_date) => Some(Self(new_date, self.1.clone())) - } + self.as_ref() + .with_day(day) + .map(|new_date| Self(new_date, self.1.clone())) } fn with_day0(&self, day0: u32) -> Option { - match self.as_ref().with_day0(day0) { - None => None, - Some(new_date) => Some(Self(new_date, self.1.clone())) - } + self.as_ref() + .with_day0(day0) + .map(|new_date| Self(new_date, self.1.clone())) } fn with_ordinal(&self, ordinal: u32) -> Option { - match self.as_ref().with_ordinal(ordinal) { - None => None, - Some(new_date) => Some(Self(new_date, self.1.clone())) - } + self.as_ref() + .with_ordinal(ordinal) + .map(|new_date| Self(new_date, self.1.clone())) } fn with_ordinal0(&self, ordinal0: u32) -> Option { - match self.as_ref().with_ordinal0(ordinal0) { - None => None, - Some(new_date) => Some(Self(new_date, self.1.clone())) - } + self.as_ref() + .with_ordinal0(ordinal0) + .map(|new_date| Self(new_date, self.1.clone())) } } @@ -628,6 +622,13 @@ impl From for NaiveDate { } } +impl Hash for DateVersion { + fn hash(&self, state: &mut H) { + self.0.hash(state); + self.1.hash(state); + } +} + impl Eq for DateVersion {} impl PartialEq for DateVersion { @@ -697,7 +698,7 @@ impl PartialEq for str { impl PartialEq<&str> for DateVersion { fn eq(&self, other: &&str) -> bool { - match Self::parse(*other) { + match Self::parse(other) { Ok(other_version) => self.eq(&other_version), Err(_) => false, } @@ -706,7 +707,7 @@ impl PartialEq<&str> for DateVersion { impl PartialEq for &str { fn eq(&self, other: &DateVersion) -> bool { - match DateVersion::parse(*self) { + match DateVersion::parse(self) { Ok(version) => version.eq(other), Err(_) => false } diff --git a/lib/dsc-lib/src/types/exit_code.rs b/lib/dsc-lib/src/types/exit_code.rs index 6f7b82951..f7c18ff07 100644 --- a/lib/dsc-lib/src/types/exit_code.rs +++ b/lib/dsc-lib/src/types/exit_code.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use std::{borrow::Borrow, fmt::Display, ops::Deref, str::FromStr}; +use std::{borrow::Borrow, fmt::Display, hash::Hash, ops::Deref, str::FromStr}; use serde::{Deserialize, Serialize}; @@ -12,7 +12,7 @@ use crate::dscerror::DscError; /// DSC uses exit codes to determine whether invoked commands, including resource and extension /// operations, are successful. DSC treats exit code `0` as successful and all other exit codes /// as indicating a failure. -#[derive(Debug, Copy, Clone, Hash, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, Eq, PartialOrd, Ord, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] pub struct ExitCode(i32); @@ -119,7 +119,11 @@ impl From for i32 { value.0 } } - +impl Hash for ExitCode { + fn hash(&self, state: &mut H) { + self.0.hash(state); + } +} impl PartialEq for ExitCode { fn eq(&self, other: &Self) -> bool { self.0.eq(&other.0) diff --git a/lib/dsc-lib/src/types/exit_codes_map.rs b/lib/dsc-lib/src/types/exit_codes_map.rs index 613dee40d..7bc915813 100644 --- a/lib/dsc-lib/src/types/exit_codes_map.rs +++ b/lib/dsc-lib/src/types/exit_codes_map.rs @@ -25,7 +25,7 @@ pub struct ExitCodesMap(HashMap); /// Defines the default map as a private static to use with the `get_code_or_default` method, /// minimizing the performance hit compared to reconstructing the default map on every method /// invocation. -static DEFAULT_MAP: LazyLock = LazyLock::new(|| ExitCodesMap::default()); +static DEFAULT_MAP: LazyLock = LazyLock::new(ExitCodesMap::default); impl ExitCodesMap { /// Defines the regular expression for validating a string as an exit code. @@ -65,15 +65,15 @@ impl ExitCodesMap { match self.0.get(&ExitCode::new(code)) { Some(description) => description.clone(), None => match code { - 0 => (&*DEFAULT_MAP).get_code(0).expect("default always defines exit code 0").clone(), - _ => (&*DEFAULT_MAP).get_code(1).expect("default always defines exit code 1").clone(), + 0 => DEFAULT_MAP.get_code(0).expect("default always defines exit code 0").clone(), + _ => DEFAULT_MAP.get_code(1).expect("default always defines exit code 1").clone(), } } } /// Indicates whether the [`ExitCodesMap`] is identical to the default map. pub fn is_default(&self) -> bool { - self == &*DEFAULT_MAP + *self == *DEFAULT_MAP } /// Indicates whether the [`ExitCodesMap`] is empty or identical to the default map. @@ -132,7 +132,7 @@ impl JsonSchema for ExitCodesMap { impl AsRef for ExitCodesMap { fn as_ref(&self) -> &ExitCodesMap { - &self + self } } diff --git a/lib/dsc-lib/src/types/fully_qualified_type_name.rs b/lib/dsc-lib/src/types/fully_qualified_type_name.rs index 6e73a7f36..4c4829c2d 100644 --- a/lib/dsc-lib/src/types/fully_qualified_type_name.rs +++ b/lib/dsc-lib/src/types/fully_qualified_type_name.rs @@ -117,6 +117,7 @@ use crate::schemas::dsc_repo::DscRepoSchema; #[derive( Clone, Debug, + Default, Eq, Serialize, Deserialize, @@ -409,7 +410,7 @@ impl FullyQualifiedTypeName { let validating_segment_regex = Self::init_validating_segment_regex(); let (_owner, _namespaces, name) = Self::parse_segments( text, - &validating_segment_regex, + validating_segment_regex, errors ); if errors.len() == 1 && errors[0] == FullyQualifiedTypeNameError::EmptyTypeName { @@ -568,14 +569,6 @@ impl FullyQualifiedTypeName { } } -// While it's technically never valid for a _defined_ FQTN to be empty, we need the default -// implementation for creating empty instances of various structs to then populate/modify. -impl Default for FullyQualifiedTypeName { - fn default() -> Self { - Self(String::new()) - } -} - // We implement `PartialEq` by hand for various types because FQTNs should be compared // case insensitively. This obviates the need to `.to_string().to_lowercase()` for comparisons. impl PartialEq for FullyQualifiedTypeName { diff --git a/lib/dsc-lib/src/types/resource_version.rs b/lib/dsc-lib/src/types/resource_version.rs index daa2b8842..5da031ce5 100644 --- a/lib/dsc-lib/src/types/resource_version.rs +++ b/lib/dsc-lib/src/types/resource_version.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use std::{fmt::Display, str::FromStr, sync::OnceLock}; +use std::{fmt::Display, hash::Hash, str::FromStr, sync::OnceLock}; use miette::Diagnostic; use regex::Regex; @@ -156,7 +156,7 @@ use crate::{ /// /// [01]: https://doc.rust-lang.org/std/cmp/trait.Ord.html#lexicographical-comparison /// [02]: https://www.iso.org/iso-8601-date-and-time-format.html -#[derive(Debug, Clone, Hash, Eq, Serialize, Deserialize, JsonSchema, DscRepoSchema)] +#[derive(Debug, Clone, Eq, Serialize, Deserialize, JsonSchema, DscRepoSchema)] #[dsc_repo_schema(base_name = "resourceVersion", folder_path = "definitions")] #[serde(untagged, try_from = "String", into = "String")] #[schemars(!try_from, !into)] @@ -366,10 +366,7 @@ impl ResourceVersion { /// assert_eq!(date.is_semver(), false); /// ``` pub fn is_semver(&self) -> bool { - match self { - Self::Semantic(_) => true, - _ => false, - } + matches!(self, Self::Semantic(_)) } /// Indicates whether the resource version is a date version. @@ -386,10 +383,7 @@ impl ResourceVersion { /// assert_eq!(date.is_date_version(), true); /// ``` pub fn is_date_version(&self) -> bool { - match self { - Self::Date(_) => true, - _ => false, - } + matches!(self, Self::Date(_)) } /// Returns the version as a reference to the underlying [`SemanticVersion`] if possible. @@ -679,6 +673,15 @@ impl TryFrom for DateVersion { } } +impl Hash for ResourceVersion { + fn hash(&self, state: &mut H) { + match self { + Self::Semantic(v) => v.hash(state), + Self::Date(v) => v.hash(state), + } + } +} + // Implement traits for comparing `ResourceVersion` to strings, semantic versions, and date // versions bi-directionally. impl PartialEq for ResourceVersion { @@ -734,7 +737,7 @@ impl PartialEq for DateVersion { impl PartialEq<&str> for ResourceVersion { fn eq(&self, other: &&str) -> bool { - if let Ok(other_version) = Self::parse(*other) { + if let Ok(other_version) = Self::parse(other) { self == &other_version } else { false @@ -744,7 +747,7 @@ impl PartialEq<&str> for ResourceVersion { impl PartialEq for &str { fn eq(&self, other: &ResourceVersion) -> bool { - if let Ok(version) = ResourceVersion::parse(*self) { + if let Ok(version) = ResourceVersion::parse(self) { &version == other } else { false @@ -869,7 +872,7 @@ impl PartialOrd for String { impl PartialOrd<&str> for ResourceVersion { fn partial_cmp(&self, other: &&str) -> Option { - match ResourceVersion::parse(*other) { + match ResourceVersion::parse(other) { Ok(other_version) => self.partial_cmp(&other_version), Err(_) => None, } @@ -887,7 +890,7 @@ impl PartialOrd for ResourceVersion { impl PartialOrd for &str { fn partial_cmp(&self, other: &ResourceVersion) -> Option { - match ResourceVersion::parse(*self) { + match ResourceVersion::parse(self) { Ok(version) => version.partial_cmp(other), Err(_) => None, } diff --git a/lib/dsc-lib/src/types/resource_version_req.rs b/lib/dsc-lib/src/types/resource_version_req.rs index 5041e6b29..f6f6c89df 100644 --- a/lib/dsc-lib/src/types/resource_version_req.rs +++ b/lib/dsc-lib/src/types/resource_version_req.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use std::{fmt::Display, str::FromStr, sync::OnceLock}; +use std::{fmt::Display, hash::Hash, str::FromStr, sync::OnceLock}; use miette::Diagnostic; use regex::Regex; @@ -78,7 +78,7 @@ use crate::{ /// ``` /// /// [01]: SemanticVersionReq -#[derive(Debug, Clone, Hash, Eq, Serialize, Deserialize, JsonSchema, DscRepoSchema)] +#[derive(Debug, Clone, Eq, Serialize, Deserialize, JsonSchema, DscRepoSchema)] #[dsc_repo_schema(base_name = "resourceVersionReq", folder_path = "definitions")] #[serde(untagged, try_from = "String", into = "String")] #[schemars(!try_from, !into)] @@ -297,10 +297,7 @@ impl ResourceVersionReq { /// assert_eq!(date.is_semver(), false); /// ``` pub fn is_semver(&self) -> bool { - match self { - Self::Semantic(_) => true, - _ => false, - } + matches!(self, Self::Semantic(_)) } /// Indicates whether the resource version requirement is for a specific [`DateVersion`]. @@ -317,10 +314,7 @@ impl ResourceVersionReq { /// assert_eq!(semantic.is_date_version(), false); /// ``` pub fn is_date_version(&self) -> bool { - match self { - Self::Date(_) => true, - _ => false, - } + matches!(self, Self::Date(_)) } /// Returns the requirement as a reference to the underlying [`SemanticVersionReq`] if possible. @@ -583,6 +577,15 @@ impl TryFrom for DateVersion { } } +impl Hash for ResourceVersionReq { + fn hash(&self, state: &mut H) { + match self { + Self::Semantic(req) => req.hash(state), + Self::Date(req) => req.hash(state), + } + } +} + // Implement traits for comparing `ResourceVersionReq` to strings and semantic version requirements // bi-directionally. impl PartialEq for ResourceVersionReq { @@ -638,7 +641,7 @@ impl PartialEq for DateVersion { impl PartialEq<&str> for ResourceVersionReq { fn eq(&self, other: &&str) -> bool { - match Self::parse(*other) { + match Self::parse(other) { Ok(other_req) => self == &other_req, Err(_) => false, } @@ -647,7 +650,7 @@ impl PartialEq<&str> for ResourceVersionReq { impl PartialEq for &str { fn eq(&self, other: &ResourceVersionReq) -> bool { - match ResourceVersionReq::parse(*self) { + match ResourceVersionReq::parse(self) { Ok(req) => &req == other, Err(_) => false, } diff --git a/lib/dsc-lib/src/types/semantic_version.rs b/lib/dsc-lib/src/types/semantic_version.rs index dfee27fbc..709ade8fc 100644 --- a/lib/dsc-lib/src/types/semantic_version.rs +++ b/lib/dsc-lib/src/types/semantic_version.rs @@ -292,7 +292,7 @@ use crate::schemas::dsc_repo::DscRepoSchema; /// /// [01]: https://semver.org /// [`SemanticVersionReq`]: crate::types::SemanticVersionReq -#[derive(Debug, Clone, Hash, Eq, Serialize, Deserialize, DscRepoSchema)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, DscRepoSchema)] #[dsc_repo_schema(base_name = "semver", folder_path = "definitions")] pub struct SemanticVersion(semver::Version); @@ -549,11 +549,11 @@ impl Deref for SemanticVersion { } // Comparison traits -impl PartialEq for SemanticVersion { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} +// impl PartialEq for SemanticVersion { +// fn eq(&self, other: &Self) -> bool { +// self.0 == other.0 +// } +// } impl PartialEq for SemanticVersion { fn eq(&self, other: &semver::Version) -> bool { @@ -605,7 +605,7 @@ impl PartialEq for str { impl PartialEq<&str> for SemanticVersion { fn eq(&self, other: &&str) -> bool { - match Self::parse(*other) { + match Self::parse(other) { Ok(other_version) => self.eq(&other_version), Err(_) => false, } @@ -614,7 +614,7 @@ impl PartialEq<&str> for SemanticVersion { impl PartialEq for &str { fn eq(&self, other: &SemanticVersion) -> bool { - match SemanticVersion::parse(*self) { + match SemanticVersion::parse(self) { Ok(version) => version.eq(other), Err(_) => false, } @@ -623,7 +623,7 @@ impl PartialEq for &str { impl PartialOrd for SemanticVersion { fn partial_cmp(&self, other: &Self) -> Option { - self.0.partial_cmp(&other.0) + Some(self.cmp(other)) } } diff --git a/lib/dsc-lib/src/types/semantic_version_req.rs b/lib/dsc-lib/src/types/semantic_version_req.rs index c249b2e20..d1ce0bec8 100644 --- a/lib/dsc-lib/src/types/semantic_version_req.rs +++ b/lib/dsc-lib/src/types/semantic_version_req.rs @@ -400,7 +400,7 @@ use crate::{schemas::dsc_repo::DscRepoSchema, types::SemanticVersion}; /// /// [01]: https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#version-requirement-syntax /// [`ComparatorIncludesForbiddenBuildMetadata`]: SemanticVersionReqError::ComparatorIncludesForbiddenBuildMetadata -#[derive(Debug, Clone, Hash, Eq, Serialize, Deserialize, DscRepoSchema)] +#[derive(Debug, Clone, Hash, PartialEq, Eq, Default, Serialize, Deserialize, DscRepoSchema)] #[dsc_repo_schema(base_name = "semverRequirement", folder_path = "definitions")] pub struct SemanticVersionReq(semver::VersionReq); @@ -983,12 +983,6 @@ impl JsonSchema for SemanticVersionReq { } } -impl Default for SemanticVersionReq { - fn default() -> Self { - Self(semver::VersionReq::default()) - } -} - impl Display for SemanticVersionReq { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) @@ -1063,11 +1057,11 @@ impl Deref for SemanticVersionReq { } // Comparison traits -impl PartialEq for SemanticVersionReq { - fn eq(&self, other: &Self) -> bool { - self.0 == other.0 - } -} +// impl PartialEq for SemanticVersionReq { +// fn eq(&self, other: &Self) -> bool { +// self.0 == other.0 +// } +// } impl PartialEq for SemanticVersionReq { fn eq(&self, other: &semver::VersionReq) -> bool { @@ -1137,7 +1131,7 @@ impl PartialEq for str { impl PartialEq<&str> for SemanticVersionReq { fn eq(&self, other: &&str) -> bool { - match Self::parse(*other) { + match Self::parse(other) { Ok(o) => self == &o, Err(_) => false } @@ -1146,7 +1140,7 @@ impl PartialEq<&str> for SemanticVersionReq { impl PartialEq for &str { fn eq(&self, other: &SemanticVersionReq) -> bool { - match SemanticVersionReq::parse(*self) { + match SemanticVersionReq::parse(self) { Ok(s) => &s == other, Err(_) => false } diff --git a/lib/dsc-lib/src/types/wildcard_type_name.rs b/lib/dsc-lib/src/types/wildcard_type_name.rs index 98ce261dd..d1e5ed85c 100644 --- a/lib/dsc-lib/src/types/wildcard_type_name.rs +++ b/lib/dsc-lib/src/types/wildcard_type_name.rs @@ -472,10 +472,10 @@ impl WildcardTypeName { if errors.is_empty() { Ok(Self { text: text.to_string(), regex }) } else { - return Err(WildcardTypeNameError::InvalidTypeName { + Err(WildcardTypeNameError::InvalidTypeName { text: text.to_string(), errors: errors.clone(), - }); + }) } } @@ -513,7 +513,7 @@ impl WildcardTypeName { fqtn_errors ); // Convert the FQTN errors and add into the WCTN error collection - errors.extend(fqtn_errors.into_iter().map(|e| { + errors.extend(fqtn_errors.iter_mut().map(|e| { match TryInto::::try_into(e.clone()) { Ok(wtne) => wtne, Err(e) => e, From d24e52bfef270fcf0d0725f602de91b094256c34 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 14:46:44 -0500 Subject: [PATCH 07/22] (MAINT) Fix clippy for `dsc` --- dsc/src/resource_command.rs | 5 ++--- dsc/src/subcommand.rs | 10 ++++------ dsc/src/util.rs | 22 ++++++++++------------ 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/dsc/src/resource_command.rs b/dsc/src/resource_command.rs index 735335bb7..2d678434f 100644 --- a/dsc/src/resource_command.rs +++ b/dsc/src/resource_command.rs @@ -34,8 +34,8 @@ pub fn get(dsc: &mut DscManager, resource_type: &FullyQualifiedTypeName, version match resource.get(input) { Ok(result) => { - if let GetResult::Resource(response) = &result { - if format == Some(&GetOutputFormat::PassThrough) { + if let GetResult::Resource(response) = &result + && format == Some(&GetOutputFormat::PassThrough) { let json = match serde_json::to_string(&response.actual_state) { Ok(json) => json, Err(err) => { @@ -46,7 +46,6 @@ pub fn get(dsc: &mut DscManager, resource_type: &FullyQualifiedTypeName, version write_object(&json, Some(&OutputFormat::Json), false); return; } - } // convert to json let json = match serde_json::to_string(&result) { diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 30b116957..25a725b32 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -322,10 +322,8 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, mounte configurator.context.dsc_version = Some(env!("CARGO_PKG_VERSION").to_string()); - if let ConfigSubCommand::Set { what_if , .. } = subcommand { - if *what_if { - configurator.context.execution_type = ExecutionKind::WhatIf; - } + if let ConfigSubCommand::Set { what_if , .. } = subcommand && *what_if { + configurator.context.execution_type = ExecutionKind::WhatIf; } let parameters: Option = match if new_parameters.is_some() { @@ -499,7 +497,7 @@ pub fn validate_config(config: &Configuration, progress_format: ProgressFormat) let type_name = &FullyQualifiedTypeName::parse(type_name)?; let require_version = resource_block["requireVersion"] .as_str() - .map(|r| ResourceVersionReq::parse(r)) + .map(ResourceVersionReq::parse) .transpose()?; resource_types.push(DiscoveryFilter::new(type_name, require_version, None)); } @@ -512,7 +510,7 @@ pub fn validate_config(config: &Configuration, progress_format: ProgressFormat) let type_name = &FullyQualifiedTypeName::parse(type_name)?; let require_version = resource_block["requireVersion"] .as_str() - .map(|r| ResourceVersionReq::parse(r)) + .map(ResourceVersionReq::parse) .transpose()?; trace!("{} '{}'", t!("subcommand.validatingResource"), resource_block["name"].as_str().unwrap_or_default()); diff --git a/dsc/src/util.rs b/dsc/src/util.rs index d691e8935..1b3c3d81a 100644 --- a/dsc/src/util.rs +++ b/dsc/src/util.rs @@ -350,18 +350,16 @@ pub fn enable_tracing(trace_level_arg: Option<&TraceLevel>, trace_format_arg: Op } // override with DSC_TRACE_LEVEL env var if permitted - if tracing_setting.allow_override { - if let Ok(level) = env::var(DSC_TRACE_LEVEL) { - tracing_setting.level = match level.to_ascii_uppercase().as_str() { - "ERROR" => TraceLevel::Error, - "WARN" => TraceLevel::Warn, - "INFO" => TraceLevel::Info, - "DEBUG" => TraceLevel::Debug, - "TRACE" => TraceLevel::Trace, - _ => { - warn!("{}: '{level}'", t!("util.invalidTraceLevel")); - TraceLevel::Warn - } + if tracing_setting.allow_override && let Ok(level) = env::var(DSC_TRACE_LEVEL) { + tracing_setting.level = match level.to_ascii_uppercase().as_str() { + "ERROR" => TraceLevel::Error, + "WARN" => TraceLevel::Warn, + "INFO" => TraceLevel::Info, + "DEBUG" => TraceLevel::Debug, + "TRACE" => TraceLevel::Trace, + _ => { + warn!("{}: '{level}'", t!("util.invalidTraceLevel")); + TraceLevel::Warn } } } From 9a6184d4c7447e59cdfe3deb80d9562d98e327f6 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 14:47:14 -0500 Subject: [PATCH 08/22] (MAINT) Fix clippy for `dsc-bicep-ext` --- dsc-bicep-ext/src/main.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dsc-bicep-ext/src/main.rs b/dsc-bicep-ext/src/main.rs index dd51fa571..ffbe56a5f 100644 --- a/dsc-bicep-ext/src/main.rs +++ b/dsc-bicep-ext/src/main.rs @@ -213,7 +213,7 @@ impl BicepExtension for BicepExtensionService { resource: Some(proto::Resource { r#type: resource_type, api_version: version, - identifiers: identifiers, + identifiers, properties: result.actual_state.to_string(), status: None, }), @@ -270,7 +270,7 @@ impl BicepExtension for BicepExtensionService { resource: Some(proto::Resource { r#type: resource_type, api_version: version, - identifiers: identifiers, + identifiers, properties: "{}".to_string(), status: None, }), @@ -367,9 +367,7 @@ async fn run_server( impl Connected for NamedPipeConnection { type ConnectInfo = (); - fn connect_info(&self) -> Self::ConnectInfo { - () - } + fn connect_info(&self) -> Self::ConnectInfo {} } impl AsyncRead for NamedPipeConnection { From 6a06074e134fdafb8edddd07c3a35b0f7a872565 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 14:47:30 -0500 Subject: [PATCH 09/22] (MAINT) Fix clippy for `dsctest` --- tools/dsctest/src/copy_resource.rs | 2 +- tools/dsctest/src/refresh_env.rs | 17 ++++++----------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/tools/dsctest/src/copy_resource.rs b/tools/dsctest/src/copy_resource.rs index a92d69623..e426c1cfb 100644 --- a/tools/dsctest/src/copy_resource.rs +++ b/tools/dsctest/src/copy_resource.rs @@ -23,7 +23,7 @@ pub fn copy_the_resource(source_file: &str, type_name: &str) -> Result<(), Strin } else { return Err("Source file not a resource manifest".to_string()); } - let name_part = type_name.split('/').last().unwrap_or(type_name); + let name_part = type_name.split('/').next_back().unwrap_or(type_name); let output_file = format!("{name_part}.dsc.resource.json"); let output_content = serde_json::to_string_pretty(&resource_json) .map_err(|e| format!("Failed to serialize JSON: {e}"))?; diff --git a/tools/dsctest/src/refresh_env.rs b/tools/dsctest/src/refresh_env.rs index bfe797961..96e4580d5 100644 --- a/tools/dsctest/src/refresh_env.rs +++ b/tools/dsctest/src/refresh_env.rs @@ -40,17 +40,12 @@ impl RefreshEnv { { // Get the environment variable from the registry for current user let hkcu = Hive::CurrentUser.open("Environment", Security::Read).unwrap(); - if let Ok(data) = hkcu.value(&self.name) { - match data { - Data::String(value) | Data::ExpandString(value) => { - return RefreshEnv { - metadata: None, - name: self.name.clone(), - value: value.to_string_lossy(), - }; - } - _ => {} - } + if let Ok(Data::String(value) | Data::ExpandString(value)) = hkcu.value(&self.name) { + return RefreshEnv { + metadata: None, + name: self.name.clone(), + value: value.to_string_lossy(), + }; } RefreshEnv { From b31ca63fe8e94dacffc5b9f60d4257b8ffeab299 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 14:47:47 -0500 Subject: [PATCH 10/22] (MAINT) Fix clippy for dism_dsc` --- resources/dism_dsc/src/feature_on_demand/export.rs | 5 ++--- resources/dism_dsc/src/optional_feature/export.rs | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/resources/dism_dsc/src/feature_on_demand/export.rs b/resources/dism_dsc/src/feature_on_demand/export.rs index 15f1b289c..6709a6a7e 100644 --- a/resources/dism_dsc/src/feature_on_demand/export.rs +++ b/resources/dism_dsc/src/feature_on_demand/export.rs @@ -41,12 +41,11 @@ pub fn handle_export(input: &str) -> Result { let mut should_get_full = !filters_without_identity.is_empty(); if !should_get_full { for f in &filters_with_identity { - if let Some(ref filter_identity) = f.identity { - if matches_wildcard(name, filter_identity) { + if let Some(ref filter_identity) = f.identity + && matches_wildcard(name, filter_identity) { should_get_full = true; break; } - } } } if !should_get_full { diff --git a/resources/dism_dsc/src/optional_feature/export.rs b/resources/dism_dsc/src/optional_feature/export.rs index b05d7e0bb..5b19f95e3 100644 --- a/resources/dism_dsc/src/optional_feature/export.rs +++ b/resources/dism_dsc/src/optional_feature/export.rs @@ -45,12 +45,11 @@ pub fn handle_export(input: &str) -> Result { let mut should_get_full = !filters_without_name.is_empty(); if !should_get_full { for f in &filters_with_name { - if let Some(ref filter_name) = f.feature_name { - if matches_wildcard(name, filter_name) { + if let Some(ref filter_name) = f.feature_name + && matches_wildcard(name, filter_name) { should_get_full = true; break; } - } } } if !should_get_full { From 505beb511cc976659be6e97cd6b135141f11856f Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 14:47:59 -0500 Subject: [PATCH 11/22] (MAINT) Fix clippy for dscecho` --- resources/dscecho/src/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/dscecho/src/main.rs b/resources/dscecho/src/main.rs index 8b1ef5dd4..ed040dc73 100644 --- a/resources/dscecho/src/main.rs +++ b/resources/dscecho/src/main.rs @@ -57,11 +57,12 @@ fn main() { } fn is_secure_value(value: &Value) -> bool { - if let Some(obj) = value.as_object() { - if obj.len() == 1 && (obj.contains_key("secureString") || obj.contains_key("secureObject")) { + if let Some(obj) = value.as_object() + && obj.len() == 1 + && (obj.contains_key("secureString") || obj.contains_key("secureObject")) { return true; } - } + false } From e67b0037cfcdd552ce02b2687b44871919f2d922 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 14:48:21 -0500 Subject: [PATCH 12/22] (MAINT) Fix clippy for `registry` --- resources/registry/src/main.rs | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/resources/registry/src/main.rs b/resources/registry/src/main.rs index cc6346444..8511dcdc5 100644 --- a/resources/registry/src/main.rs +++ b/resources/registry/src/main.rs @@ -78,24 +78,23 @@ fn main() { if what_if { reg_helper.enable_what_if(); } // In what-if, if the desired state is _exist: false, route to delete - if what_if { - if let Ok(desired) = serde_json::from_str::(&input) { - if matches!(desired.exist, Some(false)) { - match reg_helper.remove() { - Ok(Some(reg_config)) => { - let json = serde_json::to_string(®_config).unwrap(); - println!("{json}"); - }, - Ok(None) => {}, - Err(err) => { - error!("{err}"); - exit(EXIT_REGISTRY_ERROR); - } + if what_if + && let Ok(desired) = serde_json::from_str::(&input) + && matches!(desired.exist, Some(false)) { + match reg_helper.remove() { + Ok(Some(reg_config)) => { + let json = serde_json::to_string(®_config).unwrap(); + println!("{json}"); + }, + Ok(None) => {}, + Err(err) => { + error!("{err}"); + exit(EXIT_REGISTRY_ERROR); } - return; } + return; } - } + match reg_helper.set() { Ok(reg_config) => { if let Some(config) = reg_config { From 93a23989a7ecac4b63608ff250a4c952cfb7d939 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 14:48:37 -0500 Subject: [PATCH 13/22] (MAINT) Fix clippy for `sshdconfig` --- resources/sshdconfig/src/export.rs | 5 ++--- resources/sshdconfig/src/formatter.rs | 10 ++++------ resources/sshdconfig/src/get.rs | 6 ++---- resources/sshdconfig/src/parser.rs | 18 +++++++----------- resources/sshdconfig/src/repeat_keyword.rs | 11 +++++------ 5 files changed, 20 insertions(+), 30 deletions(-) diff --git a/resources/sshdconfig/src/export.rs b/resources/sshdconfig/src/export.rs index 752978118..e133194d5 100644 --- a/resources/sshdconfig/src/export.rs +++ b/resources/sshdconfig/src/export.rs @@ -19,8 +19,8 @@ pub fn invoke_export(input: Option<&String>, compare: bool) -> Result(input_value.clone()) { @@ -36,7 +36,6 @@ pub fn invoke_export(input: Option<&String>, compare: bool) -> Result SshdConfigValue<'a> { )); } - if let Value::Array(arr) = value { - if arr.is_empty() { - return Err(SshdConfigError::ParserError( - t!("formatter.invalidValue", key = key).to_string() - )); - } + if let Value::Array(arr) = value && arr.is_empty() { + return Err(SshdConfigError::ParserError( + t!("formatter.invalidValue", key = key).to_string() + )); } let mut keyword_info = KeywordInfo::from_keyword(key); diff --git a/resources/sshdconfig/src/get.rs b/resources/sshdconfig/src/get.rs index 263443a82..a162b57e2 100644 --- a/resources/sshdconfig/src/get.rs +++ b/resources/sshdconfig/src/get.rs @@ -144,10 +144,8 @@ pub fn get_sshd_settings(cmd_info: &CommandInfo, is_get: bool) -> Result Result) -> Option { keyword_array.iter().position(|item| { - if let Value::Object(obj) = item { - if let Some(Value::String(name)) = obj.get("name") { + if let Value::Object(obj) = item + && let Some(Value::String(name)) = obj.get("name") { if name != entry_name { return false; } @@ -260,7 +260,7 @@ pub fn find_name_value_entry_index(keyword_array: &[Value], entry_name: &str, ma return true; } - } + false }) } @@ -324,9 +324,8 @@ pub fn add_or_update_entry(config: &mut Map, keyword: &str, entry /// * `keyword` - The keyword name (e.g., "subsystem") /// * `entry_name` - The name of the entry to remove pub fn remove_entry(config: &mut Map, keyword: &str, entry_name: &str) { - if let Some(Value::Array(arr)) = config.get_mut(keyword) { - if let Some(index) = find_name_value_entry_index(arr, entry_name, None) { + if let Some(Value::Array(arr)) = config.get_mut(keyword) + && let Some(index) = find_name_value_entry_index(arr, entry_name, None) { arr.remove(index); } - } } From e717e1e4e41a53166959563d1b94cb5f9a651e96 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 14:48:53 -0500 Subject: [PATCH 14/22] (MAINT) Fix clippy for `windows_firewall` --- resources/windows_firewall/src/firewall.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/resources/windows_firewall/src/firewall.rs b/resources/windows_firewall/src/firewall.rs index 5c9556178..8c570e550 100644 --- a/resources/windows_firewall/src/firewall.rs +++ b/resources/windows_firewall/src/firewall.rs @@ -314,13 +314,11 @@ fn apply_rule_properties(rule: &INetFwRule, desired: &FirewallRule, existing_pro // Reject port specifications for protocols that don't support them (e.g. ICMP). // This must be checked regardless of whether the protocol itself was changed, // because the caller may only be setting local_ports or remote_ports. - if let Some(protocol) = effective_protocol { - if !protocol_supports_ports(protocol) - && (desired.local_ports.is_some() || desired.remote_ports.is_some()) - { + if let Some(protocol) = effective_protocol + && !protocol_supports_ports(protocol) + && (desired.local_ports.is_some() || desired.remote_ports.is_some()) { return Err(t!("firewall.portsNotAllowed", name = name, protocol = protocol).to_string().into()); } - } if let Some(protocol) = desired.protocol { if let Some(current_protocol) = existing_protocol From 8fb589a2c9dbcdc1c504058b5eab0758902f4b1b Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 14:49:04 -0500 Subject: [PATCH 15/22] (MAINT) Fix clippy for `windows_service` --- resources/windows_service/src/service.rs | 51 ++++++++++-------------- 1 file changed, 20 insertions(+), 31 deletions(-) diff --git a/resources/windows_service/src/service.rs b/resources/windows_service/src/service.rs index dbf425b50..c54d5e979 100644 --- a/resources/windows_service/src/service.rs +++ b/resources/windows_service/src/service.rs @@ -92,10 +92,8 @@ unsafe fn read_service_state( let sizing_result = unsafe { QueryServiceConfigW(service_handle, None, 0, &mut bytes_needed) }; - if let Err(e) = sizing_result { - if e.code() != ERROR_INSUFFICIENT_BUFFER.to_hresult() { - return Err(t!("get.queryConfigFailed", error = e.to_string()).to_string().into()); - } + if let Err(e) = sizing_result && e.code() != ERROR_INSUFFICIENT_BUFFER.to_hresult() { + return Err(t!("get.queryConfigFailed", error = e.to_string()).to_string().into()); } if bytes_needed == 0 { return Err(t!("get.queryConfigFailed", error = "buffer size is 0").to_string().into()); @@ -223,8 +221,8 @@ pub fn get_service(input: &WindowsService) -> Result) -> Result) -> Result continue, // skip services we can't query }; - if let Some(f) = filter { - if !matches_filter(&svc, f) { - continue; - } + if let Some(f) = filter && !matches_filter(&svc, f) { + continue; } results.push(svc); @@ -457,13 +451,11 @@ unsafe fn enumerate_services(scm: SC_HANDLE) -> Result bool { } } - if !ends_with_wildcard { - if let Some(last) = parts.last() { - if !last.is_empty() && !text_lower.ends_with(last) { - return false; - } + if !ends_with_wildcard + && let Some(last) = parts.last() + && !last.is_empty() + && !text_lower.ends_with(last) { + return false; } - } true } @@ -616,12 +607,10 @@ pub fn set_service(input: &WindowsService) -> Result Date: Mon, 6 Apr 2026 14:49:21 -0500 Subject: [PATCH 16/22] (MAINT) Fix clippy for `WindowsUpdate` --- .../src/windows_update/export.rs | 43 ++++++++----------- .../WindowsUpdate/src/windows_update/get.rs | 12 +++--- .../WindowsUpdate/src/windows_update/set.rs | 26 ++++++----- 3 files changed, 37 insertions(+), 44 deletions(-) diff --git a/resources/WindowsUpdate/src/windows_update/export.rs b/resources/WindowsUpdate/src/windows_update/export.rs index 66930d3da..81ab9b6ba 100644 --- a/resources/WindowsUpdate/src/windows_update/export.rs +++ b/resources/WindowsUpdate/src/windows_update/export.rs @@ -33,7 +33,7 @@ pub fn handle_export(input: &str) -> Result { } } else { serde_json::from_str(input) - .map_err(|e| Error::new(E_INVALIDARG.into(), t!("export.failedParseInput", err = e.to_string()).to_string()))? + .map_err(|e| Error::new(E_INVALIDARG, t!("export.failedParseInput", err = e.to_string())))? }; let filters = &update_list.updates; @@ -120,16 +120,14 @@ pub fn handle_export(input: &str) -> Result { } // Filter by KB article IDs (match if any KB ID in the filter is present) - if let Some(kb_filter) = &filter.kb_article_ids { - if !kb_filter.is_empty() { - if let Some(ref kb_article_ids) = update_info.kb_article_ids { - let kb_matches = kb_filter.iter().any(|filter_kb| { - kb_article_ids.iter().any(|update_kb| update_kb.eq_ignore_ascii_case(filter_kb)) - }); - matches = matches && kb_matches; - } else { - matches = false; - } + if let Some(kb_filter) = &filter.kb_article_ids && !kb_filter.is_empty() { + if let Some(ref kb_article_ids) = update_info.kb_article_ids { + let kb_matches = kb_filter.iter().any(|filter_kb| { + kb_article_ids.iter().any(|update_kb| update_kb.eq_ignore_ascii_case(filter_kb)) + }); + matches = matches && kb_matches; + } else { + matches = false; } } @@ -148,8 +146,8 @@ pub fn handle_export(input: &str) -> Result { } // Filter by security bulletin IDs (match if any bulletin ID in the filter is present) - if let Some(bulletin_filter) = &filter.security_bulletin_ids { - if !bulletin_filter.is_empty() { + if let Some(bulletin_filter) = &filter.security_bulletin_ids + && !bulletin_filter.is_empty() { if let Some(ref security_bulletin_ids) = update_info.security_bulletin_ids { let bulletin_matches = bulletin_filter.iter().any(|filter_bulletin| { security_bulletin_ids.iter().any(|update_bulletin| update_bulletin.eq_ignore_ascii_case(filter_bulletin)) @@ -159,7 +157,6 @@ pub fn handle_export(input: &str) -> Result { matches = false; } } - } // Filter by update type if let Some(type_filter) = &filter.update_type { @@ -230,7 +227,7 @@ pub fn handle_export(input: &str) -> Result { // Emit JSON error to stderr eprintln!("{{\"error\":\"{}\"}}", error_msg); - return Err(Error::new(E_FAIL.into(), error_msg)); + return Err(Error::new(E_FAIL, error_msg)); } } } @@ -252,7 +249,7 @@ pub fn handle_export(input: &str) -> Result { updates }; serde_json::to_string(&result) - .map_err(|e| Error::new(E_FAIL.into(), t!("export.failedSerializeOutput", err = e.to_string()).to_string())) + .map_err(|e| Error::new(E_FAIL, t!("export.failedSerializeOutput", err = e.to_string()))) } Err(e) => Err(e), } @@ -303,15 +300,13 @@ fn matches_wildcard(text: &str, pattern: &str) -> bool { } } } - + // For the last part, check if it should be at the end - if !ends_with_wildcard && !parts.is_empty() { - if let Some(last_part) = parts.last() { - if !last_part.is_empty() && !text_lower.ends_with(last_part) { - return false; - } + if !ends_with_wildcard && !parts.is_empty() + && let Some(last_part) = parts.last() + && !last_part.is_empty() && !text_lower.ends_with(last_part) { + return false; } - } - + true } diff --git a/resources/WindowsUpdate/src/windows_update/get.rs b/resources/WindowsUpdate/src/windows_update/get.rs index e8aab7db9..9a846ef40 100644 --- a/resources/WindowsUpdate/src/windows_update/get.rs +++ b/resources/WindowsUpdate/src/windows_update/get.rs @@ -14,10 +14,10 @@ use crate::windows_update::types::{UpdateList, extract_update_info}; pub fn handle_get(input: &str) -> Result { // Parse input as UpdateList let update_list: UpdateList = serde_json::from_str(input) - .map_err(|e| Error::new(E_INVALIDARG.into(), t!("get.failedParseInput", err = e.to_string()).to_string()))?; + .map_err(|e| Error::new(E_INVALIDARG, t!("get.failedParseInput", err = e.to_string())))?; if update_list.updates.is_empty() { - return Err(Error::new(E_INVALIDARG.into(), t!("get.updatesArrayEmpty").to_string())); + return Err(Error::new(E_INVALIDARG, t!("get.updatesArrayEmpty"))); } // Initialize COM @@ -54,7 +54,7 @@ pub fn handle_get(input: &str) -> Result { && update_input.is_installed.is_none() && update_input.update_type.is_none() && update_input.msrc_severity.is_none() { - return Err(Error::new(E_INVALIDARG.into(), t!("get.atLeastOneCriterionRequired").to_string())); + return Err(Error::new(E_INVALIDARG, t!("get.atLeastOneCriterionRequired"))); } // Find the update matching ALL provided criteria (logical AND) @@ -168,7 +168,7 @@ pub fn handle_get(input: &str) -> Result { }; eprintln!("{{\"error\":\"{}\"}}", error_msg); - return Err(Error::new(E_INVALIDARG.into(), error_msg)); + return Err(Error::new(E_INVALIDARG, error_msg)); } // Get the first (and should be only) match @@ -206,7 +206,7 @@ pub fn handle_get(input: &str) -> Result { // Emit JSON error to stderr eprintln!("{{\"error\":\"{}\"}}", error_msg); - return Err(Error::new(E_FAIL.into(), error_msg)); + return Err(Error::new(E_FAIL, error_msg)); } } @@ -227,7 +227,7 @@ pub fn handle_get(input: &str) -> Result { updates }; serde_json::to_string(&result) - .map_err(|e| Error::new(E_FAIL.into(), t!("get.failedSerializeOutput", err = e.to_string()).to_string())) + .map_err(|e| Error::new(E_FAIL, t!("get.failedSerializeOutput", err = e.to_string()))) } Err(e) => Err(e), } diff --git a/resources/WindowsUpdate/src/windows_update/set.rs b/resources/WindowsUpdate/src/windows_update/set.rs index 1381a1016..5528b826c 100644 --- a/resources/WindowsUpdate/src/windows_update/set.rs +++ b/resources/WindowsUpdate/src/windows_update/set.rs @@ -20,10 +20,10 @@ fn get_computer_name() -> String { pub fn handle_set(input: &str) -> Result { // Parse input as UpdateList let update_list: UpdateList = serde_json::from_str(input) - .map_err(|e| Error::new(E_INVALIDARG.into(), t!("set.failedParseInput", err = e.to_string()).to_string()))?; + .map_err(|e| Error::new(E_INVALIDARG, t!("set.failedParseInput", err = e.to_string())))?; if update_list.updates.is_empty() { - return Err(Error::new(E_INVALIDARG.into(), t!("set.updatesArrayEmpty").to_string())); + return Err(Error::new(E_INVALIDARG, t!("set.updatesArrayEmpty"))); } // Initialize COM @@ -60,7 +60,7 @@ pub fn handle_set(input: &str) -> Result { && update_input.is_installed.is_none() && update_input.update_type.is_none() && update_input.msrc_severity.is_none() { - return Err(Error::new(E_INVALIDARG.into(), t!("set.atLeastOneCriterionRequired").to_string())); + return Err(Error::new(E_INVALIDARG, t!("set.atLeastOneCriterionRequired"))); } // Find the update matching ALL provided criteria (logical AND) @@ -167,7 +167,7 @@ pub fn handle_set(input: &str) -> Result { t!("set.criteriaMatchedMultipleUpdates", count = matching_updates.len()).to_string() }; eprintln!("{{\"error\":\"{}\"}}", error_msg); - return Err(Error::new(E_INVALIDARG.into(), error_msg)); + return Err(Error::new(E_INVALIDARG, error_msg)); } // Get the first (and should be only) match @@ -205,7 +205,7 @@ pub fn handle_set(input: &str) -> Result { // Emit JSON error to stderr eprintln!("{{\"error\":\"{}\"}}", error_msg); - return Err(Error::new(E_FAIL.into(), error_msg)); + return Err(Error::new(E_FAIL, error_msg)); } } @@ -238,7 +238,7 @@ pub fn handle_set(input: &str) -> Result { // Check if download was successful (orcSucceeded = 2) if result_code != OperationResultCode(2) { let hresult = download_result.HResult()?; - return Err(Error::new(HRESULT(hresult).into(), t!("set.failedDownloadUpdate", code = result_code.0).to_string())); + return Err(Error::new(HRESULT(hresult), t!("set.failedDownloadUpdate", code = result_code.0))); } } @@ -252,17 +252,15 @@ pub fn handle_set(input: &str) -> Result { // Check if installation was successful (orcSucceeded = 2) if result_code != OperationResultCode(2) { let hresult = install_result.HResult()?; - return Err(Error::new(HRESULT(hresult).into(), t!("set.failedInstallUpdate", code = result_code.0).to_string())); + return Err(Error::new(HRESULT(hresult), t!("set.failedInstallUpdate", code = result_code.0))); } // Check if installation result indicates a reboot is required - if !reboot_required { - if let Ok(reboot_req) = install_result.RebootRequired() { - if reboot_req.as_bool() { - reboot_required = true; - } + if !reboot_required + && let Ok(reboot_req) = install_result.RebootRequired() + && reboot_req.as_bool() { + reboot_required = true; } - } // Get full details now that it's installed extract_update_info(&update)? @@ -298,7 +296,7 @@ pub fn handle_set(input: &str) -> Result { updates }; serde_json::to_string(&results) - .map_err(|e| Error::new(E_FAIL.into(), t!("set.failedSerializeOutput", err = e.to_string()).to_string())) + .map_err(|e| Error::new(E_FAIL, t!("set.failedSerializeOutput", err = e.to_string()))) } Err(e) => Err(e), } From e81c00be2894c82a136bb76d15de5ff67dc998b3 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Mon, 6 Apr 2026 15:51:07 -0500 Subject: [PATCH 17/22] (MAINT) Use `cargo` for `tryWhich()` test Prior to this change, the `exe_exists()` test for the `tryWhich()` configuration function looked for `dsc`, which may not exist when the project has not successfully built yet. This change uses `cargo` instead, which is guaranteed to be available when this test is executed. --- lib/dsc-lib/src/functions/try_which.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/dsc-lib/src/functions/try_which.rs b/lib/dsc-lib/src/functions/try_which.rs index 6644a81a3..93895738c 100644 --- a/lib/dsc-lib/src/functions/try_which.rs +++ b/lib/dsc-lib/src/functions/try_which.rs @@ -55,11 +55,11 @@ mod tests { #[test] fn exe_exists() { let mut parser = Statement::new().unwrap(); - let result = parser.parse_and_execute("[tryWhich('dsc')]", &Context::new()).unwrap(); + let result = parser.parse_and_execute("[tryWhich('cargo')]", &Context::new()).unwrap(); #[cfg(windows)] - assert!(result.as_str().unwrap().to_lowercase().ends_with("\\dsc.exe")); + assert!(result.as_str().unwrap().to_lowercase().ends_with("\\cargo.exe")); #[cfg(not(windows))] - assert!(result.as_str().unwrap().ends_with("/dsc")); + assert!(result.as_str().unwrap().ends_with("/cargo")); } #[test] From b97544445522c4fb91d3cfd3adab65a6cb06527f Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Tue, 7 Apr 2026 15:36:42 -0700 Subject: [PATCH 18/22] Fix dism resources to work on Windows arm64 --- resources/dism_dsc/src/dism.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/resources/dism_dsc/src/dism.rs b/resources/dism_dsc/src/dism.rs index 70415fe20..167b20597 100644 --- a/resources/dism_dsc/src/dism.rs +++ b/resources/dism_dsc/src/dism.rs @@ -25,13 +25,13 @@ unsafe extern "system" { ) -> *mut c_void; } -#[repr(C, packed(4))] +#[repr(C, packed)] struct DismFeature { feature_name: *const u16, state: i32, } -#[repr(C, packed(4))] +#[repr(C, packed)] struct DismFeatureInfo { feature_name: *const u16, state: i32, @@ -42,13 +42,13 @@ struct DismFeatureInfo { custom_property_count: u32, } -#[repr(C, packed(4))] +#[repr(C, packed)] struct DismCapability { name: *const u16, state: i32, } -#[repr(C, packed(4))] +#[repr(C, packed)] struct DismCapabilityDetail { name: *const u16, state: i32, From 3b4d2777087f7066457a3905769198d58985ff65 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Tue, 7 Apr 2026 15:52:59 -0700 Subject: [PATCH 19/22] move build tools up above tree-sitter --- build.ps1 | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build.ps1 b/build.ps1 index 0fd55e674..7f79e29f3 100755 --- a/build.ps1 +++ b/build.ps1 @@ -200,6 +200,11 @@ process { Install-Clippy -UseCFS:$UseCFS -Architecture $Architecture @VerboseParam } + if (!$SkipBuild -and !$SkipLinkCheck -and $IsWindows) { + Write-BuildProgress @progressParams -Status "Ensuring Windows C++ build tools are available" + Install-WindowsCPlusPlusBuildTools @VerboseParam + } + if (-not ($SkipBuild -and $Test -and $ExcludeRustTests)) { Write-BuildProgress @progressParams -Status 'Ensuring Protobuf is available' Install-Protobuf @VerboseParam @@ -212,10 +217,6 @@ process { } } - if (!$SkipBuild -and !$SkipLinkCheck -and $IsWindows) { - Write-BuildProgress @progressParams -Status "Ensuring Windows C++ build tools are available" - Install-WindowsCPlusPlusBuildTools @VerboseParam - } #endregion Setup if (!$SkipBuild) { From a299db7f5e452d30abbda01a7d60b85859c05ac0 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 9 Apr 2026 11:27:09 -0400 Subject: [PATCH 20/22] add test --- .../tests/sshdconfigRepeat.tests.ps1 | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 b/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 index ac39caf23..8a2a34d7b 100644 --- a/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 +++ b/resources/sshdconfig/tests/sshdconfigRepeat.tests.ps1 @@ -212,5 +212,23 @@ PasswordAuthentication yes Remove-Item -Path $stderrFile -Force -ErrorAction SilentlyContinue } + + It 'Should default to _exist=true when not specified explicitly' { + $inputConfig = @{ + _metadata = @{ + filepath = $TestConfigPath + } + subsystem = @{ + name = "testExistDefault" + value = "/path/to/subsystem" + } + } | ConvertTo-Json + + $output = sshdconfig set --input $inputConfig -s sshd-config-repeat 2>$null + $LASTEXITCODE | Should -Be 0 + # verify subsystem was added (defaulting to _exist=true) + $subsystems = Get-Content $TestConfigPath | Where-Object { $_ -match '^\s*subsystem\s+' } + $subsystems | Should -Contain "subsystem testExistDefault /path/to/subsystem" + } } } From 4680230c00d50511a761efba411cfac44cfe8d7a Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 9 Apr 2026 11:52:30 -0400 Subject: [PATCH 21/22] default _exist to true --- resources/sshdconfig/src/repeat_keyword.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/resources/sshdconfig/src/repeat_keyword.rs b/resources/sshdconfig/src/repeat_keyword.rs index edf8d4cc5..e27f51c9e 100644 --- a/resources/sshdconfig/src/repeat_keyword.rs +++ b/resources/sshdconfig/src/repeat_keyword.rs @@ -78,11 +78,13 @@ pub struct NameValueEntry { pub value: Option, } +fn default_true() -> bool { true } + /// Input for name-value keyword single-entry operations (e.g., subsystem). #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)] pub struct RepeatInput { /// Whether the entry should exist (true) or be removed (false) - #[serde(rename = "_exist", default)] + #[serde(rename = "_exist", default = "default_true")] pub exist: bool, /// Metadata for the operation #[serde(rename = "_metadata", skip_serializing_if = "Option::is_none")] From e1bacf82ca4e01a1f627e24f90b03d91c165db75 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 9 Apr 2026 20:05:33 -0700 Subject: [PATCH 22/22] Update version to 3.2-rc.1 --- Cargo.lock | 39 +++++++++++++++------------------------ Cargo.toml | 2 +- dsc/Cargo.toml | 2 +- 3 files changed, 17 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 739adf4f8..e710a7276 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -723,7 +723,7 @@ dependencies = [ [[package]] name = "dsc" -version = "3.2.0-preview.14" +version = "3.2.0-rc.1" dependencies = [ "clap", "clap_complete", @@ -1852,14 +1852,14 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.4" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "log", "wasi", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2988,9 +2988,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" dependencies = [ "libc", "mio", @@ -3032,12 +3032,12 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3273,9 +3273,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.50.0" +version = "1.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "f66bf9585cda4b724d3e78ab34b73fb2bbaba9011b9bfdf69dc836382ea13b8c" dependencies = [ "bytes", "libc", @@ -3289,9 +3289,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -3417,9 +3417,9 @@ dependencies = [ [[package]] name = "tonic-prost-build" -version = "0.14.2" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4a16cba4043dc3ff43fcb3f96b4c5c154c64cbd18ca8dce2ab2c6a451d058a2" +checksum = "f3144df636917574672e93d0f56d7edec49f90305749c668df5101751bb8f95a" dependencies = [ "prettyplease", "proc-macro2", @@ -4200,15 +4200,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.60.2" diff --git a/Cargo.toml b/Cargo.toml index 7cd0e980f..7e0b7a343 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -223,7 +223,7 @@ tempfile = { version = "3.27" } # dsc, dsc-lib, registry, dsc-lib-registry, sshdconfig thiserror = { version = "2.0" } # dsc, dsc-lib, dsc-bicep-ext -tokio = { version = "1.50" } +tokio = { version = "1.51" } # dsc-bicep-ext tokio-stream = { version = "0.1" } # dsc diff --git a/dsc/Cargo.toml b/dsc/Cargo.toml index 58de39f86..8160a0a9c 100644 --- a/dsc/Cargo.toml +++ b/dsc/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dsc" -version = "3.2.0-preview.14" +version = "3.2.0-rc.1" edition = "2024" [dependencies]