-
Notifications
You must be signed in to change notification settings - Fork 57
(GH-538) Define newtypes for versions and version requirements #1350
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(GH-538) Define newtypes for versions and version requirements #1350
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
SteveL-MSFT
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some high quality work here @michaeltlombardi !
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.