diff --git a/src/annotate.rs b/src/annotate.rs index 18966e8..bfd0652 100644 --- a/src/annotate.rs +++ b/src/annotate.rs @@ -1,6 +1,6 @@ use std::fmt; -use crate::{AnnotatedSerializer, Document, Error}; +use crate::{AnnotatedSerializer, Deserializer, Document, Error}; /// Specifies the formatting options to use when serializing. pub enum Format { @@ -61,18 +61,43 @@ impl Annotate for T { } // We use a private trait to identify whether the Serializer passed to -// serde::Serialize for dyn Annotate is AnnotatedSerializer. -unsafe trait IsAnnotatedSerializer { - fn is_annotated_serializer(&self) -> bool; +// various functions is our Serializer. +pub(crate) unsafe trait IsSerializer { + fn is_serde_annotate(&self) -> bool; } -unsafe impl IsAnnotatedSerializer for T { - default fn is_annotated_serializer(&self) -> bool { +unsafe impl IsSerializer for T { + default fn is_serde_annotate(&self) -> bool { false } } -unsafe impl<'a> IsAnnotatedSerializer for &mut AnnotatedSerializer<'a> { - fn is_annotated_serializer(&self) -> bool { + +unsafe impl<'a> IsSerializer for &mut AnnotatedSerializer<'a> { + fn is_serde_annotate(&self) -> bool { + true + } +} + +// This marker trait is to avoid specifying lifetimes in the default +// implementation. When I specify lifetimes in the default impl, the +// compiler complains that the specialized impl repeats parameter `'de`. +trait _IsDeserializer {} +impl<'de, T: serde::Deserializer<'de>> _IsDeserializer for T {} + +// We use a private trait to identify whether the Deserializer passed to +// various functions is our Deserializer. +pub(crate) unsafe trait IsDeserializer { + fn is_serde_annotate(&self) -> bool; +} + +unsafe impl IsDeserializer for T { + default fn is_serde_annotate(&self) -> bool { + false + } +} + +unsafe impl<'de> IsDeserializer for &mut Deserializer<'de> { + fn is_serde_annotate(&self) -> bool { true } } @@ -89,7 +114,7 @@ unsafe impl<'a> IsAnnotatedSerializer for &mut AnnotatedSerializer<'a> { // AnnotatedSerializer and just force the types with `transmute`. impl serde::Serialize for dyn Annotate { fn serialize(&self, serializer: S) -> Result { - if !serializer.is_annotated_serializer() { + if !serializer.is_serde_annotate() { panic!( "Expected to be called by AnnotatedSerializer, not {:?}", std::any::type_name::() diff --git a/src/de.rs b/src/de.rs index 0da51ab..f39b094 100644 --- a/src/de.rs +++ b/src/de.rs @@ -59,7 +59,7 @@ impl Deserialize { /// A `Deserializer` deserializes a parsed document. pub struct Deserializer<'de> { - doc: &'de Document, + pub(crate) doc: &'de Document, } impl<'de> Deserializer<'de> { diff --git a/src/lib.rs b/src/lib.rs index f180126..301a76a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ mod error; mod hexdump; mod integer; mod json; +mod partial; mod relax; mod ser; mod yaml; @@ -18,7 +19,7 @@ pub use annotate_derive::*; pub use color::ColorProfile; pub use de::{from_str, Deserialize, Deserializer}; pub use doc_iter::DocPath; -pub use document::Document; +pub use document::{BytesFormat, CommentFormat, Document, StrFormat}; pub use error::Error; pub use integer::{Int, IntValue}; pub use json::Json; diff --git a/src/partial.rs b/src/partial.rs new file mode 100644 index 0000000..ac5b727 --- /dev/null +++ b/src/partial.rs @@ -0,0 +1,50 @@ +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::annotate::{IsDeserializer, IsSerializer}; +use crate::Deserializer as AnnotatedDeserializer; +use crate::{Document, Error}; + +impl Serialize for Document { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + if serializer.is_serde_annotate() { + // If `serializer` is the correct type, then we can clone the + // Document node and return it. + let r: Result = Ok(self.clone()); + let result = unsafe { + // We have to transmute because the we can't determine at compile + // time that `Result` is the same type as + // `Result`. If the serializer is + // `AnnotatedSerializer`, then it must be the same. + std::mem::transmute_copy(&r) + }; + std::mem::forget(r); + result + } else { + Err(serde::ser::Error::custom("Serializing document nodes is only supported with serde_annotate::AnnotatedSerializer")) + } + } +} + +impl<'de> Deserialize<'de> for Document { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + if deserializer.is_serde_annotate() { + unsafe { + // If the deserializer is ours, then we can simply clone the + // deserializer's document node. + let dsz: &AnnotatedDeserializer = std::mem::transmute_copy(&deserializer); + std::mem::forget(deserializer); + Ok(dsz.doc.clone()) + } + } else { + Err(serde::de::Error::custom( + "Deserializing document nodes is only supported with serde_annotate::Deserializer", + )) + } + } +} diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 522ea34..49fdffa 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -40,3 +40,15 @@ rust_test( "//third_party/rust/crates:serde", ], ) + +rust_test( + name = "test_partial", + srcs = ["test_partial.rs"], + edition = "2021", + deps = [ + "//:serde_annotate", + "//third_party/rust/crates:anyhow", + "//third_party/rust/crates:serde", + "//third_party/rust/crates:serde_json", + ], +) diff --git a/tests/test_partial.rs b/tests/test_partial.rs new file mode 100644 index 0000000..d3b8011 --- /dev/null +++ b/tests/test_partial.rs @@ -0,0 +1,87 @@ +#![feature(min_specialization)] +use anyhow::Result; +use serde_annotate::serialize; +use serde_annotate::{Document, StrFormat}; + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +struct Partial { + n: i32, + doc: Document, +} + +const SERIALIZE_RESULT: &str = r#"{ + n: 5, + doc: [ + "Hello", + "world" + ] +}"#; + +#[test] +fn test_partial_serialize() -> Result<()> { + let p = Partial { + n: 5, + doc: Document::Sequence(vec![ + Document::String("Hello".into(), StrFormat::Standard), + Document::String("world".into(), StrFormat::Standard), + ]), + }; + let s = serialize(&p)?.to_json5().to_string(); + assert_eq!(s, SERIALIZE_RESULT); + Ok(()) +} + +#[test] +fn test_partial_serialize_error() -> Result<()> { + let p = Partial { + n: 5, + doc: Document::Sequence(vec![ + Document::String("Hello".into(), StrFormat::Standard), + Document::String("world".into(), StrFormat::Standard), + ]), + }; + let s = serde_json::to_string_pretty(&p); + assert!(s.is_err()); + assert_eq!( + s.unwrap_err().to_string(), + "Serializing document nodes is only supported with serde_annotate::AnnotatedSerializer" + ); + Ok(()) +} + +#[test] +fn test_partial_deserialize() -> Result<()> { + let doc = r#"{ + n: 10, + doc: { + # A comment + key: "value", + i: 5, + j: 10, + } + }"#; + let p = serde_annotate::from_str::(doc)?; + assert_eq!(p.n, 10); + let Document::Mapping(m) = p.doc else { + panic!("Expecting Document::Mapping"); + }; + let (k, v) = m[0].as_kv()?; + assert_eq!(k.as_str()?, "key"); + assert_eq!(v.as_str()?, "value"); + let (k, v) = m[1].as_kv()?; + assert_eq!(k.as_str()?, "i"); + assert_eq!(u32::try_from(v)?, 5); + let (k, v) = m[2].as_kv()?; + assert_eq!(k.as_str()?, "j"); + assert_eq!(u32::try_from(v)?, 10); + Ok(()) +} + +#[test] +fn test_partial_deserialize_error() -> Result<()> { + let p = serde_json::from_str::(r#"{"n":5, "doc": []}"#); + assert!(p.is_err()); + assert_eq!(p.unwrap_err().to_string(), + "Deserializing document nodes is only supported with serde_annotate::Deserializer at line 1 column 15"); + Ok(()) +}