-
-
Notifications
You must be signed in to change notification settings - Fork 6
Miniserde derive macro that supports enums #10
Comments
I'm on it! 🙂 WIP here: https://github.com/eupn/miniserde-derive-enum |
Nice! Let me know if you have questions. The miniserde serialization and deserialization APIs are unfortunately (even) more confusing than the ones in serde because recursion is such a natural way to express serializing and deserializing nested types and miniserde needs to work without recursion, but the existing miniserde struct derive code and using |
I've started from serialization part and have simple enums serializing with "externally-tagged" strategy working: #[derive(Serialize_enum)]
enum E {
Struct { a: u8, b: u8 },
}
let e = E::Struct { a: 0u8, b: 1u8 };
println!("{}", json::to_string(&e)); Prints: For those I generate a #[derive(Serialize)]
struct __E_Struct_VARIANT_STRUCT<'a> {
a: &'a u8,
b: &'a u8,
} And use already implemented miniserde machinery for structs to serialize that under the tag: Some((miniserde::export::Cow::Borrowed("Struct"), &self.data)), To fill those match self {
E::Struct { ref a, ref b } => {
miniserde::ser::Fragment::Map(Box::new(__E_Struct_VARIANT_STREAM {
data: __E_Struct_VARIANT_STRUCT { a, b },
state: 0,
}))
}
} Using references here and there allowed me to avoid copying, but generics and lifetimes will probably complicate everything or won't work that way. Is my approach sustainable or should I consider doing that differently? And also unit variants support is on the way: #[derive(Serialize_enum)]
enum E {
Unit,
}
let e = E::Unit;
println!("{}", json::to_string(&e)); Prints: |
That seems good to me! I think the generics and lifetimes concerns would equally complicated with any other approach too. |
Simple serialization working 🎉: #[derive(Serialize_enum)]
enum E {
Unit,
Struct { a: u8, b: u8 },
Tuple(u8, String),
}
let s = E::Struct { a: 0u8, b: 1u8 };
let u = E::Unit;
let t = E::Tuple(0u8, "Hello".to_owned());
println!(
"{}\n{}\n{}",
json::to_string(&s),
json::to_string(&u),
json::to_string(&t)
); Produces:
|
Implemented struct enum deserialization 🎉! Now we can do roundtrip ser/de test: #[derive(Debug, Serialize_enum, Deserialize_enum)]
enum E {
A { a: u8, b: Box<E> },
B { b: u8 },
}
let s_a = E::A {
a: 0,
b: Box::new(E::B { b: 1 }), // Nesting is also supported
};
let s_b = E::B { b: 1 };
let json_a = json::to_string(&s_a);
println!("{}", json_a);
let json_b = json::to_string(&s_b);
println!("{}", json_b);
let s_a: E = json::from_str(&json_a).unwrap();
let s_b: E = json::from_str(&json_b).unwrap();
dbg!(s_a, s_b); Will print:
The approach to deserialization is similar to that during serialization. Struct enum variants are represented as structures with |
Both serialization and deserialization are implemented 🎉 and following code compiles: #[derive(Debug, Serialize_enum, Deserialize_enum)]
enum E {
UnitA,
UnitB,
Struct { a: u8, b: Box<E>, c: Box<E> },
Tuple(u8, String),
}
let ua = E::UnitA;
let ub = E::UnitB;
let t = E::Tuple(0, "Hello".to_owned());
let s = E::Struct {
a: 0,
b: Box::new(E::Struct {
a: 42,
b: Box::new(E::UnitA),
c: Box::new(E::Tuple(0, "Test".to_owned())),
}),
c: Box::new(E::UnitB),
};
let json_s = json::to_string(&s);
let json_t = json::to_string(&t);
let json_ua = json::to_string(&ua);
let json_ub = json::to_string(&ub);
println!("{}", json_ua);
println!("{}", json_ub);
println!("{}", json_s);
println!("{}", json_t);
let ua: E = json::from_str(&json_ua).unwrap();
let ub: E = json::from_str(&json_ub).unwrap();
let s: E = json::from_str(&json_s).unwrap();
let t: E = json::from_str(&json_t).unwrap();
dbg!(ua, ub, s, t); and prints when run:
Next thing will be the support for generics. |
I actually started working on this as well a few days ago at https://github.com/etwyniel/miniserde-enum. I only support serialization for now, but in a way that more closely resembles SerDe's behavior, i.e. tuple variants serialize to arrays (or a single value), unit variants serialize to strings. Here are a few examples from my tests: #[serde(tag = "type")]
#[derive(Serialize_enum)]
enum Internal {
A,
#[serde(rename = "renamedB")]
B,
C {
x: i32,
},
}
use Internal::*;
let example = [A, B, C { x: 2 }];
let expected = r#"[{"type":"A"},{"type":"renamedB"},{"type":"C","x":2}]"#; #[derive(Serialize_enum)]
enum External {
A(i32),
#[serde(rename = "renamedB")]
B(i32, String),
C {
x: i32,
},
D,
}
use External::*;
let example = [A(21), B(42, "everything".to_string()), C { x: 2 }, D];
let expected = r#"[{"A":21},{"renamedB":[42,"everything"]},{"C":{"x":2}},"D"]"#; #[serde(untagged)]
#[derive(Serialize_enum)]
enum Untagged {
A(i32),
#[serde(rename = "renamedB")]
B(i32, String),
C {
x: i32,
},
D,
}
use Untagged::*;
let example = [A(21), B(42, "everything".to_string()), C { x: 2 }, D];
let expected = r#"[21,[42,"everything"],{"x":2},"D"]"#; |
Wow that's great progress! I filed a small suggestion in both. Once the crates are documented and released to crates.io, I will close out this issue. |
@dtolnay crate is published: miniserde-derive-enum. |
Terrific! I have added a link from the readme. Thanks for all your work on this library! |
For whatever it's worth, I finally got around to publishing my crate, after adding deserialization for most enum representations (haven't quite figured out how to deserialize untagged enums yet). |
@etwyniel good job! |
The Miniserde built in derive macros keep compile time down by only supporting structs. But for some use cases it is pretty important to be able to serialize and deserialize enums.
I would like a different crate to provide derive macros that implement Miniserde's traits for an enum.
The representation could be whichever of Serde's enum representations is easiest to implement.
The text was updated successfully, but these errors were encountered: