// Copyright © 2020 Metabolist. All rights reserved. import Foundation // Thank you https://www.swiftbysundell.com/tips/default-decoding-values/ protocol DecodableDefaultSource { associatedtype Value: Decodable static var defaultValue: Value { get } } enum DecodableDefault {} // swiftlint:disable nesting extension DecodableDefault { @propertyWrapper struct Wrapper { typealias Value = Source.Value var wrappedValue = Source.defaultValue } } extension DecodableDefault { typealias Source = DecodableDefaultSource typealias List = Decodable & ExpressibleByArrayLiteral typealias Map = Decodable & ExpressibleByDictionaryLiteral enum Sources { enum True: Source { static var defaultValue: Bool { true } } enum False: Source { static var defaultValue: Bool { false } } enum EmptyString: Source { static var defaultValue: String { "" } } enum EmptyList: Source { static var defaultValue: T { [] } } enum EmptyMap: Source { static var defaultValue: T { [:] } } enum Zero: Source { static var defaultValue: Int { 0 } } enum StatusVisibilityPublic: Source { static var defaultValue: Status.Visibility { .public } } enum ExpandMediaDefault: Source { static var defaultValue: MastodonPreferences.ExpandMedia { .default } } } } // swiftlint:enable nesting extension DecodableDefault { typealias True = Wrapper typealias False = Wrapper typealias EmptyString = Wrapper typealias EmptyList = Wrapper> typealias EmptyMap = Wrapper> typealias Zero = Wrapper typealias StatusVisibilityPublic = Wrapper typealias ExpandMediaDefault = Wrapper } extension DecodableDefault.Wrapper: Decodable { init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() wrappedValue = try container.decode(Value.self) } } extension DecodableDefault.Wrapper: Equatable where Value: Equatable {} extension DecodableDefault.Wrapper: Hashable where Value: Hashable {} extension DecodableDefault.Wrapper: Encodable where Value: Encodable { func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(wrappedValue) } } extension KeyedDecodingContainer { func decode(_ type: DecodableDefault.Wrapper.Type, forKey key: Key) throws -> DecodableDefault.Wrapper { try decodeIfPresent(type, forKey: key) ?? .init() } }