use proc_macro2::{Ident, TokenStream};
use quote::quote;
use super::{Error, generate_fields};
use crate::generate::{
InterpretationBounds, TypeAttributes, extend_generics, read_variant_attributes,
};
pub fn generate(
attrs: &TypeAttributes,
ident: Ident,
generics: syn::Generics,
e: syn::DataEnum,
) -> Result<TokenStream, Error> {
let mut interpretation_bounds = InterpretationBounds {
reverse: true,
..Default::default()
};
let mut bounds = Vec::new();
let mut deserialize_variants = Vec::with_capacity(e.variants.len());
for v in e.variants {
let v_ident = &v.ident;
let v_attrs = read_variant_attributes(v.attrs)?;
let deserialize_variant = match v_attrs.iri {
Some(compact_iri) => {
let iri = compact_iri.expand(&attrs.prefixes)?.to_string();
match variant_shape(&v.fields) {
VariantShape::Simple(ty) => {
bounds.push(
syn::parse2(
quote!(#ty: ::ld_core::LinkedDataDeserializePredicateObjects<I_>),
)
.unwrap(),
);
quote! {
{
let _iri_buf = ::ld_core::iri_rs::IriBuf::new(#iri.to_owned()).unwrap();
match ::ld_core::rdfx::Interpretation::iri(interpretation_, _iri_buf.as_ref()) {
Some(predicate) => {
let context_ = context_.with_predicate(&predicate);
let result = ::ld_core::LinkedDataDeserializePredicateObjects::deserialize_objects_in(
interpretation_,
dataset_,
graph_,
::ld_core::rdfx::dataset::PatternMatchingDataset::quad_objects(dataset_, graph_, resource_, &predicate),
context_
);
match result {
Ok(value) => return Ok(Self::#v_ident(value)),
Err(e) => error = Some(e)
}
}
None => {
error = Some(::ld_core::FromLinkedDataError::MissingRequiredValue(
context_.into_iris(interpretation_)
))
}
}
}
}
}
VariantShape::Compound => {
let fields_de = generate_fields(attrs, v.fields)?;
interpretation_bounds.add(fields_de.interpretation_bounds);
bounds.extend(fields_de.bounds);
let deserialize_fields = fields_de.deserialize_fields;
let constructor = fields_de.constructor;
quote! {
{
let _iri_buf = ::ld_core::iri_rs::IriBuf::new(#iri.to_owned()).unwrap();
match ::ld_core::rdfx::Interpretation::iri(interpretation_, _iri_buf.as_ref()) {
Some(predicate) => {
let mut objects = ::ld_core::rdfx::dataset::PatternMatchingDataset::quad_objects(dataset_, graph_, resource_, &predicate);
let result = match objects.next() {
Some(resource_) => {
(|| {
#(#deserialize_fields)*
if objects.next().is_some() {
Err(::ld_core::FromLinkedDataError::TooManyValues(
context_.into_iris(interpretation_)
))
} else {
Ok(Self::#v_ident #constructor)
}
})()
}
None => Err(::ld_core::FromLinkedDataError::MissingRequiredValue(
context_.into_iris(interpretation_)
))
};
match result {
Ok(value) => return Ok(value),
Err(e) => error = Some(e)
}
}
None => {
error = Some(::ld_core::FromLinkedDataError::MissingRequiredValue(
context_.into_iris(interpretation_)
))
}
}
}
}
}
VariantShape::Unit => {
interpretation_bounds.reverse = true;
quote! {
{
for i in ::ld_core::rdfx::interpretation::ReverseInterpretation::iris_of(interpretation_, resource_) {
if i.as_str() == #iri {
return Ok(Self::#v_ident)
}
}
}
}
}
}
}
None => match variant_shape(&v.fields) {
VariantShape::Simple(ty) => {
bounds.push(
syn::parse2(quote!(#ty: ::ld_core::LinkedDataDeserializeSubject<I_>))
.unwrap(),
);
quote! {
let result = ::ld_core::LinkedDataDeserializeSubject::deserialize_subject_in(
interpretation_,
dataset_,
graph_,
resource_,
context_
);
match result {
Ok(value) => return Ok(Self::#v_ident(value)),
Err(e) => error = Some(e)
}
}
}
VariantShape::Compound => {
let variant_de = generate_fields(attrs, v.fields)?;
interpretation_bounds.add(variant_de.interpretation_bounds);
bounds.extend(variant_de.bounds);
let deserialize_fields = variant_de.deserialize_fields;
let constructor = variant_de.constructor;
quote! {
let result = (|| {
#(#deserialize_fields)*
Ok(Self::#v_ident #constructor)
})();
match result {
Ok(value) => return Ok(value),
Err(e) => error = Some(e)
}
}
}
VariantShape::Unit => {
panic!()
}
},
};
deserialize_variants.push(deserialize_variant)
}
let ld_generics = extend_generics(&generics, interpretation_bounds, bounds);
let (_, ty_generics, _) = generics.split_for_impl();
let (impl_generics, _, where_clause) = ld_generics.split_for_impl();
Ok(quote! {
impl #impl_generics ::ld_core::LinkedDataDeserializeSubject<I_> for #ident #ty_generics #where_clause {
fn deserialize_subject_in<D_>(
interpretation_: &I_,
dataset_: &D_,
graph_: Option<&I_::Resource>,
resource_: &I_::Resource,
outer_context_: ::ld_core::Context<I_>
) -> Result<Self, ::ld_core::FromLinkedDataError>
where
D_: ::ld_core::rdfx::dataset::PatternMatchingDataset<Subject = I_::Resource>
{
let context_ = outer_context_.with_subject(resource_);
let mut error = None;
#(#deserialize_variants)*
Err(error.unwrap_or_else(|| {
::ld_core::FromLinkedDataError::InvalidSubject {
context: outer_context_.into_iris(interpretation_),
subject: ::ld_core::rdfx::interpretation::ReverseInterpretation::iris_of(interpretation_, resource_).next().map(|i| i.into_owned())
}
}))
}
}
impl #impl_generics ::ld_core::LinkedDataDeserializePredicateObjects<I_> for #ident #ty_generics #where_clause {
fn deserialize_objects_in<'de_, D_>(
interpretation: &I_,
dataset: &D_,
graph: Option<&I_::Resource>,
objects: impl IntoIterator<Item = &'de_ I_::Resource>,
context: ::ld_core::Context<I_>
) -> Result<Self, ::ld_core::FromLinkedDataError>
where
I_::Resource: 'de_,
D_: ::ld_core::rdfx::dataset::PatternMatchingDataset<Subject = I_::Resource>
{
let mut objects = objects.into_iter();
match objects.next() {
Some(object) => {
let value = <Self as ::ld_core::LinkedDataDeserializeSubject<I_>>::deserialize_subject_in(
interpretation,
dataset,
graph,
object,
context
)?;
if objects.next().is_some() {
Err(::ld_core::FromLinkedDataError::TooManyValues(
context.into_iris(interpretation)
))
} else {
Ok(value)
}
}
None => {
Err(::ld_core::FromLinkedDataError::MissingRequiredValue(
context.into_iris(interpretation)
))
}
}
}
}
})
}
enum VariantShape<'a> {
Simple(&'a syn::Type),
Compound,
Unit,
}
fn variant_shape(fields: &syn::Fields) -> VariantShape<'_> {
match fields {
syn::Fields::Named(_) => VariantShape::Compound,
syn::Fields::Unnamed(unnamed_fields) => {
let mut fields_iter = unnamed_fields.unnamed.iter();
if let Some(field) = fields_iter.next() {
if fields_iter.next().is_none()
&& !field.attrs.iter().any(|attr| attr.path().is_ident("ld"))
{
return VariantShape::Simple(&field.ty);
}
}
VariantShape::Compound
}
syn::Fields::Unit => VariantShape::Unit,
}
}