Last active
March 21, 2025 09:40
-
-
Save WorldDownTown/b2a8fe00252678ffbf6e3db81f0aae53 to your computer and use it in GitHub Desktop.
EitherID protocol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
enum Either<L, R> { | |
case left(L) | |
case right(R) | |
} | |
extension Either: Equatable where L: Equatable, R: Equatable {} | |
extension Either: Hashable where L: Hashable, R: Hashable {} | |
extension Either: Codable where L: Codable, R: Codable { | |
struct NeitherError: Error {} | |
init(from decoder: Decoder) throws { | |
let container: SingleValueDecodingContainer = try decoder.singleValueContainer() | |
if let l = try? container.decode(L.self) { | |
self = .left(l) | |
} else if let r = try? container.decode(R.self) { | |
self = .right(r) | |
} else { | |
throw NeitherError() | |
} | |
} | |
func encode(to encoder: Encoder) throws { | |
var container: SingleValueEncodingContainer = encoder.singleValueContainer() | |
switch self { | |
case .left(let l): | |
try container.encode(l) | |
case .right(let r): | |
try container.encode(r) | |
} | |
} | |
} | |
protocol IDProtocol: RawRepresentable, Codable, Hashable {} | |
extension IDProtocol { | |
init(_ value: RawValue) { | |
self.init(rawValue: value)! | |
} | |
} | |
protocol EitherID: IDProtocol where RawValue == Either<L, R> { | |
associatedtype L: Codable, Hashable | |
associatedtype R: Codable, Hashable | |
} | |
extension EitherID { | |
init(from decoder: Decoder) throws { | |
let container: SingleValueDecodingContainer = try decoder.singleValueContainer() | |
self.init(try container.decode(Either<L, R>.self)) | |
} | |
func encode(to encoder: Encoder) throws { | |
var container: SingleValueEncodingContainer = encoder.singleValueContainer() | |
switch rawValue { | |
case .left(let l): | |
try container.encode(l) | |
case .right(let r): | |
try container.encode(r) | |
} | |
} | |
} | |
extension EitherID where Self: ExpressibleByIntegerLiteral, L == Int { | |
init(integerLiteral value: Int) { | |
self.init(.left(value)) | |
} | |
} | |
extension EitherID where Self: ExpressibleByIntegerLiteral, R == Int { | |
init(integerLiteral value: Int) { | |
self.init(.right(value)) | |
} | |
} | |
extension EitherID where Self: ExpressibleByStringLiteral, L == String { | |
init(stringLiteral value: String) { | |
self.init(.left(value)) | |
} | |
} | |
extension EitherID where Self: ExpressibleByStringLiteral, R == String { | |
init(stringLiteral value: String) { | |
self.init(.right(value)) | |
} | |
} | |
protocol IntOrStringID: EitherID, ExpressibleByIntegerLiteral, ExpressibleByStringLiteral where L == Int, R == String {} | |
struct User: Codable { | |
let id: ID | |
struct ID: IntOrStringID { | |
let rawValue: Either<Int, String> | |
/// APIリクエストの値とかに使う | |
var stringValue: String { | |
switch rawValue { | |
case .left(let v): "\(v)" | |
case .right(let v): v | |
} | |
} | |
} | |
} | |
do { | |
// id: number | |
let user1: User = try JSONDecoder().decode(User.self, from: Data(#"{"id":1}"#.utf8)) | |
print(user1) | |
// User(id: __lldb_expr_19.User.ID(rawValue: __lldb_expr_19.Either<Swift.Int, Swift.String>.left(1))) | |
let data1: Data = try JSONEncoder().encode(user1) | |
print(String(data: data1, encoding: .utf8)!) | |
// {"id":1} | |
// id: string | |
let user2: User = try JSONDecoder().decode(User.self, from: Data(#"{"id":"1"}"#.utf8)) | |
print(user2) | |
// User(id: __lldb_expr_19.User.ID(rawValue: __lldb_expr_19.Either<Swift.Int, Swift.String>.right("1"))) | |
let data2: Data = try JSONEncoder().encode(user2) | |
print(String(data: data2, encoding: .utf8)!) | |
// {"id":"1"} | |
} catch { | |
print(error) | |
print(error.localizedDescription) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment