1 // Copyright 2022-present 650 Industries. All rights reserved. 2 3 /** 4 A protocol that allows converting raw values to enum cases. 5 */ 6 public protocol Enumerable: AnyArgument, CaseIterable { 7 /** 8 Tries to create an enum case using given raw value. 9 May throw errors, e.g. when the raw value doesn't match any case. 10 */ create<RawValueType>null11 static func create<RawValueType>(fromRawValue rawValue: RawValueType) throws -> Self 12 13 /** 14 Returns an array of all raw values available in the enum. 15 */ 16 static var allRawValues: [Any] { get } 17 18 /** 19 Type-erased enum's raw value. 20 */ 21 var anyRawValue: Any { get } 22 } 23 24 @available(*, deprecated, renamed: "Enumerable") 25 public typealias EnumArgument = Enumerable 26 27 /** 28 Extension for `Enumerable` that also conforms to `RawRepresentable`. 29 This constraint allows us to reference the associated `RawValue` type. 30 */ 31 public extension Enumerable where Self: RawRepresentable, Self: Hashable { create<ArgType>null32 static func create<ArgType>(fromRawValue rawValue: ArgType) throws -> Self { 33 guard let rawValue = rawValue as? RawValue else { 34 throw EnumCastingException((type: RawValue.self, value: rawValue)) 35 } 36 guard let enumCase = Self.init(rawValue: rawValue) else { 37 throw EnumNoSuchValueException((type: Self.self, value: rawValue)) 38 } 39 return enumCase 40 } 41 42 var anyRawValue: Any { 43 rawValue 44 } 45 46 static var allRawValues: [Any] { 47 return allCases.map { $0.rawValue } 48 } 49 } 50 51 /** 52 An error that is thrown when the value cannot be cast to associated `RawValue`. 53 */ 54 internal class EnumCastingException: GenericException<(type: Any.Type, value: Any)> { 55 override var reason: String { 56 "Unable to cast '\(param.value)' to expected type \(param.type)" 57 } 58 } 59 60 /** 61 An error that is thrown when the value doesn't match any available case. 62 */ 63 internal class EnumNoSuchValueException: GenericException<(type: Enumerable.Type, value: Any)> { 64 var allRawValuesFormatted: String { 65 return param.type.allRawValues 66 .map { "'\($0)'" } 67 .joined(separator: ", ") 68 } 69 70 override var reason: String { 71 "'\(param.value)' is not present in \(param.type) enum, it must be one of: \(allRawValuesFormatted)" 72 } 73 } 74