-
Notifications
You must be signed in to change notification settings - Fork 37
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
[DNM] Seeking feedback for SemanticVersion
implementation
#12
[DNM] Seeking feedback for SemanticVersion
implementation
#12
Conversation
The logic is mostly pulled right from SwiftPM's `Version`'s and TSC's `Version`'s, with some improvements. Now semantic versions compare correctly, by taking into account the pre-release and build metadata identifiers. A lot of test cases are added. 1 initialiser is deprecated. TODO: disallow empty identifiers TODO: disallow leading zeros in numeric identifiers Note on `SemanticVersion`'s `Codable` conformance: empty arrays of pre-release and build metadata identifiers are not encoded, for round-trip equality empty strings in pre-release and build metadata identifiers are encoded, for round-trip equality and consistency with SwiftPM and TSC pre-release and build metadata arrays containing single empty string precede empty arrays in comparison, for consistency with SwiftPM and TSC. SemVer 2.0.0 is ambiguous about this. Can do follow-up commit/pr for this. Not guaranteed that SwiftPM and TSC can accept an alternative comparison rule. proceed to next pair of identifiers if current pair is numerically equal
docc didn't exactly conform `SemanticVersion` to `LosslessStringConvertible`, but it extended it with all the functionalities.
Tests have not been updated from the previous commit, and there are still lots of documentation missing. But because of the renaming of so many files upstream, it's now difficult to rebase the changes here. Going to open a WIP PR as is for feedback, and reapply the changes at the current upstream head.
extension SymbolGraph.SemanticVersion: Comparable { | ||
// Although `Comparable` inherits from `Equatable`, it does not provide a new default implementation of `==`, but instead uses `Equatable`'s default synthesised implementation. The compiler-synthesised `==`` is composed of [member-wise comparisons](https://github.com/apple/swift-evolution/blob/main/proposals/0185-synthesize-equatable-hashable.md#implementation-details), which leads to a false `false` when 2 semantic versions differ by only their build metadata identifiers, contradicting SemVer 2.0.0's [comparison rules](https://semver.org/#spec-item-10). | ||
/// <#Description#> | ||
/// - Parameters: | ||
/// - lhs: <#lhs description#> | ||
/// - rhs: <#rhs description#> | ||
/// - Returns: <#description#> | ||
@inlinable | ||
public static func == (lhs: Self, rhs: Self) -> Bool { | ||
!(lhs < rhs) && !(lhs > rhs) | ||
} | ||
|
||
/// <#Description#> | ||
/// - Parameters: | ||
/// - lhs: <#lhs description#> | ||
/// - rhs: <#rhs description#> | ||
/// - Returns: <#description#> | ||
public static func < (lhs: Self, rhs: Self) -> Bool { | ||
let lhsVersionCore = [lhs.major, lhs.minor, lhs.patch] | ||
let rhsVersionCore = [rhs.major, rhs.minor, rhs.patch] | ||
|
||
guard lhsVersionCore == rhsVersionCore else { | ||
return lhsVersionCore.lexicographicallyPrecedes(rhsVersionCore) | ||
} | ||
|
||
return lhs.prerelease < rhs.prerelease // not lexicographically compared | ||
} | ||
} |
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.
I think one of the most important changes is the Comparable
conformance. SemVer 2.0.0 specifies that pre-release participates in version comparison, but build metadata doesn't.
Actually right when I'm writing this right now, I just realised that <
can be re-implemented like this:
(lhs.major, lhs.minor, lhs.patch, lhs.prerelease) < (rhs.major, rhs.minor, rhs.patch, rhs.prerelease)
extension SymbolGraph.SemanticVersion { | ||
/// A storage for pre-release identifiers. | ||
internal struct Prerelease { | ||
/// The identifiers. | ||
internal let identifiers: [Identifier] | ||
/// A pre-release identifier. | ||
internal enum Identifier { | ||
/// A numeric pre-release identifier. | ||
/// - Parameter identifier: The identifier. | ||
case numeric(_ identifier: Int) | ||
/// An alphanumeric pre-release identifier. | ||
/// - Parameter identifier: The identifier. | ||
case alphanumeric(_ identifier: String) | ||
} | ||
} | ||
} |
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.
Because the precedence between pre-release identifiers depends on whether they're numeric, their numeric-ness is recorded this way right after each identifier is validated during the version's initialization.
closing this because it's superseded by #36 |
The implementation is largely borrowed from SwiftPM's and TSC's
Version
(at least as of the penultimate commit in this PR), but with some changes that make it fully compliant to SemVer 2.0.0. I intend to apply the roughly same changes (the parts that make sense to each) to SwiftPM's, TSC's, and docc's too.It's not fully implemented yet (test cases are outdated (and should be ignored for now) and lots of documentation is missing), but the logic is basically done. Because of the large number of files renamed in the "main" branch, it's difficult to rebase my branch, so even though it's not finished, I'm putting it up here for some feedback.
I estimate that it will probably take another month (but likely sooner) before I get everything ready.