Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
29 changes: 27 additions & 2 deletions Sources/SwiftDocC/Model/Rendering/SemanticVersion.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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] = []
Expand Down
13 changes: 12 additions & 1 deletion Tests/SwiftDocCTests/Model/SemaToRenderNodeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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),
]

Expand Down
22 changes: 21 additions & 1 deletion Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down