File: VariantCollection.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (115 lines) | stat: -rw-r--r-- 5,275 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
 This source file is part of the Swift.org open source project

 Copyright (c) 2021-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

/// A collection of variants for a render node value.
///
/// Variant collections encapsulate different values for the same piece of content. Each variant collection has a default value and optionally, trait-specific
/// (e.g., programming language–specific) values that client can choose to use based on their context.
///
/// For example, a collection can a hold programming language-agnostic documentation value as its ``defaultValue``, and hold Objective-C specific values
/// in its ``variants`` array. Clients that want to process the Objective-C version of a documentation page then use the override rather than the
/// default value, and fall back to the default value if no Objective-C-specific override is specified.
public struct VariantCollection<Value: Codable>: Codable {
    /// The default value of the variant.
    ///
    /// Clients should decide whether the `defaultValue` or a value in ``variants`` is appropriate in their context.
    public var defaultValue: Value
    
    /// Trait-specific overrides for the default value.
    ///
    /// Clients should decide whether the `defaultValue` or a value in ``variants`` is appropriate in their context.
    public var variants: [Variant]
    
    /// Creates a variant collection given a default value and an array of trait-specific overrides.
    ///
    /// - Parameters:
    ///   - defaultValue: The default value of the variant.
    ///   - variants: The trait-specific overrides for the value.
    public init(defaultValue: Value, variants: [Variant] = []) {
        self.defaultValue = defaultValue
        self.variants = variants
    }
    
    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        self.defaultValue = try container.decode(Value.self)
        
        // When decoding a render node, the variants overrides stored in the `RenderNode.variantOverrides` property.
        self.variants = []
    }
    
    public func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(defaultValue)
        addVariantsToEncoder(encoder, isDefaultValueEncoded: true)
    }
    
    /// Adds the variants of the collection to the given encoder.
    ///
    /// - Parameters:
    ///   - encoder: The encoder to add the variants to.
    ///   - pointer: The pointer that should be used for the variants. If `nil`, the encoder's current coding path will be used.
    ///   - isDefaultValueEncoded: Whether the default value for this topic collection has been encoded in the encoder's container. If it hasn't, this function
    ///   replaces the variants' 'replace' patch operations with 'add' operations, since the container has no value to replace.
    func addVariantsToEncoder(
        _ encoder: Encoder,
        pointer: JSONPointer? = nil,
        isDefaultValueEncoded: Bool
    ) {
        let overrides = variants.map { variant in
            VariantOverride(
                traits: variant.traits,
                patch: variant.patch.map { patchOperation in
                    var patchOperation = patchOperation
                    
                    // If the default value for this variant collection wasn't encoded in the JSON and the
                    // patch operation is a 'replace', change it to an 'add' since there's no value to replace.
                    if !isDefaultValueEncoded, case .replace(let value) = patchOperation {
                        patchOperation = .add(value: value)
                    }
                    
                    let jsonPointer = (
                        pointer ?? JSONPointer(from: encoder.codingPath)
                    ).prependingPathComponents(encoder.baseJSONPatchPath ?? [])
                    
                    return JSONPatchOperation(
                        variantPatchOperation: patchOperation,
                        pointer: jsonPointer
                    )
                }
            )
        }
        
        encoder.userInfoVariantOverrides?.add(contentsOf: overrides)
    }
    
    /// Returns a variant collection containing the results of calling the given transformation with each value of this variant collection.
    public func mapValues<TransformedValue>(
        _ transform: (Value) -> TransformedValue
    ) -> VariantCollection<TransformedValue> {
        VariantCollection<TransformedValue>(
            defaultValue: transform(defaultValue),
            variants: variants.map { variant in
                variant.mapPatch(transform)
            }
        )
    }
}

extension VariantCollection: Equatable where Value: Equatable {
    public static func == (lhs: VariantCollection<Value>, rhs: VariantCollection<Value>) -> Bool {
        guard lhs.defaultValue == rhs.defaultValue else { return false }
        guard lhs.variants == rhs.variants else { return false }
        
        return true
    }
}