From 65680deb92e8ad74db8ec7821c80265653ae9247 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Haudebourg?= Date: Thu, 17 Aug 2023 16:28:25 +0200 Subject: [PATCH] Add serde code gen. --- examples/iri.rs | 3 +- examples/uri.rs | 3 +- src/attribute.rs | 7 +- src/lib.rs | 168 +++++++++++++++++++++++++++++++++++++++++++++++ src/options.rs | 2 + 5 files changed, 180 insertions(+), 3 deletions(-) diff --git a/examples/iri.rs b/examples/iri.rs index d351d65..4fbcb75 100644 --- a/examples/iri.rs +++ b/examples/iri.rs @@ -5,7 +5,8 @@ use static_regular_grammar::RegularGrammar; #[grammar( file = "examples/iri.abnf", cache = "target/examples/iri.automaton.cbor", - sized(IriBuf, derive(PartialEq, Eq)) + sized(IriBuf, derive(PartialEq, Eq)), + serde )] pub struct Iri(str); diff --git a/examples/uri.rs b/examples/uri.rs index c3310f5..d2a19d7 100644 --- a/examples/uri.rs +++ b/examples/uri.rs @@ -65,7 +65,8 @@ use static_regular_grammar::RegularGrammar; #[derive(RegularGrammar, PartialEq, Eq)] #[grammar( cache = "target/examples/uri.automaton.cbor", - sized(AuthorityBuf, derive(PartialEq, Eq)) + sized(AuthorityBuf, derive(PartialEq, Eq)), + serde )] pub struct Authority([u8]); diff --git a/src/attribute.rs b/src/attribute.rs index 536074e..f66f9f7 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -33,6 +33,7 @@ pub struct Attribute { pub no_borrow: bool, pub ascii: bool, pub disable: bool, + pub serde: bool, } impl Attribute { @@ -48,7 +49,8 @@ impl Attribute { self.no_deref |= other.no_deref; self.no_borrow |= other.no_borrow; self.ascii |= other.ascii; - self.disable |= other.disable + self.disable |= other.disable; + self.serde |= other.serde } } @@ -61,6 +63,7 @@ enum AttributeItem { NoBorrow, Ascii, Disable, + Serde, Separator, } @@ -75,6 +78,7 @@ impl AttributeItem { TokenTree::Ident(id) if id == "no_borrow" => Ok(Self::NoBorrow), TokenTree::Ident(id) if id == "ascii" => Ok(Self::Ascii), TokenTree::Ident(id) if id == "disable" => Ok(Self::Disable), + TokenTree::Ident(id) if id == "serde" => Ok(Self::Serde), TokenTree::Punct(_) => Ok(Self::Separator), t => { let span = t.span(); @@ -181,6 +185,7 @@ impl Attribute { } AttributeItem::Ascii => result.ascii = true, AttributeItem::Disable => result.disable = true, + AttributeItem::Serde => result.serde = true, } } diff --git a/src/lib.rs b/src/lib.rs index ad6f55a..80b9eb6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -570,6 +570,77 @@ fn generate_typed( }); } + if data.options.serde { + let serialize = if T::UNICODE || ascii { + quote! { + serializer.serialize_str(self.as_str()) + } + } else { + quote! { + serializer.serialize_bytes(self.as_bytes()) + } + }; + + let visit_bytes = if T::UNICODE { + quote! { + match std::str::from_utf8(v) { + Ok(s) => #ident::new(s).map_err(|_| ()), + Err(e) => Err(()) + } + } + } else { + quote! { + #ident::new(v) + } + }; + + let expected = format!("some {ident}"); + + tokens.extend(quote! { + impl ::serde::Serialize for #ident { + fn serialize(&self, serializer: S) -> Result + where + S: ::serde::Serializer + { + #serialize + } + } + + impl<'de> ::serde::Deserialize<'de> for &'de #ident { + fn deserialize(deserializer: D) -> Result + where + D: ::serde::Deserializer<'de> + { + struct Visitor; + + impl<'de> ::serde::de::Visitor<'de> for Visitor { + type Value = &'de #ident; + + fn expecting(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, #expected) + } + + fn visit_borrowed_str(self, v: &'de str) -> Result + where + E: ::serde::de::Error + { + #ident::new(v).map_err(|_| E::invalid_value(::serde::de::Unexpected::Str(v), &self)) + } + + fn visit_borrowed_bytes(self, v: &'de [u8]) -> Result + where + E: ::serde::de::Error + { + #visit_bytes.map_err(|_| E::invalid_value(::serde::de::Unexpected::Bytes(v), &self)) + } + } + + deserializer.deserialize_str(Visitor) + } + } + }) + } + if let Some(buffer) = data.options.sized { let buffer_ident = buffer.ident; let owned_string_type = T::rust_owned_string_type(); @@ -885,6 +956,103 @@ fn generate_typed( }); } } + + if data.options.serde { + let serialize = if T::UNICODE || ascii { + quote! { + serializer.serialize_str(self.as_str()) + } + } else { + quote! { + serializer.serialize_bytes(self.as_bytes()) + } + }; + + let (visit_str, visit_bytes) = if T::UNICODE { + ( + quote! { + #buffer_ident::new(v) + }, + quote! { + match ::std::string::String::from_utf8(v) { + Ok(s) => #buffer_ident::new(s).map_err(|#error(s)| #error(s.into_bytes())), + Err(e) => Err(#error(e.into_bytes())) + } + }, + ) + } else { + ( + quote! { + #buffer_ident::new(v.into_bytes()).map_err(|#error(bytes)| unsafe { + #error(::std::string::String::from_utf8_unchecked(bytes)) + }) + }, + quote! { + #buffer_ident::new(v) + }, + ) + }; + + let expected = format!("some {ident}"); + + tokens.extend(quote! { + impl ::serde::Serialize for #buffer_ident { + fn serialize(&self, serializer: S) -> Result + where + S: ::serde::Serializer + { + #serialize + } + } + + impl<'de> ::serde::Deserialize<'de> for #buffer_ident { + fn deserialize(deserializer: D) -> Result + where + D: ::serde::Deserializer<'de> + { + struct Visitor; + + impl<'de> ::serde::de::Visitor<'de> for Visitor { + type Value = #buffer_ident; + + fn expecting(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!(f, #expected) + } + + fn visit_str(self, v: &str) -> Result + where + E: ::serde::de::Error + { + self.visit_string(v.to_string()) + } + + fn visit_bytes(self, v: &[u8]) -> Result + where + E: ::serde::de::Error + { + self.visit_byte_buf(v.to_vec()) + } + + fn visit_string(self, v: String) -> Result + where + E: ::serde::de::Error + { + #visit_str.map_err(|#error(v)| E::invalid_value(::serde::de::Unexpected::Str(&v), &self)) + } + + fn visit_byte_buf(self, v: Vec) -> Result + where + E: ::serde::de::Error + { + #visit_bytes.map_err(|#error(v)| E::invalid_value(::serde::de::Unexpected::Bytes(&v), &self)) + } + } + + deserializer.deserialize_str(Visitor) + } + } + }) + } } Ok(tokens) diff --git a/src/options.rs b/src/options.rs index 6d968df..5cc7cbc 100644 --- a/src/options.rs +++ b/src/options.rs @@ -13,6 +13,7 @@ pub struct Options { pub no_borrow: bool, pub ascii: bool, pub disable: bool, + pub serde: bool, } impl Options { @@ -29,6 +30,7 @@ impl Options { no_borrow: attr.no_borrow, ascii: attr.ascii, disable: attr.disable, + serde: attr.serde, }) } }