Skip to content

Commit

Permalink
Implement DefaultAvailabilityOptions.
Browse files Browse the repository at this point in the history
`DefaultAvailabilityOptions` is a new info plist key that contains a dictionary of options to customise the logic of the default availbaility.

The only option for now is `InheritVersionNumber` that lets you add the version into the symbols availability that don't define explicit availability annotation. The default is true.
  • Loading branch information
sofiaromorales committed Sep 16, 2024
1 parent 6055aa9 commit fbc3ead
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ struct SymbolGraphLoader {
guard let defautAvailabilities else { return nil }
// Check the selected behaviour for inheritance of the default availability and remove the avaialbity
// version if it's set to `platformOnly`.
if bundle.info.inheritDefaultAvailability == .platformOnly {
if !bundle.info.defaultAvailabilityOptions.options.contains(.inheritVersionNumber) {
return defautAvailabilities.map { defaultAvailability in
var defaultAvailability = defaultAvailability
switch defaultAvailability.versionInformation {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
This source file is part of the Swift.org open source project
Copyright (c) 2024 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
See https://swift.org/CONTRIBUTORS.txt for Swift project authors
*/

import Foundation

extension DocumentationBundle.Info {

/// A collection of options that customaise the default availability behaviour.
///
/// Default availability options are applied to all the modules contained in the documentation bundle.
///
/// This information can be authored in the bundle's Info.plist file, as a dictionary of option name and boolean pairs.
///
/// ```
/// <key>CDDefaultAvailabilityOptions</key>
/// <dict>
/// <key>OptionName</key>
/// <bool/>
/// </dict>
/// ```
public struct DefaultAvailabilityOptions: Codable, Equatable {

/// A set of non-standard behaviors that apply to this node.
fileprivate(set) var options: Options

/// Options that specify behaviors of the default availability logic.
struct Options: OptionSet {

let rawValue: Int

/// Enable or disable symbol availability version inference from the module default availability.
static let inheritVersionNumber = Options(rawValue: 1 << 0)
}

/// String representation of the default availability options.
private enum CodingKeys: String, CodingKey {
case inheritVersionNumber = "InheritVersionNumber"
}

public init(from decoder: any Decoder) throws {
self.init()
let values = try decoder.container(keyedBy: CodingKeys.self)
if try values.decodeIfPresent(Bool.self, forKey: .inheritVersionNumber) == false {
options.remove(.inheritVersionNumber)
}
}

public func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
if !options.contains(.inheritVersionNumber) {
try container.encode(false, forKey: .inheritVersionNumber)
}

}

public init() {
self.options = .inheritVersionNumber
}

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ extension DocumentationBundle {
/// The keys that must be present in an Info.plist file in order for doc compilation to proceed.
static let requiredKeys: Set<CodingKeys> = [.displayName, .identifier]

/// The flag to enable or disable symbol availability inference from the module default availability.
/// If not specified the default befaviout will be ``InheritDefaultAvailabilityOptions.platformAndVersion``
public var inheritDefaultAvailability: InheritDefaultAvailabilityOptions?
/// The default availability behaviour options.
public var defaultAvailabilityOptions = DefaultAvailabilityOptions()


enum CodingKeys: String, CodingKey, CaseIterable {
case displayName = "CFBundleDisplayName"
Expand All @@ -61,7 +61,7 @@ extension DocumentationBundle {
case defaultAvailability = "CDAppleDefaultAvailability"
case defaultModuleKind = "CDDefaultModuleKind"
case featureFlags = "CDExperimentalFeatureFlags"
case inheritDefaultAvailability = "CDInheritDefaultAvailability"
case defaultAvailabilityOptions = "CDDefaultAvailabilityOptions"

var argumentName: String? {
switch self {
Expand All @@ -75,9 +75,7 @@ extension DocumentationBundle {
return "--default-code-listing-language"
case .defaultModuleKind:
return "--fallback-default-module-kind"
case .inheritDefaultAvailability:
return nil
case .defaultAvailability, .featureFlags:
case .defaultAvailability, .defaultAvailabilityOptions, .featureFlags:
return nil
}
}
Expand Down Expand Up @@ -108,23 +106,23 @@ extension DocumentationBundle {
/// - defaultCodeListingLanguage: The default language identifier for code listings in the bundle.
/// - defaultAvailability: The default availability for the various modules in the bundle.
/// - defaultModuleKind: The default kind for the various modules in the bundle.
/// - inheritDefaultAvailability: The option to enable or disable symbol availability inheritance from the module default availability.
/// - defaultAvailabilityOptions: The options to enable or disable symbol availability logic from the module default availability.
public init(
displayName: String,
identifier: String,
version: String?,
defaultCodeListingLanguage: String?,
defaultAvailability: DefaultAvailability?,
defaultModuleKind: String?,
inheritDefaultAvailability: InheritDefaultAvailabilityOptions?
defaultAvailabilityOptions: DefaultAvailabilityOptions
) {
self.displayName = displayName
self.identifier = identifier
self.version = version
self.defaultCodeListingLanguage = defaultCodeListingLanguage
self.defaultAvailability = defaultAvailability
self.defaultModuleKind = defaultModuleKind
self.inheritDefaultAvailability = inheritDefaultAvailability
self.defaultAvailabilityOptions = defaultAvailabilityOptions
}

/// Creates documentation bundle information from the given Info.plist data, falling back to the values
Expand Down Expand Up @@ -256,10 +254,7 @@ extension DocumentationBundle {
self.defaultModuleKind = try decodeOrFallbackIfPresent(String.self, with: .defaultModuleKind)
self.defaultAvailability = try decodeOrFallbackIfPresent(DefaultAvailability.self, with: .defaultAvailability)
self.featureFlags = try decodeOrFallbackIfPresent(BundleFeatureFlags.self, with: .featureFlags)
let inheritDefaultAvailabilityRawValue = try decodeOrFallbackIfPresent(String.self, with: .inheritDefaultAvailability)
if let inheritDefaultAvailabilityRawValue {
self.inheritDefaultAvailability = InheritDefaultAvailabilityOptions(rawValue: inheritDefaultAvailabilityRawValue)
}
self.defaultAvailabilityOptions = try decodeOrFallbackIfPresent(DefaultAvailabilityOptions.self, with: .defaultAvailabilityOptions) ?? DefaultAvailabilityOptions()
}

init(
Expand All @@ -270,7 +265,8 @@ extension DocumentationBundle {
defaultModuleKind: String? = nil,
defaultAvailability: DefaultAvailability? = nil,
featureFlags: BundleFeatureFlags? = nil,
inheritDefaultAvailability: InheritDefaultAvailabilityOptions? = nil
inheritDefaultAvailability: InheritDefaultAvailabilityOptions? = nil,
defaultAvailabilityOptions: DefaultAvailabilityOptions = DefaultAvailabilityOptions()
) {
self.displayName = displayName
self.identifier = identifier
Expand All @@ -279,7 +275,22 @@ extension DocumentationBundle {
self.defaultModuleKind = defaultModuleKind
self.defaultAvailability = defaultAvailability
self.featureFlags = featureFlags
self.inheritDefaultAvailability = inheritDefaultAvailability
self.defaultAvailabilityOptions = defaultAvailabilityOptions
}

public func encode(to encoder: any Encoder) throws {
var container: KeyedEncodingContainer<DocumentationBundle.Info.CodingKeys> = encoder.container(keyedBy: DocumentationBundle.Info.CodingKeys.self)

try container.encode(self.displayName, forKey: DocumentationBundle.Info.CodingKeys.displayName)
try container.encode(self.identifier, forKey: DocumentationBundle.Info.CodingKeys.identifier)
try container.encodeIfPresent(self.version, forKey: DocumentationBundle.Info.CodingKeys.version)
try container.encodeIfPresent(self.defaultCodeListingLanguage, forKey: DocumentationBundle.Info.CodingKeys.defaultCodeListingLanguage)
try container.encodeIfPresent(self.defaultAvailability, forKey: DocumentationBundle.Info.CodingKeys.defaultAvailability)
try container.encodeIfPresent(self.defaultModuleKind, forKey: DocumentationBundle.Info.CodingKeys.defaultModuleKind)
try container.encodeIfPresent(self.featureFlags, forKey: DocumentationBundle.Info.CodingKeys.featureFlags)
if defaultAvailabilityOptions != .init() {
try container.encode(self.defaultAvailabilityOptions, forKey: DocumentationBundle.Info.CodingKeys.defaultAvailabilityOptions)
}
}
}
}
Expand All @@ -298,7 +309,7 @@ extension BundleDiscoveryOptions {
/// - fallbackDefaultModuleKind: A fallback default module kind for the bundle.
/// - fallbackDefaultAvailability: A fallback default availability for the bundle.
/// - additionalSymbolGraphFiles: Additional symbol graph files to augment any discovered bundles.
/// - inheritDefaultAvailability: Option to configure default availability inheritance behaviour.
/// - defaultAvailabilityOptions: Options to configure default availability behaviour.
public init(
fallbackDisplayName: String? = nil,
fallbackIdentifier: String? = nil,
Expand All @@ -307,7 +318,7 @@ extension BundleDiscoveryOptions {
fallbackDefaultModuleKind: String? = nil,
fallbackDefaultAvailability: DefaultAvailability? = nil,
additionalSymbolGraphFiles: [URL] = [],
inheritDefaultAvailability: DocumentationBundle.InheritDefaultAvailabilityOptions? = nil
defaultAvailabilityOptions: DocumentationBundle.Info.DefaultAvailabilityOptions? = nil
) {
// Iterate over all possible coding keys with a switch
// to build up the dictionary of fallback options.
Expand All @@ -332,8 +343,8 @@ extension BundleDiscoveryOptions {
value = fallbackDefaultModuleKind
case .featureFlags:
value = nil
case .inheritDefaultAvailability:
value = inheritDefaultAvailability
case .defaultAvailabilityOptions:
value = defaultAvailabilityOptions
}

guard let unwrappedValue = value else {
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1243,8 +1243,8 @@ public struct RenderNodeTranslator: SemanticVisitor {
.compactMap { availability -> AvailabilityRenderItem? in
// Filter items with insufficient availability data unless the default availability behaviour
// allows availability withound version information.
let applyDefaultAvailabilityVersionToSymbols = bundle.info.inheritDefaultAvailability
guard availability.introducedVersion != nil || applyDefaultAvailabilityVersionToSymbols == .platformOnly else {
let applyDefaultAvailabilityVersionToSymbols = !bundle.info.defaultAvailabilityOptions.options.contains(.inheritVersionNumber)
guard availability.introducedVersion != nil || applyDefaultAvailabilityVersionToSymbols else {
return nil
}
guard let name = availability.domain.map({ PlatformName(operatingSystemName: $0.rawValue) }),
Expand Down
21 changes: 15 additions & 6 deletions Tests/SwiftDocCTests/Rendering/DefaultAvailabilityTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -687,8 +687,11 @@ class DefaultAvailabilityTests: XCTestCase {
// Don't use default availability version for symbols.

var context = try setupContext(inheritDefaultAvailability: """
<key>CDInheritDefaultAvailability</key>
<string>platformOnly</string>
<key>CDDefaultAvailabilityOptions</key>
<dict>
<key>InheritVersionNumber</key>
<false/>
</dict>
""")

// Verify we add the version number into the symbols that have availability annotation.
Expand All @@ -710,8 +713,11 @@ class DefaultAvailabilityTests: XCTestCase {
// Add an extra default availability to test behaviour when mixin in source with default behaviour.
context = try setupContext(
inheritDefaultAvailability: """
<key>CDInheritDefaultAvailability</key>
<string>platformOnly</string>
<key>CDDefaultAvailabilityOptions</key>
<dict>
<key>InheritVersionNumber</key>
<false/>
</dict>
""",
defaultAvailability: """
<dict>
Expand Down Expand Up @@ -742,8 +748,11 @@ class DefaultAvailabilityTests: XCTestCase {
// Use default availability version for symbols.

context = try setupContext(inheritDefaultAvailability: """
<key>CDInheritDefaultAvailability</key>
<string>platformAndVersion</string>
<key>CDDefaultAvailabilityOptions</key>
<dict>
<key>InheritVersionNumber</key>
<true/>
</dict>
""")

// Verify we add the version number into the symbols that have availability annotation.
Expand Down

0 comments on commit fbc3ead

Please sign in to comment.