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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
/*
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 patch to update a render node value.
public enum VariantPatchOperation<Value: Codable> {
/// A replacement operation.
///
/// - Parameter value: The value to use in the replacement.
case replace(value: Value)
/// An addition operation.
///
/// - Parameter value: The value to use in the addition.
case add(value: Value)
/// A removal operation.
case remove
/// The operation to apply.
public var operation: PatchOperation {
switch self {
case .replace(_):
return .replace
case .add(_):
return .add
case .remove:
return .remove
}
}
/// Returns a new patch operation with its value transformed using the given closure.
///
/// If the patch operation doesn't have a value---for example, if it's a removal operation---the operation is returned unmodified.
func map<TransformedValue>(
_ transform: (Value) -> TransformedValue
) -> VariantPatchOperation<TransformedValue> {
switch self {
case .replace(let value):
return VariantPatchOperation<TransformedValue>.replace(value: transform(value))
case .add(let value):
return VariantPatchOperation<TransformedValue>.add(value: transform(value))
case .remove:
return .remove
}
}
}
// The synthesized implementation is sufficient for this conformance.
extension VariantPatchOperation: Equatable where Value: Equatable {}
// MARK: Applying patches
/// A type that can be transformed by incrementally applying variant patch operations.
protocol VariantCollectionPatchable {
/// Apply an "add" patch operation to the value
mutating func add(_ other: Self)
/// Apply a "remove" patch operation to the value
mutating func remove()
}
extension Optional: VariantCollectionPatchable where Wrapped: VariantCollectionPatchable {
mutating func add(_ other: Wrapped?) {
guard var wrapped, let other else { return }
wrapped.add(other)
self = wrapped
}
mutating func remove() {
self = nil
}
}
extension Array: VariantCollectionPatchable {
mutating func add(_ other: [Element]) {
append(contentsOf: other)
}
mutating func remove() {
self.removeAll()
}
}
extension String: VariantCollectionPatchable {
mutating func add(_ other: String) {
append(contentsOf: other)
}
mutating func remove() {
self.removeAll()
}
}
extension VariantCollection where Value: VariantCollectionPatchable {
/// Returns the transformed value after applying the patch operations for all variants that match the given source language to the default value.
/// - Parameters:
/// - language: The source language that determine what variant's patches to apply to the default value.
/// - Returns: The transformed value, or the default value if no variants match the given source language.
func value(for language: SourceLanguage) -> Value {
applied(to: defaultValue, for: [.interfaceLanguage(language.id)])
}
/// Returns the transformed value after applying the patch operations for all variants that match the given traits to the default value.
/// - Parameters:
/// - traits: The traits that determine what variant's patches to apply to the default value.
/// - Returns: The transformed value, or the default value if no variants match the given traits.
func value(for traits: [RenderNode.Variant.Trait]) -> Value {
applied(to: defaultValue, for: traits)
}
/// Returns the transformed value after applying the patch operations for all variants that match the given traits to the original value.
/// - Parameters:
/// - originalValue: The original value to transform.
/// - traits: The traits that determine what variant's patches to apply to the original value.
/// - Returns: The transformed value, or the original value if no variants match the given traits.
func applied(to originalValue: Value, for traits: [RenderNode.Variant.Trait]) -> Value {
var patchedValue = originalValue
for variant in variants where variant.traits == traits {
for patch in variant.patch {
switch patch {
case .replace(let value):
patchedValue = value
case .add(let value):
patchedValue.add(value)
case .remove:
patchedValue.remove()
}
}
}
return patchedValue
}
}
|