use crate::{
PipelineKind,
deserialization_impl::{
generate_custom_deserialize, generate_newtype_constructors, generate_newtype_deserialize,
},
enum_impl::{ElementTokens, element_validator_tokens},
surreal_value_impl::{Mode, struct_surreal_value},
validate_impl::{CheckedField, struct_validate},
};
use evenframe_core::{
derive::{
attributes::parse_rust_derives,
naming::{ItemShape, ItemWire, UnitValue},
schemasync_attributes::{
parse_container_validator_overrides, parse_validator_overrides,
refuse_container_validators,
},
typesync_attributes::{Position, TypesyncAttributes},
validator_parser::{FieldValidators, parse_field_validators},
},
types::{FieldType, PathNames},
validator::ValidatorOverrides,
};
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Attribute, DeriveInput};
pub fn generate_newtype_impl(
input: &DeriveInput,
pipeline: PipelineKind,
wire: &ItemWire,
) -> TokenStream {
newtype_impl(input, pipeline, wire).unwrap_or_else(syn::Error::into_compile_error)
}
fn newtype_impl(
input: &DeriveInput,
pipeline: PipelineKind,
wire: &ItemWire,
) -> syn::Result<TokenStream> {
let syn::Data::Struct(data) = &input.data else {
return Err(syn::Error::new_spanned(&input.ident, "expected a struct"));
};
let ident = &input.ident;
let name = ident.to_string();
let fields: Vec<&syn::Field> = data.fields.iter().collect();
let value = match &wire.shape {
ItemShape::Newtype { member } => Some(
fields
.iter()
.enumerate()
.find(|(position, field)| match (member, &field.ident) {
(syn::Member::Named(name), Some(ident)) => name == ident,
(syn::Member::Unnamed(index), None) => index.index as usize == *position,
_ => false,
})
.map(|(position, field)| (position, *field))
.ok_or_else(|| syn::Error::new_spanned(ident, "the newtype's field is missing"))?,
),
ItemShape::Tuple(_) | ItemShape::Unit => None,
ItemShape::Named => {
return Err(syn::Error::new_spanned(
ident,
"a struct of named fields is not a newtype",
));
}
};
let (inner, kind, validators, validator_overrides) = match value {
Some((_, field)) => {
let attributes: Vec<Attribute> =
input.attrs.iter().chain(&field.attrs).cloned().collect();
(
FieldType::parse(&field.ty, PathNames::Last),
quote! { ::evenframe::types::NewtypeKind::Branded },
parse_field_validators(&attributes)?,
parse_container_validator_overrides(&input.attrs)?
.followed_by(parse_validator_overrides(&field.attrs)?),
)
}
None => {
if let Some(attribute) = input.attrs.iter().find(|attribute| {
attribute.path().is_ident("validators") || attribute.path().is_ident("morphs")
}) {
return Err(syn::Error::new_spanned(
attribute,
"serde writes a tuple struct of several fields as an array and a unit struct \
as null, so there is no one value for `#[validators]` or `#[morphs]`: put \
each on the value it applies to",
));
}
let inner = match &wire.shape {
ItemShape::Unit => FieldType::Unit,
_ => FieldType::Tuple(
fields
.iter()
.map(|field| FieldType::parse(&field.ty, PathNames::Last))
.collect(),
),
};
refuse_container_validators(&input.attrs)?;
(
inner,
quote! { ::evenframe::types::NewtypeKind::Alias },
FieldValidators::default(),
ValidatorOverrides::default(),
)
}
};
let element_validators: Vec<FieldValidators> = match value {
Some(_) => Vec::new(),
None => fields
.iter()
.map(|field| parse_field_validators(&field.attrs))
.collect::<syn::Result<_>>()?,
};
let checked: Vec<CheckedField> = match value {
Some((position, field)) => {
vec![CheckedField::new(
field,
position,
String::new(),
&validators,
)]
}
None => fields
.iter()
.zip(&element_validators)
.enumerate()
.map(|(position, (field, validators))| {
CheckedField::new(field, position, position.to_string(), validators)
})
.collect(),
};
let validate_impl = struct_validate(input, &checked)?;
let others: Vec<syn::Member> = match value {
Some((position, _)) => fields
.iter()
.enumerate()
.filter(|(other, _)| *other != position)
.map(|(other, field)| member_at(field, other))
.collect(),
None => Vec::new(),
};
let others: Vec<&syn::Member> = others.iter().collect();
let deserialize_impl = match checked.first() {
Some(written) if value.is_some() && !validators.is_empty() => {
let deserialize = generate_newtype_deserialize(input, written, &others)?;
let constructors = generate_newtype_constructors(input, written, &others)?;
quote! { #deserialize #constructors }
}
_ if element_validators
.iter()
.any(|validators| !validators.is_empty()) =>
{
generate_custom_deserialize(input, &checked)?
}
_ => TokenStream::new(),
};
let newtype_impl = match (value, checked.first()) {
(Some((_, field)), Some(written)) => {
newtype_trait_impls(input, field, &written.member, &others)
}
_ => TokenStream::new(),
};
let surreal_value_impl = if pipeline.reaches_database() {
struct_surreal_value(input, wire, Mode::Stored, &[])?
} else {
TokenStream::new()
};
let morph_tokens = validators.morph_tokens();
let validator_tokens = validators.config_tokens();
let storage = evenframe_core::types::Storage {
tuple: wire.stored_tuple,
opaque_elements: wire.opaque_elements.clone(),
value: wire.unit_value.as_ref().map(UnitValue::surql),
..evenframe_core::types::Storage::default()
};
let ElementTokens {
morphs: element_morphs,
validators: element_validators_tokens,
overrides: element_validator_overrides,
} = match value {
Some(_) => ElementTokens::none(),
None => element_validator_tokens(&data.fields)?,
};
let TypesyncAttributes {
macroforge_derives,
annotations,
..
} = TypesyncAttributes::parse(&input.attrs, Position::Container)?;
let rust_derives = parse_rust_derives(&input.attrs);
let pipeline_tokens = pipeline.to_tokens();
let registry_entry = syn::Ident::new(
&format!("{}_NEWTYPE_REGISTRY_ENTRY", name.to_uppercase()),
ident.span(),
);
let metadata_gate = crate::metadata::gate(
input,
quote! {
const _: () = {
use ::evenframe::types::FieldType;
impl #ident {
pub fn static_newtype_config() -> ::evenframe::types::NewtypeConfig {
::evenframe::types::NewtypeConfig {
name: #name.to_owned(),
inner: #inner,
kind: #kind,
morphs: vec![#(#morph_tokens),*],
validators: vec![#(#validator_tokens),*],
validator_overrides: #validator_overrides,
element_morphs: #element_morphs,
element_validators: #element_validators_tokens,
element_validator_overrides: #element_validator_overrides,
storage: #storage,
doccom: None,
annotations: vec![#(#annotations.to_string()),*],
macroforge_derives: vec![#(#macroforge_derives.to_string()),*],
rust_derives: vec![#(#rust_derives.to_string()),*],
pipeline: #pipeline_tokens,
resolve_only: false,
raw_attributes: ::std::collections::BTreeMap::new(),
output_override: None,
}
}
}
#[::evenframe::linkme::distributed_slice(::evenframe::registry::NEWTYPE_REGISTRY_ENTRIES)]
#[linkme(crate = ::evenframe::linkme)]
static #registry_entry: ::evenframe::registry::NewtypeRegistryEntry =
::evenframe::registry::NewtypeRegistryEntry {
type_name: #name,
newtype_config_fn: || #ident::static_newtype_config(),
pipeline: #pipeline_tokens,
};
};
},
);
Ok(quote! {
#metadata_gate
#validate_impl
#newtype_impl
#deserialize_impl
#surreal_value_impl
})
}
pub(crate) fn written_as_metadata(
input: &DeriveInput,
written: &syn::Type,
pipeline: PipelineKind,
) -> TokenStream {
let ident = &input.ident;
let name = ident.to_string();
let inner = FieldType::parse(written, PathNames::Last);
let pipeline_tokens = pipeline.to_tokens();
let registry_entry = syn::Ident::new(
&format!("{}_WRITTEN_AS_REGISTRY_ENTRY", name.to_uppercase()),
ident.span(),
);
crate::metadata::gate(
input,
quote! {
const _: () = {
use ::evenframe::types::FieldType;
#[::evenframe::linkme::distributed_slice(::evenframe::registry::NEWTYPE_REGISTRY_ENTRIES)]
#[linkme(crate = ::evenframe::linkme)]
static #registry_entry: ::evenframe::registry::NewtypeRegistryEntry =
::evenframe::registry::NewtypeRegistryEntry {
type_name: #name,
newtype_config_fn: || ::evenframe::types::NewtypeConfig {
name: #name.to_owned(),
inner: #inner,
kind: ::evenframe::types::NewtypeKind::Alias,
pipeline: #pipeline_tokens,
..::std::default::Default::default()
},
pipeline: #pipeline_tokens,
};
};
},
)
}
fn newtype_trait_impls(
input: &DeriveInput,
field: &syn::Field,
member: &syn::Member,
others: &[&syn::Member],
) -> TokenStream {
let ident = &input.ident;
let inner = &field.ty;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
quote! {
impl #impl_generics ::evenframe::validator::runtime::Newtype for #ident #ty_generics #where_clause {
type Inner = #inner;
fn inner(&self) -> &#inner {
&self.#member
}
}
impl #impl_generics ::evenframe::validator::runtime::NewtypeParts for #ident #ty_generics #where_clause {
fn inner_mut(&mut self) -> &mut #inner {
&mut self.#member
}
fn from_inner(inner: #inner) -> Self {
Self {
#member: inner,
#(#others: ::std::default::Default::default(),)*
}
}
}
}
}
fn member_at(field: &syn::Field, position: usize) -> syn::Member {
match &field.ident {
Some(ident) => syn::Member::Named(ident.clone()),
None => syn::Member::Unnamed(syn::Index::from(position)),
}
}