From 1ea367890d5d754d8beb3b38a702b72e1c2f2ae9 Mon Sep 17 00:00:00 2001 From: Mikey Lombardi Date: Tue, 9 Dec 2025 15:03:20 -0600 Subject: [PATCH] (GH-538) Use derive macro for `DscRepoSchema` trait Prior to this change, the resource manifest, extension manifest, and configuration document structs in `dsc-lib` manually implemented the `DscRepoSchema` trait. This change uses the new macro to derive the trait on those types and other types in the library that currently map to schema files. This change also makes an effort to correctly annotate types that aren't covered by the separate schemas, but as not all types map to a schema file, these changes don't fully cover all types that derive the `JsonSchema` trait. This change prepares the library for further canonicalization steps to steer the generated schemas towards the definitions in the canonical schemas. --- lib/dsc-lib/src/configure/config_doc.rs | 79 +++++++++---------- lib/dsc-lib/src/configure/config_result.rs | 26 +++--- lib/dsc-lib/src/dscresources/dscresource.rs | 8 +- lib/dsc-lib/src/dscresources/invoke_result.rs | 28 ++++--- .../src/dscresources/resource_manifest.rs | 79 +++++++++---------- lib/dsc-lib/src/extensions/discover.rs | 7 +- lib/dsc-lib/src/extensions/dscextension.rs | 8 +- .../src/extensions/extension_manifest.rs | 40 +++------- lib/dsc-lib/src/extensions/import.rs | 8 +- lib/dsc-lib/src/extensions/secret.rs | 5 +- lib/dsc-lib/src/functions/mod.rs | 10 ++- 11 files changed, 160 insertions(+), 138 deletions(-) diff --git a/lib/dsc-lib/src/configure/config_doc.rs b/lib/dsc-lib/src/configure/config_doc.rs index 6489114b6..367d01456 100644 --- a/lib/dsc-lib/src/configure/config_doc.rs +++ b/lib/dsc-lib/src/configure/config_doc.rs @@ -3,28 +3,30 @@ use chrono::{DateTime, Local}; use rust_i18n::t; -use schemars::{JsonSchema, json_schema}; +use schemars::JsonSchema; use serde::{Deserialize, Deserializer, Serialize}; use serde_json::{Map, Value}; use std::{collections::HashMap, fmt::Display}; use crate::schemas::{ - dsc_repo::{DscRepoSchema, UnrecognizedSchemaUri}, + dsc_repo::DscRepoSchema, transforms::{idiomaticize_externally_tagged_enum, idiomaticize_string_enum} }; -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(rename_all = "camelCase")] #[schemars(transform = idiomaticize_string_enum)] +#[dsc_repo_schema(base_name = "securityContext", folder_path = "metadata/Microsoft.DSC")] pub enum SecurityContextKind { Current, Elevated, Restricted, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(rename_all = "camelCase")] #[schemars(transform = idiomaticize_string_enum)] +#[dsc_repo_schema(base_name = "operation", folder_path = "metadata/Microsoft.DSC")] pub enum Operation { Get, Set, @@ -32,9 +34,10 @@ pub enum Operation { Export, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(rename_all = "camelCase")] #[schemars(transform = idiomaticize_string_enum)] +#[dsc_repo_schema(base_name = "executionType", folder_path = "metadata/Microsoft.DSC")] pub enum ExecutionKind { Actual, WhatIf, @@ -47,9 +50,10 @@ pub struct Process { pub id: u32, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(rename_all = "camelCase")] #[schemars(transform = idiomaticize_externally_tagged_enum)] +#[dsc_repo_schema(base_name = "restartRequired", folder_path = "metadata/Microsoft.DSC")] pub enum RestartRequired { System(String), Service(String), @@ -104,7 +108,8 @@ impl MicrosoftDscMetadata { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "document.metadata", folder_path = "config")] pub struct Metadata { #[serde(rename = "Microsoft.DSC", skip_serializing_if = "Option::is_none")] pub microsoft: Option, @@ -112,25 +117,29 @@ pub struct Metadata { pub other: Map, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "document.function", folder_path = "config")] pub struct UserFunction { pub namespace: String, pub members: HashMap, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "definition", folder_path = "definitions/functions/user")] pub struct UserFunctionDefinition { pub parameters: Option>, pub output: UserFunctionOutput, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "parameter", folder_path = "definitions/functions/user")] pub struct UserFunctionParameter { pub name: String, pub r#type: DataType, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "output", folder_path = "definitions/functions/user")] pub struct UserFunctionOutput { pub r#type: DataType, pub value: String, @@ -143,8 +152,9 @@ pub enum ValueOrCopy { Copy(Copy), } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "document.output", folder_path = "config")] pub struct Output { pub condition: Option, pub r#type: DataType, @@ -152,8 +162,18 @@ pub struct Output { pub value_or_copy: ValueOrCopy, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema( + base_name = "document", + folder_path = "config", + should_bundle = true, + schema_field( + name = schema, + title = t!("configure.config_doc.configurationDocumentSchemaTitle"), + description = t!("configure.config_doc.configurationDocumentSchemaDescription"), + ) +)] pub struct Configuration { #[serde(rename = "$schema")] #[schemars(schema_with = "Configuration::recognized_schema_uris_subschema")] @@ -222,8 +242,9 @@ where } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "document.parameter", folder_path = "config")] pub struct Parameter { #[serde(rename = "type")] pub parameter_type: DataType, @@ -245,8 +266,9 @@ pub struct Parameter { pub metadata: Option>, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[schemars(transform = idiomaticize_string_enum)] +#[dsc_repo_schema(base_name = "dataTypes", folder_path = "definitions/parameters")] pub enum DataType { #[serde(rename = "string")] String, @@ -353,8 +375,9 @@ pub struct Sku { pub capacity: Option, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "document.resource", folder_path = "config")] pub struct Resource { #[serde(skip_serializing_if = "Option::is_none")] pub condition: Option, @@ -406,30 +429,6 @@ impl Default for Configuration { } } -impl DscRepoSchema for Configuration { - const SCHEMA_FILE_BASE_NAME: &'static str = "document"; - const SCHEMA_FOLDER_PATH: &'static str = "config"; - const SCHEMA_SHOULD_BUNDLE: bool = true; - - fn schema_property_metadata() -> schemars::Schema { - json_schema!({ - "title": t!("configure.config_doc.configurationDocumentSchemaTitle").to_string(), - "description": t!("configure.config_doc.configurationDocumentSchemaDescription").to_string(), - }) - } - - fn validate_schema_uri(&self) -> Result<(), UnrecognizedSchemaUri> { - if Self::is_recognized_schema_uri(&self.schema) { - Ok(()) - } else { - Err(UnrecognizedSchemaUri( - self.schema.clone(), - Self::recognized_schema_uris(), - )) - } - } -} - impl Configuration { #[must_use] pub fn new() -> Self { diff --git a/lib/dsc-lib/src/configure/config_result.rs b/lib/dsc-lib/src/configure/config_result.rs index 3bce3860d..31b7c2a84 100644 --- a/lib/dsc-lib/src/configure/config_result.rs +++ b/lib/dsc-lib/src/configure/config_result.rs @@ -7,7 +7,7 @@ use serde_json::{Map, Value}; use crate::dscresources::invoke_result::{GetResult, SetResult, TestResult}; use crate::configure::config_doc::{Configuration, Metadata}; -use crate::schemas::transforms::idiomaticize_string_enum; +use crate::schemas::{dsc_repo::DscRepoSchema, transforms::idiomaticize_string_enum}; #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(rename_all = "camelCase")] @@ -18,8 +18,9 @@ pub enum MessageLevel { Information, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "message", folder_path = "definitions")] pub struct ResourceMessage { pub name: String, #[serde(rename="type")] @@ -28,8 +29,9 @@ pub struct ResourceMessage { pub level: MessageLevel, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "get.full", folder_path = "outputs/resource")] pub struct ResourceGetResult { #[serde(skip_serializing_if = "Option::is_none")] pub metadata: Option, @@ -50,8 +52,9 @@ impl From for ResourceGetResult { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "get", folder_path = "outputs/config")] pub struct ConfigurationGetResult { pub metadata: Option, pub results: Vec, @@ -97,8 +100,9 @@ impl From for ConfigurationGetResult { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "set.full", folder_path = "outputs/resource")] pub struct ResourceSetResult { #[serde(skip_serializing_if = "Option::is_none")] pub metadata: Option, @@ -140,8 +144,9 @@ impl Default for GroupResourceSetResult { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "set", folder_path = "outputs/config")] pub struct ConfigurationSetResult { pub metadata: Option, pub results: Vec, @@ -171,8 +176,9 @@ impl Default for ConfigurationSetResult { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "test.full", folder_path = "outputs/resource")] pub struct ResourceTestResult { #[serde(skip_serializing_if = "Option::is_none")] pub metadata: Option, @@ -203,8 +209,9 @@ impl Default for GroupResourceTestResult { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "test", folder_path = "outputs/config")] pub struct ConfigurationTestResult { pub metadata: Option, pub results: Vec, @@ -234,8 +241,9 @@ impl Default for ConfigurationTestResult { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "export", folder_path = "outputs/config")] pub struct ConfigurationExportResult { pub metadata: Option, pub result: Option, diff --git a/lib/dsc-lib/src/dscresources/dscresource.rs b/lib/dsc-lib/src/dscresources/dscresource.rs index cf230c231..afb2612a8 100644 --- a/lib/dsc-lib/src/dscresources/dscresource.rs +++ b/lib/dsc-lib/src/dscresources/dscresource.rs @@ -14,6 +14,8 @@ use std::collections::HashMap; use std::path::PathBuf; use tracing::{debug, info, trace}; +use crate::schemas::dsc_repo::DscRepoSchema; + use super::{ command_resource, dscerror, @@ -25,8 +27,9 @@ use super::{ } }; -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "list", folder_path = "outputs/resource")] pub struct DscResource { /// The namespaced name of the resource. #[serde(rename="type")] @@ -59,9 +62,10 @@ pub struct DscResource { pub manifest: Option, } -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(rename_all = "camelCase")] #[schemars(transform = idiomaticize_string_enum)] +#[dsc_repo_schema(base_name = "resourceCapabilities", folder_path = "definitions")] pub enum Capability { /// The resource supports retrieving configuration. Get, diff --git a/lib/dsc-lib/src/dscresources/invoke_result.rs b/lib/dsc-lib/src/dscresources/invoke_result.rs index 9d7404a12..c1170c781 100644 --- a/lib/dsc-lib/src/dscresources/invoke_result.rs +++ b/lib/dsc-lib/src/dscresources/invoke_result.rs @@ -6,9 +6,11 @@ use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; use crate::configure::config_result::{ResourceGetResult, ResourceSetResult, ResourceTestResult}; +use crate::schemas::dsc_repo::DscRepoSchema; -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(untagged)] +#[dsc_repo_schema(base_name = "get", folder_path = "outputs/resource")] pub enum GetResult { Resource(ResourceGetResponse), Group(Vec), @@ -33,16 +35,18 @@ impl From for GetResult { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "get.simple", folder_path = "outputs/resource")] pub struct ResourceGetResponse { /// The state of the resource as it was returned by the Get method. #[serde(rename = "actualState")] pub actual_state: Value, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(untagged)] +#[dsc_repo_schema(base_name = "set", folder_path = "outputs/resource")] pub enum SetResult { Resource(ResourceSetResponse), Group(Vec), @@ -69,8 +73,9 @@ impl From for SetResult { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "set.simple", folder_path = "outputs/resource")] pub struct ResourceSetResponse { /// The state of the resource as it was before the Set method was called. #[serde(rename = "beforeState")] @@ -83,8 +88,9 @@ pub struct ResourceSetResponse { pub changed_properties: Option>, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(untagged)] +#[dsc_repo_schema(base_name = "test", folder_path = "outputs/resource")] pub enum TestResult { Resource(ResourceTestResponse), Group(Vec), @@ -107,8 +113,9 @@ pub fn get_in_desired_state(test_result: &TestResult) -> bool { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "test.simple", folder_path = "outputs/resource")] pub struct ResourceTestResponse { /// The state of the resource as it was expected to be. #[serde(rename = "desiredState")] @@ -124,8 +131,9 @@ pub struct ResourceTestResponse { pub diff_properties: Vec, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "validate", folder_path = "outputs/resource")] pub struct ValidateResult { /// Whether the supplied configuration is valid. pub valid: bool, @@ -133,16 +141,18 @@ pub struct ValidateResult { pub reason: Option, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "export", folder_path = "outputs/resource")] pub struct ExportResult { /// The state of the resource as it was returned by the Export method. #[serde(rename = "actualState")] pub actual_state: Vec, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "resolve", folder_path = "outputs/resource")] pub struct ResolveResult { /// The resolved configuration. pub configuration: Value, diff --git a/lib/dsc-lib/src/dscresources/resource_manifest.rs b/lib/dsc-lib/src/dscresources/resource_manifest.rs index 513d9b2b2..4e9a19925 100644 --- a/lib/dsc-lib/src/dscresources/resource_manifest.rs +++ b/lib/dsc-lib/src/dscresources/resource_manifest.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. use rust_i18n::t; -use schemars::{Schema, JsonSchema, json_schema}; +use schemars::JsonSchema; use semver::Version; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; @@ -10,12 +10,13 @@ use std::collections::HashMap; use crate::{ dscerror::DscError, - schemas::{dsc_repo::{DscRepoSchema, UnrecognizedSchemaUri}, transforms::idiomaticize_string_enum}, + schemas::{dsc_repo::DscRepoSchema, transforms::idiomaticize_string_enum}, }; -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(rename_all = "camelCase")] #[schemars(transform = idiomaticize_string_enum)] +#[dsc_repo_schema(base_name = "resourceKind", folder_path = "definitions")] pub enum Kind { Adapter, Exporter, @@ -24,8 +25,18 @@ pub enum Kind { Resource, } -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema( + base_name = "manifest", + folder_path = "resource", + should_bundle = true, + schema_field( + name = schema_version, + title = t!("dscresources.resource_manifest.resourceManifestSchemaTitle"), + description = t!("dscresources.resource_manifest.resourceManifestSchemaDescription"), + ) +)] pub struct ResourceManifest { /// The version of the resource manifest schema. #[serde(rename = "$schema")] @@ -82,8 +93,9 @@ pub struct ResourceManifest { pub metadata: Option>, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(untagged)] +#[dsc_repo_schema(base_name = "commandArgs", folder_path = "definitions")] pub enum ArgKind { /// The argument is a string. String(String), @@ -102,8 +114,9 @@ pub enum ArgKind { } } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[schemars(transform = idiomaticize_string_enum)] +#[dsc_repo_schema(base_name = "inputKind", folder_path = "definitions")] pub enum InputKind { /// The input is accepted as environmental variables. #[serde(rename = "env")] @@ -113,7 +126,8 @@ pub enum InputKind { Stdin, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.schema", folder_path = "resource")] pub enum SchemaKind { /// The schema is returned by running a command. #[serde(rename = "command")] @@ -131,8 +145,9 @@ pub struct SchemaCommand { pub args: Option>, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[schemars(transform = idiomaticize_string_enum)] +#[dsc_repo_schema(base_name = "returnKind", folder_path = "definitions")] pub enum ReturnKind { /// The return JSON is the state of the resource. #[serde(rename = "state")] @@ -142,7 +157,8 @@ pub enum ReturnKind { StateAndDiff, } -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.get", folder_path = "resource")] pub struct GetMethod { /// The command to run to get the state of the resource. pub executable: String, @@ -153,7 +169,8 @@ pub struct GetMethod { pub input: Option, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.set", folder_path = "resource")] pub struct SetMethod { /// The command to run to set the state of the resource. pub executable: String, @@ -172,7 +189,8 @@ pub struct SetMethod { pub returns: Option, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.test", folder_path = "resource")] pub struct TestMethod { /// The command to run to test the state of the resource. pub executable: String, @@ -185,7 +203,8 @@ pub struct TestMethod { pub returns: Option, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.delete", folder_path = "resource")] pub struct DeleteMethod { /// The command to run to delete the state of the resource. pub executable: String, @@ -195,7 +214,8 @@ pub struct DeleteMethod { pub input: Option, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.validate", folder_path = "resource")] pub struct ValidateMethod { // TODO: enable validation via schema or command /// The command to run to validate the state of the resource. pub executable: String, @@ -205,7 +225,8 @@ pub struct ValidateMethod { // TODO: enable validation via schema or command pub input: Option, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.export", folder_path = "resource")] pub struct ExportMethod { /// The command to run to enumerate instances of the resource. pub executable: String, @@ -215,7 +236,8 @@ pub struct ExportMethod { pub input: Option, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.resolve", folder_path = "resource")] pub struct ResolveMethod { /// The command to run to enumerate instances of the resource. pub executable: String, @@ -225,7 +247,8 @@ pub struct ResolveMethod { pub input: Option, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.adapter", folder_path = "resource")] pub struct Adapter { /// The way to list adapter supported resources. pub list: ListMethod, @@ -256,30 +279,6 @@ pub struct ListMethod { pub args: Option>, } -impl DscRepoSchema for ResourceManifest { - const SCHEMA_FILE_BASE_NAME: &'static str = "manifest"; - const SCHEMA_FOLDER_PATH: &'static str = "resource"; - const SCHEMA_SHOULD_BUNDLE: bool = true; - - fn schema_property_metadata() -> Schema { - json_schema!({ - "title": t!("dscresources.resource_manifest.resourceManifestSchemaTitle").to_string(), - "description": t!("dscresources.resource_manifest.resourceManifestSchemaDescription").to_string(), - }) - } - - fn validate_schema_uri(&self) -> Result<(), UnrecognizedSchemaUri> { - if Self::is_recognized_schema_uri(&self.schema_version) { - Ok(()) - } else { - Err(UnrecognizedSchemaUri( - self.schema_version.clone(), - Self::recognized_schema_uris(), - )) - } - } -} - /// Import a resource manifest from a JSON value. /// /// # Arguments diff --git a/lib/dsc-lib/src/extensions/discover.rs b/lib/dsc-lib/src/extensions/discover.rs index 6efb91abb..11128669c 100644 --- a/lib/dsc-lib/src/extensions/discover.rs +++ b/lib/dsc-lib/src/extensions/discover.rs @@ -20,6 +20,7 @@ use crate::{ }, extension_manifest::ExtensionManifest, }, + schemas::dsc_repo::DscRepoSchema }; use rust_i18n::t; use schemars::JsonSchema; @@ -27,7 +28,8 @@ use serde::{Deserialize, Serialize}; use std::path::PathBuf; use tracing::{info, trace}; -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.discover", folder_path = "extension")] pub struct DiscoverMethod { /// The command to run to get the state of the resource. pub executable: String, @@ -35,7 +37,8 @@ pub struct DiscoverMethod { pub args: Option>, } -#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "discover", folder_path = "extension/stdout")] pub struct DiscoverResult { /// The path to the resource manifest, must be absolute. #[serde(rename = "manifestPath")] diff --git a/lib/dsc-lib/src/extensions/dscextension.rs b/lib/dsc-lib/src/extensions/dscextension.rs index cc21002a9..379dedcb6 100644 --- a/lib/dsc-lib/src/extensions/dscextension.rs +++ b/lib/dsc-lib/src/extensions/dscextension.rs @@ -2,15 +2,16 @@ // Licensed under the MIT License. use crate::extensions::import::ImportMethod; -use crate::schemas::transforms::idiomaticize_string_enum; +use crate::schemas::{dsc_repo::DscRepoSchema, transforms::idiomaticize_string_enum}; use serde::{Deserialize, Serialize}; use serde_json::Value; use schemars::JsonSchema; use std::fmt::Display; use std::path::PathBuf; -#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)] +#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "list", folder_path = "outputs/extension")] pub struct DscExtension { /// The namespaced name of the resource. #[serde(rename="type")] @@ -33,9 +34,10 @@ pub struct DscExtension { pub manifest: Value, } -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(rename_all = "camelCase")] #[schemars(transform = idiomaticize_string_enum)] +#[dsc_repo_schema(base_name = "extensionCapabilities", folder_path = "definitions")] pub enum Capability { /// The extension aids in discovering resources. Discover, diff --git a/lib/dsc-lib/src/extensions/extension_manifest.rs b/lib/dsc-lib/src/extensions/extension_manifest.rs index 1337d48b6..59c95e979 100644 --- a/lib/dsc-lib/src/extensions/extension_manifest.rs +++ b/lib/dsc-lib/src/extensions/extension_manifest.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. use rust_i18n::t; -use schemars::{Schema, JsonSchema, json_schema}; +use schemars::JsonSchema; use semver::Version; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; @@ -10,10 +10,20 @@ use std::collections::HashMap; use crate::dscerror::DscError; use crate::extensions::{discover::DiscoverMethod, import::ImportMethod, secret::SecretMethod}; -use crate::schemas::dsc_repo::{DscRepoSchema, UnrecognizedSchemaUri}; +use crate::schemas::dsc_repo::DscRepoSchema; -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema( + base_name = "manifest", + folder_path = "extension", + should_bundle = true, + schema_field( + name = schema_version, + title = t!("extensions.extension_manifest.extensionManifestSchemaTitle"), + description = t!("extensions.extension_manifest.extensionManifestSchemaDescription"), + ) +)] pub struct ExtensionManifest { /// The version of the extension manifest schema. #[serde(rename = "$schema")] @@ -47,30 +57,6 @@ pub struct ExtensionManifest { pub metadata: Option>, } -impl DscRepoSchema for ExtensionManifest { - const SCHEMA_FILE_BASE_NAME: &'static str = "manifest"; - const SCHEMA_FOLDER_PATH: &'static str = "extension"; - const SCHEMA_SHOULD_BUNDLE: bool = true; - - fn schema_property_metadata() -> Schema { - json_schema!({ - "title": t!("extensions.extension_manifest.extensionManifestSchemaTitle").to_string(), - "description": t!("extensions.extension_manifest.extensionManifestSchemaDescription").to_string(), - }) - } - - fn validate_schema_uri(&self) -> Result<(), UnrecognizedSchemaUri> { - if Self::is_recognized_schema_uri(&self.schema_version) { - Ok(()) - } else { - Err(UnrecognizedSchemaUri( - self.schema_version.clone(), - Self::recognized_schema_uris(), - )) - } - } -} - /// Import a resource manifest from a JSON value. /// /// # Arguments diff --git a/lib/dsc-lib/src/extensions/import.rs b/lib/dsc-lib/src/extensions/import.rs index d164eda69..d456c235a 100644 --- a/lib/dsc-lib/src/extensions/import.rs +++ b/lib/dsc-lib/src/extensions/import.rs @@ -8,8 +8,11 @@ use crate::{ DscExtension, }, extension_manifest::ExtensionManifest, - }, parser::Statement + }, + parser::Statement, + schemas::dsc_repo::DscRepoSchema }; + use path_absolutize::Absolutize; use rust_i18n::t; use schemars::JsonSchema; @@ -17,7 +20,8 @@ use serde::{Deserialize, Serialize}; use std::path::Path; use tracing::{debug, info}; -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.import", folder_path = "extension")] pub struct ImportMethod { /// The extensions to import. #[serde(rename = "fileExtensions")] diff --git a/lib/dsc-lib/src/extensions/secret.rs b/lib/dsc-lib/src/extensions/secret.rs index 5af7caa48..819620b07 100644 --- a/lib/dsc-lib/src/extensions/secret.rs +++ b/lib/dsc-lib/src/extensions/secret.rs @@ -13,7 +13,9 @@ use crate::{ }, extension_manifest::ExtensionManifest, }, + schemas::dsc_repo::DscRepoSchema }; + use rust_i18n::t; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; @@ -38,7 +40,8 @@ pub enum SecretArgKind { }, } -#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "manifest.secret", folder_path = "extension")] pub struct SecretMethod { /// The command to run to get the state of the resource. pub executable: String, diff --git a/lib/dsc-lib/src/functions/mod.rs b/lib/dsc-lib/src/functions/mod.rs index 0ab82e7f1..da75c6ec1 100644 --- a/lib/dsc-lib/src/functions/mod.rs +++ b/lib/dsc-lib/src/functions/mod.rs @@ -6,6 +6,7 @@ use std::collections::HashMap; use crate::DscError; use crate::configure::context::Context; use crate::functions::user_function::invoke_user_function; +use crate::schemas::dsc_repo::DscRepoSchema; use rust_i18n::t; use schemars::JsonSchema; use serde::Serialize; @@ -89,7 +90,8 @@ pub mod variables; pub mod try_which; /// The kind of argument that a function accepts. -#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, JsonSchema, DscRepoSchema)] +#[dsc_repo_schema(base_name = "argKind", folder_path = "definitions/functions/builtin")] pub enum FunctionArgKind { Array, Boolean, @@ -330,8 +332,9 @@ impl Default for FunctionDispatcher { } } -#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "list", folder_path = "outputs/function")] pub struct FunctionDefinition { pub category: Vec, pub name: String, @@ -348,8 +351,9 @@ pub struct FunctionDefinition { pub return_types: Vec, } -#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, JsonSchema)] +#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Serialize, JsonSchema, DscRepoSchema)] #[serde(deny_unknown_fields)] +#[dsc_repo_schema(base_name = "category", folder_path = "definitions/functions/builtin")] pub enum FunctionCategory { Array, Cidr,