diff --git a/Sources/SwiftDocC/Model/Rendering/DocumentationContentRenderer.swift b/Sources/SwiftDocC/Model/Rendering/DocumentationContentRenderer.swift index 1879a8b4b9..b25c4ea94c 100644 --- a/Sources/SwiftDocC/Model/Rendering/DocumentationContentRenderer.swift +++ b/Sources/SwiftDocC/Model/Rendering/DocumentationContentRenderer.swift @@ -230,7 +230,7 @@ public class DocumentationContentRenderer { } // Verify that the current platform is in beta and the version number matches the introduced platform version. - guard current.beta && SemanticVersion(introduced).isEqualToVersionTriplet(current.version) else { + guard current.beta && SemanticVersion(introduced) >= SemanticVersion(versionTriplet: current.version) else { return false } } diff --git a/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift b/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift index 5a3ba8182f..a3ba316f06 100644 --- a/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift +++ b/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift @@ -1762,7 +1762,8 @@ public struct RenderNodeTranslator: SemanticVisitor { // Build a module availability version, defaulting the patch number to 0 if not provided (e.g. 10.15) let moduleVersionTriplet = VersionTriplet(moduleVersion[0], moduleVersion[1], moduleVersion.count > 2 ? moduleVersion[2] : 0) - return moduleVersionTriplet == targetPlatformVersion.version + // Consider the module beta if its version is greater than or equal to the target platform + return moduleVersionTriplet >= targetPlatformVersion.version } /// The default availability for modules in a given bundle and module. diff --git a/Sources/SwiftDocC/Model/Rendering/SemanticVersion.swift b/Sources/SwiftDocC/Model/Rendering/SemanticVersion.swift index e68590b494..ae4ff5a5ff 100644 --- a/Sources/SwiftDocC/Model/Rendering/SemanticVersion.swift +++ b/Sources/SwiftDocC/Model/Rendering/SemanticVersion.swift @@ -11,8 +11,8 @@ /// A semantic version. /// /// A version that follows the [Semantic Versioning](https://semver.org) specification. -public struct SemanticVersion: Codable, Equatable, CustomStringConvertible { - +public struct SemanticVersion: Codable, Equatable, Comparable, CustomStringConvertible { + /// The major version number. /// /// For example, the `1` in `1.2.3` @@ -51,6 +51,31 @@ public struct SemanticVersion: Codable, Equatable, CustomStringConvertible { self.buildMetadata = try container.decodeIfPresent(String.self, forKey: .buildMetadata) } + // TODO: https://github.com/swiftlang/swift-docc/issues/970 + // Migrate all the code to use semantic versions, and not version triplets. + /// Create a semantic version from a version triplet. + init(versionTriplet: VersionTriplet) { + self.major = versionTriplet.major + self.minor = versionTriplet.minor + self.patch = versionTriplet.patch + } + + /// Compare one semantic version with another. + /// + /// - Parameters: + /// - lhs: A version to compare. + /// - rhs: Another version to compare. + /// + /// - Returns: a Boolean value that indicates whether the first version is less than the second version. + public static func < (lhs: SemanticVersion, rhs: SemanticVersion) -> Bool { + if lhs.major != rhs.major { return lhs.major < rhs.major } + if lhs.minor != rhs.minor { return lhs.minor < rhs.minor } + if lhs.patch != rhs.patch { return lhs.patch < rhs.patch } + // Note: don't compare the values of prerelease, even if it is + // present in both semantic versions. + return false // The version are equal + } + public var description: String { var result = "\(major).\(minor).\(patch)" if let prerelease { diff --git a/Sources/SwiftDocC/Model/Rendering/Symbol/AvailabilityRenderMetadataItem.swift b/Sources/SwiftDocC/Model/Rendering/Symbol/AvailabilityRenderMetadataItem.swift index 5883fde724..e2a3d12749 100644 --- a/Sources/SwiftDocC/Model/Rendering/Symbol/AvailabilityRenderMetadataItem.swift +++ b/Sources/SwiftDocC/Model/Rendering/Symbol/AvailabilityRenderMetadataItem.swift @@ -52,15 +52,6 @@ extension SemanticVersion { self.prerelease = semanticVersion.prerelease self.buildMetadata = semanticVersion.buildMetadata } - - /// Compares a version triplet to a semantic version. - /// - Parameter version: A version triplet to compare to this semantic version. - /// - Returns: Returns whether the given triple represents the same version as the current version. - func isEqualToVersionTriplet(_ version: VersionTriplet) -> Bool { - return major == version.major && - minor == version.minor && - patch == version.patch - } } /// Availability information of a symbol on a specific platform. @@ -154,11 +145,11 @@ public struct AvailabilityRenderItem: Codable, Hashable, Equatable { } private static func isBeta(introduced: SemanticVersion?, current: PlatformVersion?) -> Bool { - guard let introduced, let current, current.beta, introduced.isEqualToVersionTriplet(current.version) else { + guard let introduced, let current, current.beta else { return false } - - return true + + return introduced >= SemanticVersion(versionTriplet: current.version) } /// Creates a new item with the given platform name and version string. diff --git a/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift b/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift index e04b1e4137..08cc55131f 100644 --- a/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift +++ b/Sources/SwiftDocCUtilities/ArgumentParsing/Subcommands/Convert.swift @@ -148,6 +148,7 @@ extension Docc { help: ArgumentHelp("Specify information about the current release of a platform.", discussion: """ Each platform's information is specified via separate "--platform" values using the following format: "name={platform name},version={semantic version}". Optionally, the platform information can include a 'beta={true|false}' component. If no beta information is provided, the platform is considered not in beta. + If the platform is set to beta, any symbol introduced in a version equal to or greater than the specified semantic version will be marked as beta. """) ) var platforms: [String] = [] diff --git a/Tests/SwiftDocCTests/Model/SemaToRenderNodeTests.swift b/Tests/SwiftDocCTests/Model/SemaToRenderNodeTests.swift index a0f41fd04e..2cd3c98652 100644 --- a/Tests/SwiftDocCTests/Model/SemaToRenderNodeTests.swift +++ b/Tests/SwiftDocCTests/Model/SemaToRenderNodeTests.swift @@ -1947,6 +1947,17 @@ Document // Beta platform matching the introduced version context.externalMetadata.currentPlatforms = ["macOS": PlatformVersion(VersionTriplet(10, 15, 0), beta: true)] + + do { + var translator = RenderNodeTranslator(context: context, bundle: bundle, identifier: reference) + let renderNode = translator.visitSymbol(symbol) as! RenderNode + + // Verify platform beta was plumbed all the way to the render JSON + XCTAssertEqual(renderNode.metadata.platforms?.first?.isBeta, true) + } + + // Beta platform earlier than the introduced version + context.externalMetadata.currentPlatforms = ["macOS": PlatformVersion(VersionTriplet(10, 14, 0), beta: true)] do { var translator = RenderNodeTranslator(context: context, bundle: bundle, identifier: reference) @@ -1959,7 +1970,7 @@ Document // Set only some platforms to beta & the exact version MyClass is being introduced at context.externalMetadata.currentPlatforms = [ "macOS": PlatformVersion(VersionTriplet(10, 15, 0), beta: true), - "watchOS": PlatformVersion(VersionTriplet(3, 0, 0), beta: true), + "watchOS": PlatformVersion(VersionTriplet(9, 0, 0), beta: true), "tvOS": PlatformVersion(VersionTriplet(1, 0, 0), beta: true), ] diff --git a/Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift b/Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift index 1d0aea5a48..6662c6a555 100644 --- a/Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift +++ b/Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift @@ -106,12 +106,32 @@ class DefaultAvailabilityTests: XCTestCase { try? FileManager.default.removeItem(at: url.appendingPathComponent("Info.plist")) try? FileManager.default.copyItem(at: self.infoPlistAvailabilityURL, to: url.appendingPathComponent("Info.plist")) } - + // Set a beta status for the docs (which would normally be set via command line argument) context.externalMetadata.currentPlatforms = [ "macOS": PlatformVersion(VersionTriplet(10, 15, 1), beta: true), "Mac Catalyst": PlatformVersion(VersionTriplet(13, 5, 0), beta: true), ] + + // Test if the module availability is also "beta" for the "macOS" platform, + // verify that the Mac Catalyst platform's name (including a space) is rendered correctly + do { + let identifier = ResolvedTopicReference(bundleIdentifier: "org.swift.docc.example", path: "/documentation/MyKit", fragment: nil, sourceLanguage: .swift) + let node = try context.entity(with: identifier) + var translator = RenderNodeTranslator(context: context, bundle: bundle, identifier: node.reference) + let renderNode = translator.visit(node.semantic) as! RenderNode + + XCTAssertEqual(renderNode.metadata.platforms?.map({ "\($0.name ?? "") \($0.introduced ?? "")\($0.isBeta == true ? "(beta)" : "")" }).sorted(), [ + "Mac Catalyst 13.5(beta)", + "macOS 10.15.1(beta)", + ]) + } + + // Repeat the assertions, but use an earlier platform version this time + context.externalMetadata.currentPlatforms = [ + "macOS": PlatformVersion(VersionTriplet(10, 14, 1), beta: true), + "Mac Catalyst": PlatformVersion(VersionTriplet(13, 5, 0), beta: true), + ] // Test if the module availability is also "beta" for the "macOS" platform, // verify that the Mac Catalyst platform's name (including a space) is rendered correctly