Skip to content

Commit

Permalink
Add serde code gen.
Browse files Browse the repository at this point in the history
  • Loading branch information
timothee-haudebourg committed Aug 17, 2023
1 parent d33fbe5 commit 65680de
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 3 deletions.
3 changes: 2 additions & 1 deletion examples/iri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 2 additions & 1 deletion examples/uri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]);

Expand Down
7 changes: 6 additions & 1 deletion src/attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct Attribute {
pub no_borrow: bool,
pub ascii: bool,
pub disable: bool,
pub serde: bool,
}

impl Attribute {
Expand All @@ -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
}
}

Expand All @@ -61,6 +63,7 @@ enum AttributeItem {
NoBorrow,
Ascii,
Disable,
Serde,
Separator,
}

Expand All @@ -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();
Expand Down Expand Up @@ -181,6 +185,7 @@ impl Attribute {
}
AttributeItem::Ascii => result.ascii = true,
AttributeItem::Disable => result.disable = true,
AttributeItem::Serde => result.serde = true,
}
}

Expand Down
168 changes: 168 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,77 @@ fn generate_typed<T: Token>(
});
}

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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer
{
#serialize
}
}

impl<'de> ::serde::Deserialize<'de> for &'de #ident {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
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<E>(self, v: &'de str) -> Result<Self::Value, E>
where
E: ::serde::de::Error
{
#ident::new(v).map_err(|_| E::invalid_value(::serde::de::Unexpected::Str(v), &self))
}

fn visit_borrowed_bytes<E>(self, v: &'de [u8]) -> Result<Self::Value, E>
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();
Expand Down Expand Up @@ -885,6 +956,103 @@ fn generate_typed<T: Token>(
});
}
}

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<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ::serde::Serializer
{
#serialize
}
}

impl<'de> ::serde::Deserialize<'de> for #buffer_ident {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
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<E>(self, v: &str) -> Result<Self::Value, E>
where
E: ::serde::de::Error
{
self.visit_string(v.to_string())
}

fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: ::serde::de::Error
{
self.visit_byte_buf(v.to_vec())
}

fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: ::serde::de::Error
{
#visit_str.map_err(|#error(v)| E::invalid_value(::serde::de::Unexpected::Str(&v), &self))
}

fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
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)
Expand Down
2 changes: 2 additions & 0 deletions src/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct Options {
pub no_borrow: bool,
pub ascii: bool,
pub disable: bool,
pub serde: bool,
}

impl Options {
Expand All @@ -29,6 +30,7 @@ impl Options {
no_borrow: attr.no_borrow,
ascii: attr.ascii,
disable: attr.disable,
serde: attr.serde,
})
}
}
Expand Down

0 comments on commit 65680de

Please sign in to comment.