Design a good API for non-readable [de]serialization in serde_test
Marwes opened this issue · 4 comments
The current API for creating serializers and deserializers aren't ideal and is therefore hidden. It is needed though so that proper tests can be created for crates which provide non-readable serializations.
Here is an idea that uses the same mechanism as serde-rs/serde#1044 (comment) but is more nicely extensible.
struct Readable<T: ?Sized>(T);
struct Compact<T: ?Sized>(T);
trait Configure {
fn readable(&self) -> &Readable<Self> { /* ... */ }
fn compact(&self) -> &Compact<Self> { /* ... */ }
}
impl<T: ?Sized> Configure for T {}
impl<T: ?Sized> Serialize for Readable<T> where T: Serialize { /* ... */ }
impl<T: ?Sized> Serialize for Compact<T> where T: Serialize { /* ... */ }
impl<'de, T> Deserialize<'de> for Readable<T> where T: Deserialize<'de> { /* ... */ }
impl<'de, T> Deserialize<'de> for Compact<T> where T: Deserialize<'de> { /* ... */ }
use serde_test::Configure;
let s = /* ... */;
// In human readable formats, field `timestamp` serializes as ISO 8601.
assert_tokens(s.readable(), &[
Token::Struct { name: "S", len: 1 },
Token::Str("timestamp"),
Token::String("2017-09-26T21:49:25Z"),
Token::StructEnd,
]);
// In binary formats, field `timestamp` serializes in compact byte form.
assert_tokens(s.compact(), &[
Token::Struct { name: "S", len: 1 },
Token::Str("timestamp"),
Token::Bytes(&[226, 152, 131]),
Token::StructEnd,
]);
I ended up doing the thing where it would panic in serde_test if the type tries to check whether the format is human-readable. I don't think this will be disruptive, but we can revisit if I am mistaken.
@dtolnay Looked into this a bit but found that #12 and serde-rs/serde#1044 (comment) needs a lot of boilerplate to implement. This is because overriding is_human_readable
requires all methods on Serializer, SerializeSeq, Deserializer, MapAccess
as well as their associated types need to be overridden which is trivial in some cases but not all as the inner [de]serializer needs to re-wrapped withReadable
or Compact
wrapper in some methods. macros alleviate this somewhat but we are still talking of several hundred lines of boiler plate.
Am I missing something here? While it would arguably give a nice API the implementation takes a lot of effort for something this small and worse, it doesn't scale (easily) if more configuration were to be added later.
Start of implementation
https://gist.github.com/Marwes/c09c42899d01362d682daa67fecd34a7
Generally I have tried to optimize for nice APIs over effort of implementation. I have not found several hundred lines of boilerplate to be a maintenance burden. "Serializer adapters" where one serializer wraps another one is a really powerful concept -- serde-ignored
and erased-serde
and serde-encrypted-value
are other examples of this pattern.