Skip to content

Commit 527760b

Browse files
Chris Frantzcfrantz
Chris Frantz
authored andcommitted
Support partial serialization & deserialization
This feature allows writing structs which contain `Document` nodes. - When deserializing, everything under the `Document` node is simply attached to the struct. - When serializing, the `Document` node is passed through unmodified. Signed-off-by: Chris Frantz <cfrantz@google.com>
1 parent 51a55bc commit 527760b

File tree

6 files changed

+187
-11
lines changed

6 files changed

+187
-11
lines changed

src/annotate.rs

+34-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::fmt;
22

3-
use crate::{AnnotatedSerializer, Document, Error};
3+
use crate::{AnnotatedSerializer, Deserializer, Document, Error};
44

55
/// Specifies the formatting options to use when serializing.
66
pub enum Format {
@@ -61,18 +61,43 @@ impl<T: ?Sized + serde::Serialize> Annotate for T {
6161
}
6262

6363
// We use a private trait to identify whether the Serializer passed to
64-
// serde::Serialize for dyn Annotate is AnnotatedSerializer.
65-
unsafe trait IsAnnotatedSerializer {
66-
fn is_annotated_serializer(&self) -> bool;
64+
// various functions is our Serializer.
65+
pub(crate) unsafe trait IsSerializer {
66+
fn is_serde_annotate(&self) -> bool;
6767
}
6868

69-
unsafe impl<T: serde::Serializer> IsAnnotatedSerializer for T {
70-
default fn is_annotated_serializer(&self) -> bool {
69+
unsafe impl<T: serde::Serializer> IsSerializer for T {
70+
default fn is_serde_annotate(&self) -> bool {
7171
false
7272
}
7373
}
74-
unsafe impl<'a> IsAnnotatedSerializer for &mut AnnotatedSerializer<'a> {
75-
fn is_annotated_serializer(&self) -> bool {
74+
75+
unsafe impl<'a> IsSerializer for &mut AnnotatedSerializer<'a> {
76+
fn is_serde_annotate(&self) -> bool {
77+
true
78+
}
79+
}
80+
81+
// This marker trait is to avoid specifying lifetimes in the default
82+
// implementation. When I specify lifetimes in the default impl, the
83+
// compiler complains that the specialized impl repeats parameter `'de`.
84+
trait _IsDeserializer {}
85+
impl<'de, T: serde::Deserializer<'de>> _IsDeserializer for T {}
86+
87+
// We use a private trait to identify whether the Deserializer passed to
88+
// various functions is our Deserializer.
89+
pub(crate) unsafe trait IsDeserializer {
90+
fn is_serde_annotate(&self) -> bool;
91+
}
92+
93+
unsafe impl<T: _IsDeserializer> IsDeserializer for T {
94+
default fn is_serde_annotate(&self) -> bool {
95+
false
96+
}
97+
}
98+
99+
unsafe impl<'de> IsDeserializer for &mut Deserializer<'de> {
100+
fn is_serde_annotate(&self) -> bool {
76101
true
77102
}
78103
}
@@ -89,7 +114,7 @@ unsafe impl<'a> IsAnnotatedSerializer for &mut AnnotatedSerializer<'a> {
89114
// AnnotatedSerializer and just force the types with `transmute`.
90115
impl serde::Serialize for dyn Annotate {
91116
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
92-
if !serializer.is_annotated_serializer() {
117+
if !serializer.is_serde_annotate() {
93118
panic!(
94119
"Expected to be called by AnnotatedSerializer, not {:?}",
95120
std::any::type_name::<S>()

src/de.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ impl Deserialize {
5959

6060
/// A `Deserializer` deserializes a parsed document.
6161
pub struct Deserializer<'de> {
62-
doc: &'de Document,
62+
pub(crate) doc: &'de Document,
6363
}
6464

6565
impl<'de> Deserializer<'de> {

src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ mod error;
99
mod hexdump;
1010
mod integer;
1111
mod json;
12+
mod partial;
1213
mod relax;
1314
mod ser;
1415
mod yaml;
@@ -18,7 +19,7 @@ pub use annotate_derive::*;
1819
pub use color::ColorProfile;
1920
pub use de::{from_str, Deserialize, Deserializer};
2021
pub use doc_iter::DocPath;
21-
pub use document::Document;
22+
pub use document::{BytesFormat, CommentFormat, Document, StrFormat};
2223
pub use error::Error;
2324
pub use integer::{Int, IntValue};
2425
pub use json::Json;

src/partial.rs

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
2+
3+
use crate::annotate::{IsDeserializer, IsSerializer};
4+
use crate::Deserializer as AnnotatedDeserializer;
5+
use crate::{Document, Error};
6+
7+
impl Serialize for Document {
8+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
9+
where
10+
S: Serializer,
11+
{
12+
if serializer.is_serde_annotate() {
13+
unsafe {
14+
// If `serializer` is the correct type, then we can clone the
15+
// Document node and return it.
16+
let r: Result<Document, Error> = Ok(self.clone());
17+
// We have to transmute because the we can't determine at compile
18+
// time that `Result<Document, Error>` is the same type as
19+
// `Result<S::Ok, S::Error>`. If the serializer is
20+
// `AnnotatedSerializer`, then it must be the same.
21+
let result = std::mem::transmute_copy(&r);
22+
std::mem::forget(r);
23+
result
24+
}
25+
} else {
26+
Err(serde::ser::Error::custom("Serializing document nodes is only supported with serde_annotate::AnnotatedSerializer"))
27+
}
28+
}
29+
}
30+
31+
impl<'de> Deserialize<'de> for Document {
32+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
33+
where
34+
D: Deserializer<'de>,
35+
{
36+
if deserializer.is_serde_annotate() {
37+
unsafe {
38+
// If the deserializer is ours, then we can simply clone the
39+
// deserializer's document node.
40+
let dsz: &AnnotatedDeserializer = std::mem::transmute_copy(&deserializer);
41+
std::mem::forget(deserializer);
42+
Ok(dsz.doc.clone())
43+
}
44+
} else {
45+
Err(serde::de::Error::custom(
46+
"Deserializing document nodes is only supported with serde_annotate::Deserializer",
47+
))
48+
}
49+
}
50+
}

tests/BUILD.bazel

+12
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,15 @@ rust_test(
4040
"//third_party/rust/crates:serde",
4141
],
4242
)
43+
44+
rust_test(
45+
name = "test_partial",
46+
srcs = ["test_partial.rs"],
47+
edition = "2021",
48+
deps = [
49+
"//:serde_annotate",
50+
"//third_party/rust/crates:anyhow",
51+
"//third_party/rust/crates:serde",
52+
"//third_party/rust/crates:serde_json",
53+
],
54+
)

tests/test_partial.rs

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#![feature(min_specialization)]
2+
use anyhow::Result;
3+
use serde_annotate::serialize;
4+
use serde_annotate::{Document, StrFormat};
5+
6+
#[derive(Debug, serde::Serialize, serde::Deserialize)]
7+
struct Partial {
8+
n: i32,
9+
doc: Document,
10+
}
11+
12+
const SERIALIZE_RESULT: &str = r#"{
13+
n: 5,
14+
doc: [
15+
"Hello",
16+
"world"
17+
]
18+
}"#;
19+
20+
#[test]
21+
fn test_partial_serialize() -> Result<()> {
22+
let p = Partial {
23+
n: 5,
24+
doc: Document::Sequence(vec![
25+
Document::String("Hello".into(), StrFormat::Standard),
26+
Document::String("world".into(), StrFormat::Standard),
27+
]),
28+
};
29+
let s = serialize(&p)?.to_json5().to_string();
30+
assert_eq!(s, SERIALIZE_RESULT);
31+
Ok(())
32+
}
33+
34+
#[test]
35+
fn test_partial_serialize_error() -> Result<()> {
36+
let p = Partial {
37+
n: 5,
38+
doc: Document::Sequence(vec![
39+
Document::String("Hello".into(), StrFormat::Standard),
40+
Document::String("world".into(), StrFormat::Standard),
41+
]),
42+
};
43+
let s = serde_json::to_string_pretty(&p);
44+
assert!(s.is_err());
45+
assert_eq!(
46+
s.unwrap_err().to_string(),
47+
"Serializing document nodes is only supported with serde_annotate::AnnotatedSerializer"
48+
);
49+
Ok(())
50+
}
51+
52+
#[test]
53+
fn test_partial_deserialize() -> Result<()> {
54+
let doc = r#"{
55+
n: 10,
56+
doc: {
57+
# A comment
58+
key: "value",
59+
i: 5,
60+
j: 10,
61+
}
62+
}"#;
63+
let p = serde_annotate::from_str::<Partial>(doc)?;
64+
assert_eq!(p.n, 10);
65+
if let Document::Mapping(m) = p.doc {
66+
let (k, v) = m[0].as_kv()?;
67+
assert_eq!(k.as_str()?, "key");
68+
assert_eq!(v.as_str()?, "value");
69+
let (k, v) = m[1].as_kv()?;
70+
assert_eq!(k.as_str()?, "i");
71+
assert_eq!(u32::try_from(v)?, 5);
72+
let (k, v) = m[2].as_kv()?;
73+
assert_eq!(k.as_str()?, "j");
74+
assert_eq!(u32::try_from(v)?, 10);
75+
} else {
76+
assert!(false, "Expecting Document::Mapping");
77+
}
78+
Ok(())
79+
}
80+
81+
#[test]
82+
fn test_partial_deserialize_error() -> Result<()> {
83+
let p = serde_json::from_str::<Partial>(r#"{"n":5, "doc": []}"#);
84+
assert!(p.is_err());
85+
assert_eq!(p.unwrap_err().to_string(),
86+
"Deserializing document nodes is only supported with serde_annotate::Deserializer at line 1 column 15");
87+
Ok(())
88+
}

0 commit comments

Comments
 (0)