TM-SGNL-iOS/SignalServiceKit/Util/DecodableDefaults.swift
TeleMessage developers dde0620daf initial commit
2025-05-03 12:28:28 -07:00

129 lines
4.7 KiB
Swift

//
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
//
import Foundation
// Based on https://www.swiftbysundell.com/tips/default-decoding-values/
public protocol DecodableDefaultSource {
associatedtype Value: Decodable
static var defaultValue: Value { get }
}
public enum DecodableDefault {
@propertyWrapper
public struct Wrapper<Source: DecodableDefaultSource> {
public typealias Value = Source.Value
public var wrappedValue = Source.defaultValue
public init() {}
public init(wrappedValue: Value) {
self.wrappedValue = wrappedValue
}
}
}
extension DecodableDefault.Wrapper: Decodable {
public init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
wrappedValue = try container.decode(Value.self)
}
}
extension KeyedDecodingContainer {
func decode<T>(
_ type: DecodableDefault.Wrapper<T>.Type,
forKey key: Key
) throws -> DecodableDefault.Wrapper<T> {
try decodeIfPresent(type, forKey: key) ?? .init()
}
}
public extension DecodableDefault {
typealias Source = DecodableDefaultSource
typealias DecodableBooleanLiteral = Decodable & ExpressibleByBooleanLiteral
typealias DecodableIntegerLiteral = Decodable & ExpressibleByIntegerLiteral
typealias DecodableFloatLiteral = Decodable & ExpressibleByFloatLiteral
typealias DecodableStringLiteral = Decodable & ExpressibleByStringLiteral
typealias DecodableArrayLiteral = Decodable & ExpressibleByArrayLiteral
typealias DecodableDictionaryLiteral = Decodable & ExpressibleByDictionaryLiteral
enum Sources {
public enum True<T: DecodableBooleanLiteral>: Source {
public static var defaultValue: T { true }
}
public enum False<T: DecodableBooleanLiteral>: Source {
public static var defaultValue: T { false }
}
public enum Zero<T: DecodableIntegerLiteral>: Source {
public static var defaultValue: T { 0 }
}
public enum One<T: DecodableIntegerLiteral>: Source {
public static var defaultValue: T { 1 }
}
public enum ZeroFloat<T: DecodableFloatLiteral>: Source {
public static var defaultValue: T { 0.0 }
}
public enum OneFloat<T: DecodableFloatLiteral>: Source {
public static var defaultValue: T { 1.0 }
}
public enum EmptyString<T: DecodableStringLiteral>: Source {
public static var defaultValue: T { "" }
}
public enum EmptyArray<T: DecodableArrayLiteral>: Source {
public static var defaultValue: T { [] }
}
public enum EmptyDictionary<T: DecodableDictionaryLiteral>: Source {
public static var defaultValue: T { [:] }
}
public enum GenerateUUIDString: Source {
public static var defaultValue: String { UUID().uuidString }
}
public enum OutgoingMessageSending: Source {
public static var defaultValue: OWSOutgoingMessageRecipientStatus { .sending }
}
}
}
public extension DecodableDefault {
typealias GenerateUUIDString = Wrapper<Sources.GenerateUUIDString>
typealias OutgoingMessageSending = Wrapper<Sources.OutgoingMessageSending>
typealias True<T: DecodableBooleanLiteral> = Wrapper<Sources.True<T>>
typealias False<T: DecodableBooleanLiteral> = Wrapper<Sources.False<T>>
typealias Zero<T: DecodableIntegerLiteral> = Wrapper<Sources.Zero<T>>
typealias One<T: DecodableIntegerLiteral> = Wrapper<Sources.One<T>>
typealias ZeroFloat<T: DecodableFloatLiteral> = Wrapper<Sources.ZeroFloat<T>>
typealias OneFloat<T: DecodableFloatLiteral> = Wrapper<Sources.OneFloat<T>>
typealias EmptyString<T: DecodableStringLiteral> = Wrapper<Sources.EmptyString<T>>
typealias EmptyArray<T: DecodableArrayLiteral> = Wrapper<Sources.EmptyArray<T>>
typealias EmptyDictionary<T: DecodableDictionaryLiteral> = Wrapper<Sources.EmptyDictionary<T>>
}
extension DecodableDefault.Wrapper: Equatable where Value: Equatable {}
extension DecodableDefault.Wrapper: Hashable where Value: Hashable {}
extension DecodableDefault.Wrapper: Encodable where Value: Encodable {
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(wrappedValue)
}
}
extension DecodableDefault.Wrapper: CustomStringConvertible where Value: CustomStringConvertible {
public var description: String { wrappedValue.description }
}
extension DecodableDefault.Wrapper: CustomDebugStringConvertible where Value: CustomDebugStringConvertible {
public var debugDescription: String { wrappedValue.debugDescription }
}