Skip to content

Commit

Permalink
fixes #128
Browse files Browse the repository at this point in the history
  • Loading branch information
oscartbeaumont committed Sep 22, 2023
1 parent 165ec52 commit 44e4020
Show file tree
Hide file tree
Showing 14 changed files with 440 additions and 196 deletions.
12 changes: 5 additions & 7 deletions macros/src/data_type_from/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,23 +41,21 @@ pub fn derive(input: proc_macro::TokenStream) -> syn::Result<proc_macro::TokenSt
Ok((field, field_attrs))
})
.collect::<syn::Result<Vec<_>>>()?;
let fields = fields.iter().filter_map(|(field, attrs)| {
if attrs.skip {
return None;
}

let fields = fields.iter().map(|(field, attrs)| {
let ident = field
.ident
.as_ref()
// TODO: Proper syn error would be nice
.expect("'specta::DataTypeFrom' requires named fields.");
let ident_str = ident.to_string();
let skip = attrs.skip;

Some(quote!((#ident_str.into(), #crate_ref::internal::construct::field(
quote!((#ident_str.into(), #crate_ref::internal::construct::field(
#skip,
false,
false,
t.#ident.into(),
))))
)))
});

let struct_name = ident.to_string();
Expand Down
26 changes: 17 additions & 9 deletions macros/src/type/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ pub fn parse_enum(
})
.collect::<syn::Result<Vec<_>>>()?
.into_iter()
.filter(|(_, attrs)| !attrs.skip)
.map(|(variant, attrs)| {
let variant_ident_str = unraw_raw_ident(&variant.ident);

Expand All @@ -98,8 +97,11 @@ pub fn parse_enum(
.unnamed
.iter()
.map(|field| {
let (field, field_attrs) = decode_field_attrs(field)?;
let field_attrs = decode_field_attrs(field)?;
let field_ty = field_attrs.r#type.as_ref().unwrap_or(&field.ty);
let skip = field_attrs.skip;
let optional = field_attrs.optional;
let flatten = field_attrs.flatten;

let generic_vars = construct_datatype(
format_ident!("gen"),
Expand All @@ -110,9 +112,10 @@ pub fn parse_enum(
)?;

Ok(quote!(#crate_ref::internal::construct::field(
false,
false,
{
#skip,
#optional,
#flatten,
{
#generic_vars

gen
Expand All @@ -130,7 +133,7 @@ pub fn parse_enum(
.named
.iter()
.map(|field| {
let (field, field_attrs) = decode_field_attrs(field)?;
let field_attrs = decode_field_attrs(field)?;

let field_ty = field_attrs.r#type.as_ref().unwrap_or(&field.ty);

Expand All @@ -153,10 +156,14 @@ pub fn parse_enum(
}
(_, _) => quote::quote!(#field_ident_str),
};
let skip = field_attrs.skip;
let optional = field_attrs.optional;
let flatten = field_attrs.flatten;

Ok(quote!((#field_name.into(), #crate_ref::internal::construct::field(
false,
false,
#skip,
#optional,
#flatten,
{
#generic_vars

Expand All @@ -170,7 +177,8 @@ pub fn parse_enum(
}
};

Ok(quote!((#variant_name_str.into(), #inner)))
let skip = attrs.skip;
Ok(quote!((#variant_name_str.into(), #crate_ref::internal::construct::enum_variant(#skip, #inner))))
})
.collect::<syn::Result<Vec<_>>>()?;

Expand Down
87 changes: 33 additions & 54 deletions macros/src/type/struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use syn::{spanned::Spanned, DataStruct, Field, Fields, GenericParam, Generics};

use super::{attr::*, generics::construct_datatype};

pub fn decode_field_attrs(field: &Field) -> syn::Result<(&Field, FieldAttr)> {
pub fn decode_field_attrs(field: &Field) -> syn::Result<FieldAttr> {
// We pass all the attributes at the start and when decoding them pop them off the list.
// This means at the end we can check for any that weren't consumed and throw an error.
let mut attrs = parse_attrs(&field.attrs)?;
Expand All @@ -21,7 +21,7 @@ pub fn decode_field_attrs(field: &Field) -> syn::Result<(&Field, FieldAttr)> {
))
})?;

Ok((field, field_attrs))
Ok(field_attrs)
}

pub fn parse_struct(
Expand Down Expand Up @@ -79,12 +79,12 @@ pub fn parse_struct(
));
}

let (field, field_attrs) = decode_field_attrs(
data.fields
.iter()
.next()
.expect("unreachable: we just checked this!"),
)?;
let field = data
.fields
.iter()
.next()
.expect("unreachable: we just checked this!");
let field_attrs = decode_field_attrs(field)?;

let field_ty = field_attrs.r#type.as_ref().unwrap_or(&field.ty);

Expand All @@ -104,121 +104,106 @@ pub fn parse_struct(
} else {
let fields = match &data.fields {
Fields::Named(_) => {
let fields = data.fields.iter().map(decode_field_attrs)
.collect::<syn::Result<Vec<_>>>()?
let fields = data.fields
.iter()
.filter_map(|(field, field_attrs)| {

if field_attrs.skip {
return None;
}

Some((field, field_attrs))
}).map(|(field, field_attrs)| {
.map(|field| {
let field_attrs = decode_field_attrs(field)?;
let field_ty = field_attrs.r#type.as_ref().unwrap_or(&field.ty);

let ty = construct_datatype(
format_ident!("ty"),
field_ty,
&generic_idents,
crate_ref,
field_attrs.inline,
)?;

let field_ident_str = unraw_raw_ident(field.ident.as_ref().unwrap());

let field_name = match (field_attrs.rename.clone(), container_attrs.rename_all) {
(Some(name), _) => name,
(_, Some(inflection)) => inflection.apply(&field_ident_str).to_token_stream(),
(_, _) => field_ident_str.to_token_stream(),
};

let optional = field_attrs.optional;
let flatten = field_attrs.flatten;

let skip = field_attrs.skip;

let parent_inline = container_attrs
.inline
.then(|| quote!(true))
.unwrap_or(parent_inline.clone());

let ty = if field_attrs.flatten {
quote! {
#[allow(warnings)]
{
#ty
}

fn validate_flatten<T: #crate_ref::Flatten>() {}
validate_flatten::<#field_ty>();

let mut ty = <#field_ty as #crate_ref::Type>::inline(#crate_ref::DefOpts {
parent_inline: #parent_inline,
type_map: opts.type_map
}, &generics);

ty
}
} else {
quote! {
#ty

ty
}
};

Ok(quote!((#field_name.into(), #crate_ref::internal::construct::field(
#skip,
#optional,
#flatten,
{
#ty
}
))))
}).collect::<syn::Result<Vec<TokenStream>>>()?;

let tag = container_attrs
.tag
.as_ref()
.map(|t| quote!(Some(#t.into())))
.unwrap_or(quote!(None));


quote!(#crate_ref::internal::construct::struct_named(vec![#(#fields),*], #tag))
}
Fields::Unnamed(_) => {
let fields = data
.fields
.iter()
.map(decode_field_attrs)
.collect::<syn::Result<Vec<_>>>()?
.iter()
.filter_map(|(field, field_attrs)| {
if field_attrs.skip {
return None;
}

Some((field, field_attrs))
})
.map(|(field, field_attrs)| {
.map(|field| {
let field_attrs = decode_field_attrs(field)?;
let field_ty = field_attrs.r#type.as_ref().unwrap_or(&field.ty);

let generic_vars = construct_datatype(
format_ident!("gen"),
field_ty,
&generic_idents,
crate_ref,
field_attrs.inline,
)?;

let optional = field_attrs.optional;
let flatten = field_attrs.flatten;
let skip = field_attrs.skip;
Ok(quote!({
#generic_vars
#crate_ref::internal::construct::field(#optional, #flatten, gen)

#crate_ref::internal::construct::field(#skip, #optional, #flatten, gen)
}))
})
.collect::<syn::Result<Vec<TokenStream>>>()?;


quote!(#crate_ref::internal::construct::struct_unnamed(vec![#(#fields),*]))
}
Expand All @@ -228,8 +213,6 @@ pub fn parse_struct(
quote!(#crate_ref::DataType::Struct(#crate_ref::internal::construct::r#struct(#name.into(), vec![#(#definition_generics),*], #fields)))
};



let category = if container_attrs.inline {
quote!({
let generics = &[#(#reference_generics),*];
Expand All @@ -246,9 +229,5 @@ pub fn parse_struct(
})
};

Ok((
definition,
category,
true,
))
Ok((definition, category, true))
}
23 changes: 22 additions & 1 deletion src/datatype/enum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,30 @@ pub enum EnumRepr {
},
}

#[derive(Debug, Clone, PartialEq)]
pub struct EnumVariant {
/// Did the user apply a `#[serde(skip)]` or `#[specta(skip)]` attribute.
///
/// You might think, well why not apply this in the macro and just not emit the variant?
/// Well in Serde `A(String)` and `A(#[serde(skip)] (), String)` export as different Typescript types so the exporter needs runtime knowledge of this.
pub(crate) skip: bool,
/// The type of the variant.
pub(crate) inner: EnumVariants,
}

impl EnumVariant {
pub fn skip(&self) -> bool {
self.skip
}

pub fn inner(&self) -> &EnumVariants {
&self.inner
}
}

/// Type of an [`EnumType`] variant.
#[derive(Debug, Clone, PartialEq)]
pub enum EnumVariant {
pub enum EnumVariants {
/// A unit enum variant
/// Eg. `Variant`
Unit,
Expand Down
7 changes: 7 additions & 0 deletions src/datatype/fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ use crate::DataType;

#[derive(Debug, Clone, PartialEq)]
pub struct Field {
/// Did the user apply a `#[serde(skip)]` or `#[specta(skip)]` attribute.
///
/// You might think, well why not apply this in the macro and just not emit the variant?
/// Well in Serde `A(String)` and `A(#[serde(skip)] (), String)` export as different Typescript types so the exporter needs runtime knowledge of this.
pub(crate) skip: bool,
/// Did the user apply a `#[specta(optional)]` attribute.
pub(crate) optional: bool,
/// Did the user apply a `#[serde(flatten)]` or `#[specta(flatten)]` attribute.
pub(crate) flatten: bool,
pub(crate) ty: DataType,
}
Expand Down
18 changes: 11 additions & 7 deletions src/datatype/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,13 +161,17 @@ impl<T: Into<DataType> + 'static> From<Vec<T>> for DataType {
// TODO: This is probs gonna cause problems so we should try and remove the need for this entire impl block if we can.
_ => "".into(),
},
EnumVariant::Unnamed(UnnamedFields {
fields: vec![Field {
optional: false,
flatten: false,
ty,
}],
}),
EnumVariant {
skip: false,
inner: EnumVariants::Unnamed(UnnamedFields {
fields: vec![Field {
skip: false,
optional: false,
flatten: false,
ty,
}],
}),
},
)
})
.collect(),
Expand Down
Loading

0 comments on commit 44e4020

Please sign in to comment.