mod fields;
use proc_macro2::TokenStream;
use quote::ToTokens;
use syn::{
parse::{Parse, ParseStream},
Generics, Ident,
};
use syn::{Attribute, Field, ItemEnum, ItemStruct, Meta, NestedMeta};
use fields::BigArrayField;
fn check_if_serializing_deserializing<'a>(
attrs: impl Iterator<Item = &'a Attribute>,
) -> (bool, bool) {
let mut serialize = false;
let mut deserialize = false;
for derive_attr in attrs.filter(|attr| attr.path.is_ident("derive")) {
if let Meta::List(derive_attr) = derive_attr.parse_meta().unwrap() {
for derive in derive_attr.nested {
if let NestedMeta::Meta(Meta::Path(path)) = derive {
if let Some(last_segment) = path.segments.iter().last() {
if last_segment.ident == "Serialize" {
serialize = true;
} else if last_segment.ident == "Deserialize" {
deserialize = true;
}
}
};
}
}
}
(serialize, deserialize)
}
pub struct Context {
pub type_name: String,
pub serialize: bool,
pub deserialize: bool,
pub generics: Generics,
}
pub enum Item {
Struct(ItemStruct),
Enum(ItemEnum),
}
impl Item {
pub fn ident(&self) -> &Ident {
match self {
Item::Struct(s) => &s.ident,
Item::Enum(e) => &e.ident,
}
}
pub fn context(&self) -> Context {
let (serialize, deserialize) = check_if_serializing_deserializing(self.attrs());
Context {
type_name: self.ident().to_string(),
serialize,
deserialize,
generics: self.generics().clone(),
}
}
pub fn big_array_fields(&mut self) -> impl Iterator<Item = BigArrayField> {
self.fields().filter_map(BigArrayField::parse_field)
}
fn fields(&mut self) -> impl Iterator<Item = &mut Field> {
let result: Box<dyn Iterator<Item = &mut Field>> = match self {
Item::Struct(s) => Box::new(s.fields.iter_mut()),
Item::Enum(e) => {
let outer_iter = e.variants.iter_mut();
let result = outer_iter.map(|v| v.fields.iter_mut()).flatten();
Box::new(result)
}
};
result
}
fn generics(&self) -> &Generics {
match self {
Item::Struct(s) => &s.generics,
Item::Enum(e) => &e.generics,
}
}
fn attrs(&self) -> impl Iterator<Item = &Attribute> {
match self {
Item::Struct(s) => s.attrs.iter(),
Item::Enum(e) => e.attrs.iter(),
}
}
}
impl Parse for Item {
fn parse(input: ParseStream) -> syn::Result<Self> {
let item: syn::Item = input.parse()?;
match item {
syn::Item::Struct(s) => Ok(Self::Struct(s)),
syn::Item::Enum(e) => Ok(Self::Enum(e)),
_ => Err(syn::Error::new(
input.span(),
"serbia accepts only enums or structs",
)),
}
}
}
impl ToTokens for Item {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Item::Struct(s) => s.to_tokens(tokens),
Item::Enum(e) => e.to_tokens(tokens),
}
}
}
#[cfg(test)]
mod tests {
use syn::{parse_quote, Attribute, ItemStruct};
use super::{check_if_serializing_deserializing, BigArrayField};
#[test]
fn parse_big_array_len() {
let s: ItemStruct = parse_quote! {
struct S {
a: String,
b: [u32; 32],
c: [u32; 33],
}
};
let mut fields: Vec<_> = s.fields.into_iter().collect();
assert!(BigArrayField::parse_field(&mut fields[0]).is_none());
assert!(BigArrayField::parse_field(&mut fields[1]).is_none());
assert!(BigArrayField::parse_field(&mut fields[2]).is_some());
}
#[test]
fn manual_bufsize() {
let s: ItemStruct = parse_quote! {
struct S {
a: String,
#[serbia(bufsize = 32)]
b: [u32; 32],
c: [u32; 33],
}
};
let mut fields: Vec<_> = s.fields.into_iter().collect();
assert!(BigArrayField::parse_field(&mut fields[0]).is_none());
assert!(BigArrayField::parse_field(&mut fields[1]).is_some());
assert!(BigArrayField::parse_field(&mut fields[2]).is_some());
}
#[test]
fn no_serde_derive() {
let attrs: Vec<Attribute> = vec![
parse_quote! {
#[derive(Serializer, Debug, Asd)]
},
parse_quote! {
#[asd]
},
parse_quote! {
#[serde(serialize_with = "asd")]
},
];
assert_eq!(
check_if_serializing_deserializing(attrs.iter()),
(false, false)
);
}
#[test]
fn detect_serialize() {
let attrs: Vec<Attribute> = vec![
parse_quote! {
#[asd]
},
parse_quote! {
#[derive(Deserializer, Debug, Asd, Serialize)]
},
parse_quote! {
#[asd]
},
parse_quote! {
#[serde(serialize_with = "asd")]
},
];
assert_eq!(
check_if_serializing_deserializing(attrs.iter()),
(true, false)
);
}
#[test]
fn detect_deserialize() {
let attrs: Vec<Attribute> = vec![
parse_quote! {
#[asd]
},
parse_quote! {
#[derive(Deserializer, Debug, Deserialize, Asd)]
},
parse_quote! {
#[asd]
},
parse_quote! {
#[serde(serialize_with = "asd")]
},
];
assert_eq!(
check_if_serializing_deserializing(attrs.iter()),
(false, true)
);
}
#[test]
fn detect_serialize_deserialize() {
let attrs: Vec<Attribute> = vec![
parse_quote! {
#[asd]
},
parse_quote! {
#[derive(Serialize, Deserializer, Debug, Deserialize, Asd)]
},
parse_quote! {
#[asd]
},
parse_quote! {
#[serde(serialize_with = "asd")]
},
];
assert_eq!(
check_if_serializing_deserializing(attrs.iter()),
(true, true)
);
}
#[test]
fn detect_serialize_deserialize_qualified() {
let attrs: Vec<Attribute> = vec![
parse_quote! {
#[asd]
},
parse_quote! {
#[derive(serde::Serialize, Deserializer, Debug, serde::Deserialize, Asd)]
},
parse_quote! {
#[asd]
},
parse_quote! {
#[serde(serialize_with = "asd")]
},
];
assert_eq!(
check_if_serializing_deserializing(attrs.iter()),
(true, true)
);
}
}