Repository: apple/swift-docc (swift)

Issue:
Add  `deprecated` version argument to the `@Available` directive
### Feature Name

Specify deprecated version in `@Available` directive

### Description

The initial implementation of `@Available` (https://github.com/apple/swift-docc/pull/440) only supports specifying the "introduced" version. We should add support for specifying a "deprecated" version as well.

### Motivation

_No response_

### Importance

_No response_

### Alternatives Considered

_No response_

Pull request:
## Summary

Adding `deprecated` argument to the directive `@Available`, this is done by adding a field `deprecated` to the `Metadata.Availability` and also addressing all the comments that are related to the issue within the codebase.

This PR also include the `.any` platform for `@Available`, which can be used as follows:

```
@Available(*, introduced: "1.0")
@Available(*, introduced: "1.0", deprecated: "10.0")
```

Closes #441 

Testing

Adding the following metadata into `SwiftDocC/Directives.md`

```
@Metadata {
    @Available("Swift DocC", introduced: "1.0", deprecated: "2.0")
}
```

Using [swift-docc-render-artifact](https://github.com/apple/swift-docc-render-artifact)

Steps:

1. Add the above metadata to `Sources/SwiftDocC/SwiftDocC.docc/SwiftDocC/Directives.md`.
2. `./bin/preview-docs SwiftDocC`

3. Ensure that in the documentation list the "Directives" shows a deprecated tag
<img width="414" alt="Screenshot 2024-03-08 at 4 09 05 PM" src="https://github.com/apple/swift-docc/assets/26250654/36730c30-c9b2-4fec-9398-4d48041f7c0c">

4. Ensure that the "Directives" page is printed with an availability callout for "Swift DocC" which contains a introduced and deprecated versions

<img width="738" alt="Screenshot 2024-03-08 at 4 09 12 PM" src="https://github.com/apple/swift-docc/assets/26250654/9734cc1e-ba78-437f-b165-605770696561">

## Checklist

- [X] Added tests
- [X] Ran the `./bin/test` script and it succeeded
- [x] Updated documentation if necessary 
   (Not sure how to update the docs properly)

Patch:
diff --git a/Sources/SwiftDocC/Model/Rendering/Symbol/AvailabilityRenderMetadataItem.swift b/Sources/SwiftDocC/Model/Rendering/Symbol/AvailabilityRenderMetadataItem.swift
index 13de91ba74..c94de34998 100644
--- a/Sources/SwiftDocC/Model/Rendering/Symbol/AvailabilityRenderMetadataItem.swift
+++ b/Sources/SwiftDocC/Model/Rendering/Symbol/AvailabilityRenderMetadataItem.swift
@@ -147,6 +147,7 @@ public struct AvailabilityRenderItem: Codable, Hashable, Equatable {
         let platformName = PlatformName(metadataPlatform: availability.platform)
         name = platformName?.displayName
         introduced = availability.introduced
+        deprecated = availability.deprecated
     }
     
     /// Creates a new item with the given platform name and version string.
diff --git a/Sources/SwiftDocC/Semantics/Metadata/Availability.swift b/Sources/SwiftDocC/Semantics/Metadata/Availability.swift
index a990c97831..4130c45903 100644
--- a/Sources/SwiftDocC/Semantics/Metadata/Availability.swift
+++ b/Sources/SwiftDocC/Semantics/Metadata/Availability.swift
@@ -16,10 +16,12 @@ extension Metadata {
     ///
     /// `@Available` is analogous to the `@available` attribute in Swift: It allows you to specify a
     /// platform version that the page relates to. To specify a platform and version, list the platform
-    /// name and use the `introduced` argument:
+    /// name and use the `introduced` argument. In addition, you can also specify a deprecated
+    /// version, using the `deprecated` argument:
     ///
     /// ```markdown
     /// @Available(macOS, introduced: "12.0")
+    /// @Available(macOS, introduced: "12.0", deprecated: "14.0")
     /// ```
     ///
     /// Any text can be given to the first argument, and will be displayed in the page's
@@ -48,8 +50,6 @@ extension Metadata {
         public static let introducedVersion = "5.8"
 
         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)
@@ -90,16 +90,18 @@ extension Metadata {
         @DirectiveArgumentWrapped(name: .unnamed)
         public var platform: Platform
 
-        /// The platform version that this page applies to.
+        /// The platform version that this page was introduced in.
         @DirectiveArgumentWrapped
         public var introduced: String
 
-        // FIXME: `isBeta` and `isDeprecated` properties/arguments
-        // cf. https://github.com/apple/swift-docc/issues/441
+        /// The platform version that this page was deprecated in.
+        @DirectiveArgumentWrapped
+        public var deprecated: String? = nil
 
         static var keyPaths: [String : AnyKeyPath] = [
             "platform"     : \Availability._platform,
             "introduced"   : \Availability._introduced,
+            "deprecated"   : \Availability._deprecated,
         ]
 
         public let originalMarkup: Markdown.BlockDirective


Test patch:
diff --git a/Tests/SwiftDocCTests/Semantics/MetadataAvailabilityTests.swift b/Tests/SwiftDocCTests/Semantics/MetadataAvailabilityTests.swift
index 66aeaf2687..9b517a45bd 100644
--- a/Tests/SwiftDocCTests/Semantics/MetadataAvailabilityTests.swift
+++ b/Tests/SwiftDocCTests/Semantics/MetadataAvailabilityTests.swift
@@ -63,10 +63,7 @@ class MetadataAvailabilityTests: XCTestCase {
     func testValidDirective() throws {
         // assemble all the combinations of arguments you could give
         let validArguments: [String] = [
-            // FIXME: isBeta and isDeprecated are unused (https://github.com/apple/swift-docc/issues/441)
-//            "isBeta: true",
-//            "isDeprecated: true",
-//            "isBeta: true, isDeprecated: true",
+          "deprecated: \"1.0\"",
         ]
         // separate those that give a version so we can test the `*` platform separately
         var validArgumentsWithVersion = ["introduced: \"1.0\""]
@@ -90,17 +87,12 @@ class MetadataAvailabilityTests: XCTestCase {
             try assertValidAvailability(source: "@Available(\"My Package\", \(args))")
         }
 
-        // also test for giving no platform
-        for args in validArguments {
-            try assertValidAvailability(source: "@Available(\(args))")
-        }
-
-        // basic validity test for giving several directives
-        // FIXME: re-add isBeta after that is implemented (https://github.com/apple/swift-docc/issues/441)
         let source = """
         @Metadata {
             @Available(macOS, introduced: "11.0")
             @Available(iOS, introduced: "15.0")
+            @Available(watchOS, introduced: "7.0", deprecated: "9.0")
+            @Available("My Package", introduced: "0.1", deprecated: "1.0")
         }
         """
         try assertValidMetadata(source: source)