We can't find the internet
Attempting to reconnect
Something went wrong!
Hang in there while we get back on track
Serialise and deserialise enums with named associated values from Rust → Swift
2023-01-02 ・ swift, rust, json
Between Rust and Serde and Swift and Codable
, it's relatively easy to serialise and deserialise between the 2, using JSON. Whilst there aren't shared definitions through a common format, such as Protobuf or MessagePack, for simple data it looks to be maintainable.
serde_derive
and Codable
ideally save you writing encoders/serialisers and decoders/deserialisers. For Rust → Swift, I've so far had to write a decoder for enums with named associated values. Both Rust and Swift use nth-indexing for unnamed associated values, so I don't think it would be too hard. Without associated values, decoding worked without having to write anything for decoding.
Here are small snippets of the types and decoder.
Rust1:
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
pub enum State {
Paused { duration: Duration },
Stopped,
Working { duration: Duration },
TakingShortBreak { duration: Duration },
TakingLongBreak { duration: Duration },
}
Swift2:
@available(macOS 13.0, *)
enum State: Codable {
case Paused(duration: Duration)
case Stopped
case Working(duration: Duration)
case TakingShortBreak(duration: Duration)
case TakingLongBreak(duration: Duration)
enum CodingKeys: String, CodingKey {
case Paused
case Stopped
case Working
case TakingShortBreak
case TakingLongBreak
}
enum AdditionalCodingKeys: String, CodingKey {
case duration
case secs
case nanos
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let state = try? container.decode(String.self) {
// For when it's "state":"Stopped"
switch state {
case "Stopped":
self = .Stopped
default:
fatalError("Unexpected value \(state)")
}
} else {
// For when "state":{"Working":{"duration":{"secs":1,"nanos":0}}}
let values = try decoder.container(keyedBy: CodingKeys.self)
// Dynamically get the CodingKey for the State from its enum
let stateKey = values.allKeys.first!
let stateContainer = try values.nestedContainer(
keyedBy: AdditionalCodingKeys.self, forKey: stateKey
)
let durationKey = stateContainer.allKeys.first!
let durationContainer = try stateContainer.nestedContainer(
keyedBy: AdditionalCodingKeys.self, forKey: durationKey
)
let nanos = try durationContainer.decode(Int.self, forKey: .nanos)
let secs = try durationContainer.decode(Int.self, forKey: .secs)
let duration = Duration.nanoseconds(nanos) + Duration.seconds(secs)
let state: State = {
switch stateKey.stringValue {
case "Paused":
return State.Paused(duration: duration)
case "Working":
return State.Working(duration: duration)
case "TakingShortBreak":
return State.TakingShortBreak(duration: duration)
case "TakingLongBreak":
return State.TakingLongBreak(duration: duration)
default:
fatalError("Unexpected value \(stateKey.stringValue)")
}
}()
// Fake
self = state
}
}
}