(GH-538) Define newtypes for versions and version requirements#1350
Open
michaeltlombardi wants to merge 6 commits intoPowerShell:mainfrom
Open
(GH-538) Define newtypes for versions and version requirements#1350michaeltlombardi wants to merge 6 commits intoPowerShell:mainfrom
michaeltlombardi wants to merge 6 commits intoPowerShell:mainfrom
Conversation
170bfe1 to
5955b07
Compare
5955b07 to
96257ea
Compare
TypeVersion as newtype96257ea to
1a8a212
Compare
3d407f5 to
58f8957
Compare
58f8957 to
308b03f
Compare
308b03f to
4e08c76
Compare
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 16 out of 17 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
4e08c76 to
0588c30
Compare
Prior to this change, the `version` fields in various types for `dsc-lib` used `String`. In order to correctly present the JSON Schema for a semantic version, the library needs a reusable type. This change: - Defines the `SemanticVersion` newtype as a wrapper around the `semver::Version` type. - Implements methods for creating instances of the type from strings and version segments, mirroring the implementation for `semver::Version`. - Implements traits for comparing instances of the type to strings and `semver::Version`. - Defines the JSON Schema for the type with a validation pattern and VS Code vocabulary keywords for documentation. - Adds integration tests for the type's behavior. This change doesn't modify any existing code. Updating types in the library to use `SemanticVersion` must be done in a follow up change.
Prior to this change, `dsc-lib` used the `semver::VersionReq` type for pinning versions of resources and extensions. To provide better schema values for specifying semantic version requirements, we need to Define a newtype so we can modify and extend the JSON Schema. This change: - Defines the `SemanticVersionReq` newtype as a wrapper for `semver::VersionReq` and implements traits for conversion and comparison. The wrapping type has stricter validation requirements over the underlying type, given the usage and context for DSC. - Provides thorough documentation with the understanding that this will provide context to the maintainers, to integrating developers working with the library, and eventually be hoisted to CLI user documentation when discussing how to define version requirements in configuration documents. This change does not modify any code in the library to use the wrapping type instead of the underlying type. That will be accomplished in a future changeset.
Prior to this change, the version fields for DSC used an arbitrary `String` for both resources and extensions. The generated schema for those types then reports that any string is valid and DSC must check every time it needs to process the version for whether the version is semantic or an arbitrary string. This change follows the Rust "parse, don't validate pattern" by defining a `ResourceVersion` enum with the `Semantic` and `String` variants. If the version can be parsed as a semantic version, the type creates it as an instance of `ResourceVersion::Semantic` and otherwise creates it as `ResourceVersion::Arbitrary`. The `ResourceVersion` enum implements several conversion and comparison traits to make using the newtype more ergonomic. It also defines helper methods `is_arbitrary()`, `is_semver()` and `matches_semver_req()` for easier usage. When comparing an instance of `ResourceVersion`: - A semantic version is always greater than an arbitrary string version. This applies both when comparing `ResourceVersion::Arbitrary` instances to `ResourceVersion::Semantic` and to `semver::Version`. - Arbitrary string version comparisons use Rust's underlying [lexicographic comparison logic][01]. Arbitrary string versions are only equivalent when the strings are exactly the same. If the strings differ by case, spacing, or any other characters, they are unequal. Because ordering is lexicographic, arbitrary string version `Foo` is greater than both `foo` and `Bar`. - You can directly compare instances of `ResourceVersion` to string slices and instances of `ResourceVersion`, `SemanticVersion`, `String`, and `semver::Version`. - The trait implementations support using `==`, `>`, and `<` operators for easier reading. The newtype overhauls the JSON Schema for resource versions to help users get better validation and IntelliSense when authoring manifests. Finally, this change adds comprehensive integration tests for the newtype and its implementations as well as documentation for the type and its public methods.
Prior to this change, the `apiVersion` field for resource instances in configuration documents used the rust type `Option<String>` to represent a version pin for a DSC Resource. This change: - Defines the `ResourceVersionReq` enum, which can contain either a `SemanticVersionReq` or an arbitrary string. Like the `ResourceVersion` enum, this enables us to distinguish between resources that are semantically versioned and those using an arbitrary string version. If the string can be parsed as a semantic version requirement the created instance is the `Semantic` variant and otherwise `Arbitrary`. - Implements the `matches` method to compare an instance of `ResourceVersion` against a version requirement. When both the version and requirement are semantic, it uses the underlying implementation for semantic version requirements in `semver::VersionReq`. When either the version or requirement is semantic and the other is arbitrary the match fails. When both the version and requirement are arbitrary, the version matches the requirement only when the strings are equal. The comparison is case sensitive. - Implements traits for converting and comparing instances of `ResourceVersionReq` to string slices and instances of `String` and `SemanticVersionReq`. - Implements the `JsonSchema` and `DscRepoSchema` traits for the newtype to enable sharing a canonical JSON Schema for this field.
0588c30 to
5c29625
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
PR Summary
This change follows the Rust "parse, don't validate pattern" by defining the following newtypes:
SemanticVersionas a wrapper aroundsemver::Versionto enable a fully controlled JSON Schema for semantic versions.SemanticVersionReqas a wrapper aroundsemver::VersionReqboth to enable providing a full JSON Schema for semantic version requirements and to apply stricter standards for parsing the type in the context of DSC.ResourceVersionas an enum with theSemanticandArbitraryvariants to support DSC resources with semantic versions (preferred and recommended for resource authors) and arbitrary strings (required for compatibility purposes to enable versions like2026-02).ResourceVersionReqas an enum with theSemanticandArbitraryvariants to support defining version pinning for DSC resources with semantic and arbitrary version strings.Each newtype implements comparison and conversion traits to make using the types more ergonomic. The types are also defined with extensive reference documentation and integration tests to improve confidence and clarity.
Important notes for behavior:
When comparing instances of
ResourceVersion:ResourceVersion::Arbitraryinstances toResourceVersion::Semanticand toSemanticVersion.Fooandfooare not equal. Ordering comparisons are lexicographic.ResourceVersionto string slices,Stringinstances, andSemanticVersioninstances in addition to other instances ofResourceVersion.==,>, and<operators for easier reading.When matching a resource version to
ResourceVersionReq:semver::VersionReq.The
SemanticVersionReqis nearly identical tosemver::VersionReqexcept that:DSC semantic version requirements forbid the inclusion of build metadata.
Rust allows and ignores them. DSC forbids the inclusion of build metadata to limit confusion and unexpected behavior when specifying a version requirement for a DSC resource or extension.
DSC semantic version requirements must define a major version segment. All other segments are optional.
Rust technically supports specifying a wildcard-only version requirement (
*). DSC forbids specifying this version requirement as it maps to the default version selection and is discouraged when specifying version requirements for production systems.DSC semantic version requirements only support the asterisk (
*) character for wildcards, notxorX.PR Context
Prior to this change, the version and version requirement fields for DSC used an arbitrary
Stringfor both resources and extensions. The generated schema for those types then reports that any string is valid and DSC must check every time it needs to process the version for whether the version is semantic or an arbitrary string.Implementing strong types for these values enables DSC to parse the input strings once and reuse them as-needed throughout the code. It also ensures that we can provide canonical JSON Schemas for these fields.