Skip to content

Commit 2503c0d

Browse files
allow arbitrary platform strings in the @Available directive
rdar://104224866
1 parent f85fe77 commit 2503c0d

File tree

7 files changed

+100
-15
lines changed

7 files changed

+100
-15
lines changed

Sources/SwiftDocC/Model/Rendering/Symbol/AvailabilitySortOrder.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -23,6 +23,10 @@ enum AvailabilityRenderOrder {
2323
/// Sort two availability render items based on their platform name.
2424
static func compare(lhs: AvailabilityRenderItem, rhs: AvailabilityRenderItem) -> Bool {
2525
guard let lhsName = lhs.name, let rhsName = rhs.name else { return false }
26-
return platformsOrder[lhsName, default: Int.max] < platformsOrder[rhsName, default: Int.max]
26+
if platformsOrder.keys.contains(lhsName) || platformsOrder.keys.contains(rhsName) {
27+
return platformsOrder[lhsName, default: Int.max] < platformsOrder[rhsName, default: Int.max]
28+
} else {
29+
return lhsName < rhsName
30+
}
2731
}
2832
}

Sources/SwiftDocC/Semantics/Metadata/Availability.swift

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2022 Apple Inc. and the Swift project authors
4+
Copyright (c) 2022-2023 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -22,7 +22,15 @@ extension Metadata {
2222
/// @Available(macOS, introduced: "12.0")
2323
/// ```
2424
///
25-
/// The available platforms are `macOS`, `iOS`, `watchOS`, and `tvOS`.
25+
/// Any text can be given to the first argument, and will be displayed in the page's
26+
/// availability data. The platforms `iOS`, `macOS`, `watchOS`, and `tvOS` will be matched
27+
/// case-insensitively, but anything else will be printed verbatim.
28+
///
29+
/// To provide a platform name with spaces in it, provide it as a quoted string:
30+
///
31+
/// ```markdown
32+
/// @Available("My Package", introduced: "1.0")
33+
/// ```
2634
///
2735
/// This directive is available on both articles and documentation extension files. In extension
2836
/// files, the information overrides any information from the symbol itself.
@@ -38,19 +46,42 @@ extension Metadata {
3846
public final class Availability: Semantic, AutomaticDirectiveConvertible {
3947
static public let directiveName: String = "Available"
4048

41-
public enum Platform: String, RawRepresentable, CaseIterable, DirectiveArgumentValueConvertible {
49+
public enum Platform: RawRepresentable, Hashable, DirectiveArgumentValueConvertible {
4250
// FIXME: re-add `case any = "*"` when `isBeta` and `isDeprecated` are implemented
4351
// cf. https://github.com/apple/swift-docc/issues/441
4452
case macOS, iOS, watchOS, tvOS
4553

54+
case other(String)
55+
56+
static var defaultCases: [Platform] = [.macOS, .iOS, .watchOS, .tvOS]
57+
4658
public init?(rawValue: String) {
47-
for platform in Self.allCases {
59+
for platform in Self.defaultCases {
4860
if platform.rawValue.lowercased() == rawValue.lowercased() {
4961
self = platform
5062
return
5163
}
5264
}
53-
return nil
65+
if rawValue == "*" {
66+
// Reserve the `*` platform for when `isBeta` and `isDeprecated` can be implemented
67+
return nil
68+
} else {
69+
self = .other(rawValue)
70+
}
71+
}
72+
73+
public var rawValue: String {
74+
switch self {
75+
case .macOS: return "macOS"
76+
case .iOS: return "iOS"
77+
case .watchOS: return "watchOS"
78+
case .tvOS: return "tvOS"
79+
case .other(let platform): return platform
80+
}
81+
}
82+
83+
static func allowedValues() -> [String]? {
84+
nil
5485
}
5586
}
5687

Sources/SwiftDocC/Semantics/Symbol/PlatformName.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -109,6 +109,11 @@ public struct PlatformName: Codable, Hashable, Equatable {
109109
// Note: This is still an optional initializer to prevent source breakage when
110110
// `Availability.Platform` re-introduces the `.any` case
111111
// cf. https://github.com/apple/swift-docc/issues/441
112-
self = .init(operatingSystemName: platform.rawValue)
112+
if let knowDomain = Self.platformNamesIndex[platform.rawValue.lowercased()] {
113+
self = knowDomain
114+
} else {
115+
let identifier = platform.rawValue.lowercased().replacingOccurrences(of: " ", with: "")
116+
self.init(rawValue: identifier, displayName: platform.rawValue)
117+
}
113118
}
114119
}

Tests/SwiftDocCTests/Rendering/PlatformAvailabilityTests.swift

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2021 Apple Inc. and the Swift project authors
4+
Copyright (c) 2021-2023 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -106,4 +106,30 @@ class PlatformAvailabilityTests: XCTestCase {
106106
item.name == "watchOS" && item.introduced == "7.0"
107107
}))
108108
}
109+
110+
func testArbitraryPlatformAvailability() throws {
111+
let (bundle, context) = try testBundleAndContext(named: "AvailabilityBundle")
112+
let reference = ResolvedTopicReference(
113+
bundleIdentifier: bundle.identifier,
114+
path: "/documentation/AvailabilityBundle/ArbitraryPlatforms",
115+
sourceLanguage: .swift
116+
)
117+
let article = try XCTUnwrap(context.entity(with: reference).semantic as? Article)
118+
var translator = RenderNodeTranslator(
119+
context: context,
120+
bundle: bundle,
121+
identifier: reference,
122+
source: nil
123+
)
124+
let renderNode = try XCTUnwrap(translator.visitArticle(article) as? RenderNode)
125+
let availability = try XCTUnwrap(renderNode.metadata.platformsVariants.defaultValue)
126+
XCTAssertEqual(availability.count, 2)
127+
128+
XCTAssert(availability.contains(where: { item in
129+
item.name == "SomePackage" && item.introduced == "1.0"
130+
}))
131+
XCTAssert(availability.contains(where: { item in
132+
item.name == "My Package" && item.introduced == "2.0"
133+
}))
134+
}
109135
}

