/// For a job to be decodable, it has to be registered. Call `MyJob.register()` to register a job.
public protocol HBJob: Codable {
/// Unique Job name
static var name: String { get }
}
extension HBJob {
/// register job
public static func register() {
HBJobRegister.register(job: Self.self)
}
}
/// Register Jobs, for decoding and encoding
enum HBJobRegister {
static func decode(from decoder: Decoder) throws -> HBJob {
let container = try decoder.container(keyedBy: _HBJobCodingKey.self)
// get key and use as lookup into JobRegister
guard let key = container.allKeys.first,
let jobType = HBJobRegister.nameTypeMap[key.stringValue] else { throw JobQueueError.decodeJobFailed }
let childDecoder = try container.superDecoder(forKey: key)
return try jobType.init(from: childDecoder)
}
static func encode(job: HBJob, to encoder: Encoder) throws {
var container = encoder.container(keyedBy: _HBJobCodingKey.self)
let childEncoder = container.superEncoder(forKey: .init(stringValue: type(of: job).name, intValue: nil))
try job.encode(to: childEncoder)
}
static func register(job: HBJob.Type) {
self.nameTypeMap[job.name] = job
}
static var nameTypeMap: [String: HBJob.Type] = [:]
}
internal struct _HBJobCodingKey: CodingKey {
public var stringValue: String
public var intValue: Int?
public init?(stringValue: String) {
self.stringValue = stringValue
self.intValue = nil
}
public init?(intValue: Int) {
self.stringValue = "\(intValue)"
self.intValue = intValue
}
public init(stringValue: String, intValue: Int?) {
self.stringValue = stringValue
self.intValue = intValue
}
internal init(index: Int) {
self.stringValue = "Index \(index)"
self.intValue = index
}
}
/// To encode/decode Job needs to be in a container type because you cannot decode `HBJob` as it does conform to `HBJob`.
public struct HBJobContainer: Codable {
/// Job data
public let job: HBJob
/// Initialize a queue job
init(_ job: HBJob) {
self.job = job
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let jobDecoder = try container.superDecoder(forKey: .job)
self.job = try HBJobRegister.decode(from: jobDecoder)
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
let jobEncoder = container.superEncoder(forKey: .job)
try HBJobRegister.encode(job: self.job, to: jobEncoder)
}
private enum CodingKeys: String, CodingKey {
case job
}
}
Last active
October 26, 2021 10:21
-
-
Save adam-fowler/28f7a5d5acc1c251a34a02b7ee702dac to your computer and use it in GitHub Desktop.
Decoding/Encoding of Polymorphic payloads (taken from Job implementation in https://github.com/hummingbird-project/hummingbird)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment