Skip to content

Commit

Permalink
Modified default availability info during decoding based on the defau…
Browse files Browse the repository at this point in the history
…lt availability options.
  • Loading branch information
sofiaromorales committed Sep 18, 2024
1 parent fbc3ead commit 59942f6
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
/// ```
/// <key>CDDefaultAvailabilityOptions</key>
/// <dict>
/// <key>OptionName</key>
/// <bool/>
/// </dict>
/// ```
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.
///
/// ```
/// <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 {
/// 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
}

}
}

Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ extension DocumentationBundle {
static let requiredKeys: Set<CodingKeys> = [.displayName, .identifier]

/// The default availability behaviour options.
public var defaultAvailabilityOptions = DefaultAvailabilityOptions()
public var defaultAvailabilityOptions: DefaultAvailabilityOptions?


enum CodingKeys: String, CodingKey, CaseIterable {
Expand Down Expand Up @@ -114,7 +114,7 @@ extension DocumentationBundle {
defaultCodeListingLanguage: String?,
defaultAvailability: DefaultAvailability?,
defaultModuleKind: String?,
defaultAvailabilityOptions: DefaultAvailabilityOptions
defaultAvailabilityOptions: DefaultAvailabilityOptions?
) {
self.displayName = displayName
self.identifier = identifier
Expand Down Expand Up @@ -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(
Expand All @@ -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
Expand All @@ -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)
}
}
}
Expand Down Expand Up @@ -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.
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.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) }),
Expand Down

0 comments on commit 59942f6

Please sign in to comment.