Tests/SwiftDocCTests/Semantics/MetadataAvailabilityTests.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
This source file is part of the Swift.org open source project
33

4-
Copyright (c) 2022 Apple Inc. and the Swift project authors
4+
Copyright (c) 2022-2023 Apple Inc. and the Swift project authors
55
Licensed under Apache License v2.0 with Runtime Library Exception
66

77
See https://swift.org/LICENSE.txt for license information
@@ -49,7 +49,7 @@ class MetadataAvailabilityTests: XCTestCase {
4949
}
5050
}
5151

52-
for platform in Metadata.Availability.Platform.allCases {
52+
for platform in Metadata.Availability.Platform.defaultCases {
5353
let source = """
5454
@Metadata {
5555
@Available(\(platform.rawValue), introduced: \"1.0\")
@@ -74,14 +74,22 @@ class MetadataAvailabilityTests: XCTestCase {
7474
validArgumentsWithVersion.append("introduced: \"1.0\", \(arg)")
7575
}
7676

77-
for platform in Metadata.Availability.Platform.allCases {
77+
var checkPlatforms = Metadata.Availability.Platform.defaultCases.map({ $0.rawValue })
78+
checkPlatforms.append("Package")
79+
80+
for platform in checkPlatforms {
7881
// FIXME: Test validArguments with the `*` platform once that's introduced
7982
// cf. https://github.com/apple/swift-docc/issues/441
8083
for args in validArgumentsWithVersion {
81-
try assertValidAvailability(source: "@Available(\(platform.rawValue), \(args))")
84+
try assertValidAvailability(source: "@Available(\(platform), \(args))")
8285
}
8386
}
8487

88+
// also check a platform with spaces in the name
89+
for args in validArgumentsWithVersion {
90+
try assertValidAvailability(source: "@Available(\"My Package\", \(args))")
91+
}
92+
8593
// also test for giving no platform
8694
for args in validArguments {
8795
try assertValidAvailability(source: "@Available(\(args))")
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Arbitrary Platforms
2+
3+
@Metadata {
4+
@Available(SomePackage, introduced: "1.0")
5+
@Available("My Package", introduced: "2.0")
6+
}
7+
8+
This page applies to platforms that aren't even operating systems!
9+
10+
<!-- Copyright (c) 2023 Apple Inc and the Swift Project authors. All Rights Reserved. -->

Tests/SwiftDocCTests/Test Bundles/AvailabilityBundle.docc/AvailableArticle.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,6 @@ Here's a cool framework that I'm offering to the world.
1212
### Cool Articles
1313

1414
- <doc:ComplexAvailable>
15+
- <doc:ArbitraryPlatforms>
1516

16-
<!-- Copyright (c) 2022 Apple Inc and the Swift Project authors. All Rights Reserved. -->
17+
<!-- Copyright (c) 2022-2023 Apple Inc and the Swift Project authors. All Rights Reserved. -->

0 commit comments

Comments
 (0)