linked-data-core 0.1.1

Core library for Linked Data crates
Documentation
use std::marker::PhantomData;

use iref::IriBuf;
use proc_macro_error::abort;
use syn::DeriveInput;
use syn::spanned::Spanned;
use syn::visit::Visit;

use crate::attributes::field::RdfFieldAttributes;
use crate::attributes::r#type::{RdfEnumAttributes, RdfStructAttributes};
use crate::attributes::variant::{PredicatePath, RdfVariantAttributes};
use crate::prefix_mappings::PrefixMappings;
use crate::{Error, TokenGenerator};

pub enum RdfType<F> {
  Enum(RdfEnum<F>),
  Struct(RdfStruct<F>),
}

pub struct RdfEnum<G> {
  attributes: RdfEnumAttributes,
  pub ident: syn::Ident,
  pub variants: Vec<RdfVariant<G>>,
}

pub struct RdfVariant<G> {
  attributes: RdfVariantAttributes,
  pub ty: syn::Type,
  _generator: PhantomData<G>,
}

pub struct RdfStruct<G> {
  attributes: RdfStructAttributes,
  pub ident: syn::Ident,
  pub fields: Vec<RdfField<G>>,
}

#[derive(Debug)]
pub struct RdfField<G> {
  attributes: RdfFieldAttributes,
  pub ty: syn::Type,
  _generator: PhantomData<G>,
}

pub fn unwrap_or_abort<T>(result: Result<T, Error>) -> T {
  match result {
    Ok(value) => value,
    Err(error) => abort!(error.span(), error),
  }
}

impl<G: TokenGenerator> RdfType<G> {
  pub fn from_derive(derive_input: DeriveInput) -> Self {
    unwrap_or_abort(Self::try_from_derive(derive_input))
  }

  fn try_from_derive(derive_input: DeriveInput) -> Result<Self, Error> {
    match derive_input.data {
      syn::Data::Struct(data) => {
        let mut r#struct = RdfStruct {
          ident: derive_input.ident,
          attributes: derive_input.attrs.try_into()?,
          fields: vec![],
        };
        r#struct.visit_data_struct(&data);
        Ok(RdfType::Struct(r#struct))
      }
      syn::Data::Enum(data) => {
        let mut r#enum = RdfEnum {
          ident: derive_input.ident,
          attributes: derive_input.attrs.try_into()?,
          variants: vec![],
        };
        r#enum.visit_data_enum(&data);
        Ok(RdfType::Enum(r#enum))
      }
      syn::Data::Union(data_union) => Err(Error::UnionType {
        span: data_union.union_token.span(),
      }),
    }
  }
}

impl<'ast, F> Visit<'ast> for RdfEnum<F> {
  fn visit_variant(&mut self, variant: &'ast syn::Variant) {
    let variant = unwrap_or_abort(RdfVariant::from_variant(
      variant.clone(),
      &self.attributes.prefix_mappings,
    ));
    self.variants.push(variant);
  }
}

impl<F> RdfVariant<F> {
  fn from_variant(variant: syn::Variant, prefix_mappings: &PrefixMappings) -> Result<Self, Error> {
    let mut fields = variant.fields.iter();

    let Some(field) = fields.next() else {
      return Err(Error::UnitVariant {
        span: variant.span(),
      });
    };

    if let Some(field) = fields.next() {
      return Err(Error::StructVariant { span: field.span() });
    }

    Ok(RdfVariant {
      attributes: RdfVariantAttributes::try_from_attrs(
        &variant,
        field.attrs.clone(),
        variant.attrs.clone(),
        prefix_mappings,
      )?,
      ty: field.ty.clone(),
      _generator: PhantomData,
    })
  }

  pub fn predicate_path(&self) -> &PredicatePath {
    &self.attributes.predicate_path
  }
}

impl<G> RdfStruct<G> {
  pub fn type_iri(&self) -> Option<&IriBuf> {
    self.attributes.r#type.as_ref()
  }

  pub fn prefix_mappings(&self) -> &PrefixMappings {
    &self.attributes.prefix_mappings
  }
}

impl<'ast, F> Visit<'ast> for RdfStruct<F> {
  fn visit_field(&mut self, field: &'ast syn::Field) {
    let rdf_field = unwrap_or_abort(RdfField::try_from_field(
      field.clone(),
      &self.attributes.prefix_mappings,
    ));
    self.fields.push(rdf_field);
  }
}

impl<F> RdfField<F> {
  fn try_from_field(field: syn::Field, prefix_mappings: &PrefixMappings) -> Result<Self, Error> {
    let attributes = RdfFieldAttributes::try_from_attrs(field.attrs, prefix_mappings)?;

    Ok(RdfField {
      attributes,
      ty: field.ty,
      _generator: PhantomData,
    })
  }

  pub fn is_flattened(&self) -> bool {
    self.attributes.flatten
  }

  pub fn is_graph(&self) -> bool {
    self.attributes.is_graph
  }

  pub fn is_ignored(&self) -> bool {
    self.attributes.ignore
  }

  pub fn predicate(&self) -> Option<&IriBuf> {
    self.attributes.predicate.as_ref()
  }

  pub fn is_id(&self) -> bool {
    self.attributes.is_id
  }
}