Skip to content
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

allow arbitrary platform strings in the @Available directive #463

Merged
merged 2 commits into from
Jan 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

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

See https://swift.org/LICENSE.txt for license information
Expand All @@ -23,6 +23,10 @@ enum AvailabilityRenderOrder {
/// Sort two availability render items based on their platform name.
static func compare(lhs: AvailabilityRenderItem, rhs: AvailabilityRenderItem) -> Bool {
guard let lhsName = lhs.name, let rhsName = rhs.name else { return false }
return platformsOrder[lhsName, default: Int.max] < platformsOrder[rhsName, default: Int.max]
if platformsOrder.keys.contains(lhsName) || platformsOrder.keys.contains(rhsName) {
return platformsOrder[lhsName, default: Int.max] < platformsOrder[rhsName, default: Int.max]
} else {
return lhsName < rhsName
}
}
}
41 changes: 36 additions & 5 deletions Sources/SwiftDocC/Semantics/Metadata/Availability.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

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

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

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

case other(String)

static var defaultCases: [Platform] = [.macOS, .iOS, .watchOS, .tvOS]

public init?(rawValue: String) {
for platform in Self.allCases {
for platform in Self.defaultCases {
if platform.rawValue.lowercased() == rawValue.lowercased() {
self = platform
return
}
}
return nil
if rawValue == "*" {
Copy link
Contributor

@binamaniar binamaniar Jan 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this implementation as well, we can mimic Deprecated behavior. it would be so confusing to have @available() with Deprecated information passed to it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean allowing @Available(isDeprecated: true) once that is implemented? That will need to be handled as part of that implementation.

If you mean allowing @Available(MyPackage, isDeprecated, true), that should come for free without any extra work. I could add a test for that if you would like.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant the first part, i.e @available(isDeprecated: true). sorry my response was never posted due to my internet connectivity.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, deprecation status isn't available yet; see #441 for information.

// Reserve the `*` platform for when `isBeta` and `isDeprecated` can be implemented
return nil
} else {
self = .other(rawValue)
}
}

public var rawValue: String {
switch self {
case .macOS: return "macOS"
case .iOS: return "iOS"
case .watchOS: return "watchOS"
case .tvOS: return "tvOS"
case .other(let platform): return platform
}
}

static func allowedValues() -> [String]? {
nil
}
}

Expand Down
9 changes: 7 additions & 2 deletions Sources/SwiftDocC/Semantics/Symbol/PlatformName.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

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

See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -109,6 +109,11 @@ public struct PlatformName: Codable, Hashable, Equatable {
// Note: This is still an optional initializer to prevent source breakage when
// `Availability.Platform` re-introduces the `.any` case
// cf. https://github.com/apple/swift-docc/issues/441
self = .init(operatingSystemName: platform.rawValue)
if let knowDomain = Self.platformNamesIndex[platform.rawValue.lowercased()] {
self = knowDomain
} else {
let identifier = platform.rawValue.lowercased().replacingOccurrences(of: " ", with: "")
self.init(rawValue: identifier, displayName: platform.rawValue)
}
}
}
28 changes: 27 additions & 1 deletion Tests/SwiftDocCTests/Rendering/PlatformAvailabilityTests.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

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

See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -106,4 +106,30 @@ class PlatformAvailabilityTests: XCTestCase {
item.name == "watchOS" && item.introduced == "7.0"
}))
}

func testArbitraryPlatformAvailability() throws {
let (bundle, context) = try testBundleAndContext(named: "AvailabilityBundle")
let reference = ResolvedTopicReference(
bundleIdentifier: bundle.identifier,
path: "/documentation/AvailabilityBundle/ArbitraryPlatforms",
sourceLanguage: .swift
)
let article = try XCTUnwrap(context.entity(with: reference).semantic as? Article)
var translator = RenderNodeTranslator(
context: context,
bundle: bundle,
identifier: reference,
source: nil
)
let renderNode = try XCTUnwrap(translator.visitArticle(article) as? RenderNode)
let availability = try XCTUnwrap(renderNode.metadata.platformsVariants.defaultValue)
XCTAssertEqual(availability.count, 2)

XCTAssert(availability.contains(where: { item in
item.name == "SomePackage" && item.introduced == "1.0"
}))
XCTAssert(availability.contains(where: { item in
item.name == "My Package" && item.introduced == "2.0"
}))
}
}
16 changes: 12 additions & 4 deletions Tests/SwiftDocCTests/Semantics/MetadataAvailabilityTests.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/*
This source file is part of the Swift.org open source project

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

See https://swift.org/LICENSE.txt for license information
Expand Down Expand Up @@ -49,7 +49,7 @@ class MetadataAvailabilityTests: XCTestCase {
}
}

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

for platform in Metadata.Availability.Platform.allCases {
var checkPlatforms = Metadata.Availability.Platform.defaultCases.map({ $0.rawValue })
checkPlatforms.append("Package")

for platform in checkPlatforms {
// FIXME: Test validArguments with the `*` platform once that's introduced
// cf. https://github.com/apple/swift-docc/issues/441
for args in validArgumentsWithVersion {
try assertValidAvailability(source: "@Available(\(platform.rawValue), \(args))")
try assertValidAvailability(source: "@Available(\(platform), \(args))")
}
}

// also check a platform with spaces in the name
for args in validArgumentsWithVersion {
try assertValidAvailability(source: "@Available(\"My Package\", \(args))")
}

// also test for giving no platform
for args in validArguments {
try assertValidAvailability(source: "@Available(\(args))")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Arbitrary Platforms

@Metadata {
@Available(SomePackage, introduced: "1.0")
@Available("My Package", introduced: "2.0")
}

This page applies to platforms that aren't even operating systems!

<!-- Copyright (c) 2023 Apple Inc and the Swift Project authors. All Rights Reserved. -->
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@ Here's a cool framework that I'm offering to the world.
### Cool Articles

- <doc:ComplexAvailable>
- <doc:ArbitraryPlatforms>

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