diff --git a/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift b/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift
index a3981a1dac..bb4b51cda4 100644
--- a/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift
+++ b/Sources/SwiftDocC/Infrastructure/Symbol Graph/SymbolGraphLoader.swift
@@ -59,24 +59,6 @@ struct SymbolGraphLoader {
let bundle = self.bundle
let dataProvider = self.dataProvider
- /// Computes the default availbiality based on the `inheritDefaultAvailability` option.
- let defaultAvailabilities: ([DefaultAvailability.ModuleAvailability]?) -> [DefaultAvailability.ModuleAvailability]? = { defautAvailabilities in
- 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.defaultAvailabilityOptions.options.contains(.inheritVersionNumber) {
- return defautAvailabilities.map { defaultAvailability in
- var defaultAvailability = defaultAvailability
- switch defaultAvailability.versionInformation {
- case .available(_): defaultAvailability.versionInformation = .available(version: nil)
- case .unavailable: ()
- }
- return defaultAvailability
- }
- }
- return defautAvailabilities
- }
-
let loadGraphAtURL: (URL) -> Void = { symbolGraphURL in
// Bail out in case a symbol graph has already errored
guard loadError == nil else { return }
@@ -97,9 +79,8 @@ struct SymbolGraphLoader {
configureSymbolGraph?(&symbolGraph)
let (moduleName, isMainSymbolGraph) = Self.moduleNameFor(symbolGraph, at: symbolGraphURL)
- let defaultAvailabilities = defaultAvailabilities(bundle.info.defaultAvailability?.modules[moduleName])
// If the bundle provides availability defaults add symbol availability data.
- self.addDefaultAvailability(to: &symbolGraph, moduleName: moduleName, defaultAvailabilities: defaultAvailabilities)
+ self.addDefaultAvailability(to: &symbolGraph, moduleName: moduleName)
// main symbol graphs are ambiguous
var usesExtensionSymbolFormat: Bool? = nil
@@ -172,7 +153,7 @@ struct SymbolGraphLoader {
var defaultUnavailablePlatforms = [PlatformName]()
var defaultAvailableInformation = [DefaultAvailability.ModuleAvailability]()
- if let defaultAvailabilities = defaultAvailabilities(bundle.info.defaultAvailability?.modules[unifiedGraph.moduleName]) {
+ if let defaultAvailabilities = bundle.info.defaultAvailability?.modules[unifiedGraph.moduleName] {
let (unavailablePlatforms, availablePlatforms) = defaultAvailabilities.categorize(where: { $0.versionInformation == .unavailable })
defaultUnavailablePlatforms = unavailablePlatforms.map(\.platformName)
defaultAvailableInformation = availablePlatforms
@@ -298,11 +279,11 @@ struct SymbolGraphLoader {
/// If the bundle defines default availability for the symbols in the given symbol graph
/// this method adds them to each of the symbols in the graph.
- private func addDefaultAvailability(to symbolGraph: inout SymbolGraph, moduleName: String, defaultAvailabilities: [DefaultAvailability.ModuleAvailability]?) {
+ private func addDefaultAvailability(to symbolGraph: inout SymbolGraph, moduleName: String) {
let selector = UnifiedSymbolGraph.Selector(forSymbolGraph: symbolGraph)
// Check if there are defined default availabilities for the current module
- if let defaultAvailabilities = defaultAvailabilities,
- let platformName = symbolGraph.module.platform.name.map(PlatformName.init) {
+ if let defaultAvailabilities = bundle.info.defaultAvailability?.modules[moduleName],
+ let platformName = symbolGraph.module.platform.name.map(PlatformName.init) {
// Prepare a default availability versions lookup for this module.
let defaultAvailabilityVersionByPlatform = defaultAvailabilities
diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailailabilityOptions.swift b/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailailabilityOptions.swift
index bba999fb06..781d829430 100644
--- a/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailailabilityOptions.swift
+++ b/Sources/SwiftDocC/Infrastructure/Workspace/DefaultAvailailabilityOptions.swift
@@ -10,60 +10,66 @@
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.
+///
+/// ```
+/// CDDefaultAvailabilityOptions
+///
+/// OptionName
+///
+///
+/// ```
+public struct DefaultAvailabilityOptions: Codable, Equatable {
- /// 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.
- ///
- /// ```
- /// CDDefaultAvailabilityOptions
- ///
- /// OptionName
- ///
- ///
- /// ```
- 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 {
+ /// 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)
- }
+ let rawValue: Int
- /// String representation of the default availability options.
- private enum CodingKeys: String, CodingKey {
- case inheritVersionNumber = "InheritVersionNumber"
- }
+ /// 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() {
+ self.options = .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 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 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
+ }
+
+ /// Convenient method to determine if an option has to be applied depending
+ /// on it's exsistence inside the options set.
+ func shouldApplyOption(_ option: Options) -> Bool {
+ switch option {
+ case .inheritVersionNumber:
+ return options.contains(.inheritVersionNumber)
+ default:
+ return false
}
-
}
}
diff --git a/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift b/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift
index b4a001e204..7a9083ade6 100644
--- a/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift
+++ b/Sources/SwiftDocC/Infrastructure/Workspace/DocumentationBundle+Info.swift
@@ -50,7 +50,7 @@ extension DocumentationBundle {
static let requiredKeys: Set = [.displayName, .identifier]
/// The default availability behaviour options.
- public var defaultAvailabilityOptions = DefaultAvailabilityOptions()
+ public var defaultAvailabilityOptions: DefaultAvailabilityOptions?
enum CodingKeys: String, CodingKey, CaseIterable {
@@ -114,7 +114,7 @@ extension DocumentationBundle {
defaultCodeListingLanguage: String?,
defaultAvailability: DefaultAvailability?,
defaultModuleKind: String?,
- defaultAvailabilityOptions: DefaultAvailabilityOptions
+ defaultAvailabilityOptions: DefaultAvailabilityOptions?
) {
self.displayName = displayName
self.identifier = identifier
@@ -252,9 +252,31 @@ extension DocumentationBundle {
self.defaultCodeListingLanguage = try decodeOrFallbackIfPresent(String.self, with: .defaultCodeListingLanguage)
self.defaultModuleKind = try decodeOrFallbackIfPresent(String.self, with: .defaultModuleKind)
- self.defaultAvailability = try decodeOrFallbackIfPresent(DefaultAvailability.self, with: .defaultAvailability)
+ self.defaultAvailabilityOptions = try decodeOrFallbackIfPresent(DefaultAvailabilityOptions.self, with: .defaultAvailabilityOptions)
+ var defaultAvailability = try decodeOrFallbackIfPresent(DefaultAvailability.self, with: .defaultAvailability)
+
+ // Apply default availability options mutations.
+ if let defaultAvailabilityOptions {
+ // Remove the availability version if `inheritVersionNumber` is not part
+ // of the default availability options.
+ if !defaultAvailabilityOptions.shouldApplyOption(.inheritVersionNumber) {
+ for (key, var moduleAvailability) in defaultAvailability?.modules ?? [:] {
+ moduleAvailability = moduleAvailability.map { moduleAvailability in
+ var moduleAvailability = moduleAvailability
+ moduleAvailability.versionInformation = {
+ switch moduleAvailability.versionInformation {
+ case .available(_): return .available(version: nil)
+ default: return moduleAvailability.versionInformation
+ }
+ }()
+ return moduleAvailability
+ }
+ defaultAvailability?.modules[key] = moduleAvailability
+ }
+ }
+ }
+ self.defaultAvailability = defaultAvailability
self.featureFlags = try decodeOrFallbackIfPresent(BundleFeatureFlags.self, with: .featureFlags)
- self.defaultAvailabilityOptions = try decodeOrFallbackIfPresent(DefaultAvailabilityOptions.self, with: .defaultAvailabilityOptions) ?? DefaultAvailabilityOptions()
}
init(
@@ -266,7 +288,7 @@ extension DocumentationBundle {
defaultAvailability: DefaultAvailability? = nil,
featureFlags: BundleFeatureFlags? = nil,
inheritDefaultAvailability: InheritDefaultAvailabilityOptions? = nil,
- defaultAvailabilityOptions: DefaultAvailabilityOptions = DefaultAvailabilityOptions()
+ defaultAvailabilityOptions: DefaultAvailabilityOptions? = nil
) {
self.displayName = displayName
self.identifier = identifier
@@ -289,7 +311,7 @@ extension DocumentationBundle {
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)
+ try container.encodeIfPresent(self.defaultAvailabilityOptions, forKey: DocumentationBundle.Info.CodingKeys.defaultAvailabilityOptions)
}
}
}
@@ -318,7 +340,7 @@ extension BundleDiscoveryOptions {
fallbackDefaultModuleKind: String? = nil,
fallbackDefaultAvailability: DefaultAvailability? = nil,
additionalSymbolGraphFiles: [URL] = [],
- defaultAvailabilityOptions: DocumentationBundle.Info.DefaultAvailabilityOptions? = nil
+ defaultAvailabilityOptions: DefaultAvailabilityOptions? = nil
) {
// Iterate over all possible coding keys with a switch
// to build up the dictionary of fallback options.
diff --git a/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift b/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift
index ce2c002a11..3ddc28bb6c 100644
--- a/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift
+++ b/Sources/SwiftDocC/Model/Rendering/RenderNodeTranslator.swift
@@ -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.defaultAvailabilityOptions.options.contains(.inheritVersionNumber)
- guard availability.introducedVersion != nil || applyDefaultAvailabilityVersionToSymbols else {
+ let omitDefaultAvailabilityVersionFromSymbols = bundle.info.defaultAvailabilityOptions?.shouldApplyOption(.inheritVersionNumber) == false
+ guard availability.introducedVersion != nil || omitDefaultAvailabilityVersionFromSymbols else {
return nil
}
guard let name = availability.domain.map({ PlatformName(operatingSystemName: $0.rawValue) }),