cido-macros 0.2.0

Macros for generating code that enables easier interfacing with cido
Documentation
use super::Conversion;
use crate::parse::util::{Attributes, SqlSafeName};
use deluxe::{ExtractAttributes, HasAttributes, ParseAttributes, ParseMetaItem, Result};
use quote::ToTokens;
use syn::{Expr, Ident, Type};

#[derive(ExtractAttributes)]
#[deluxe(attributes(alias))]
pub struct Alias(#[deluxe(flatten)] pub Vec<Ident>);

pub struct ActuallyExists<'t, T: HasAttributes, A: ParseAttributes<'t, T>> {
  pub exists: bool,
  pub span: proc_macro2::Span,
  _marker: core::marker::PhantomData<fn() -> &'t (A, T)>,
}

impl<'t, T: HasAttributes, A: ParseAttributes<'t, T>> ParseAttributes<'t, T>
  for ActuallyExists<'t, T, A>
{
  fn path_matches(path: &syn::Path) -> bool {
    A::path_matches(path)
  }

  fn parse_attributes(obj: &'t T) -> Result<Self> {
    if let Some(next) = ::deluxe::____private::parse_helpers::ref_tokens::<A, _>(obj).next() {
      Ok(Self {
        exists: true,
        span: next.2,
        _marker: core::marker::PhantomData,
      })
    } else {
      Ok(Self {
        exists: false,
        span: proc_macro2::Span::call_site(),
        _marker: core::marker::PhantomData,
      })
    }
  }
}

pub trait AttributeFlag: Sized {
  fn path() -> &'static str;
}

pub struct Flag<A: AttributeFlag> {
  pub set: bool,
  // used when doing custom checks to point at the offending attribute
  pub span: proc_macro2::Span,
  _marker: core::marker::PhantomData<fn() -> A>,
}

impl<A: AttributeFlag> Flag<A> {
  fn new(set: bool, span: proc_macro2::Span) -> Self {
    Self {
      set,
      span,
      _marker: core::marker::PhantomData,
    }
  }
}

impl<T: HasAttributes, A: AttributeFlag> ExtractAttributes<T> for Flag<A> {
  fn path_matches(path: &syn::Path) -> bool {
    deluxe::____private::parse_helpers::path_matches(path, &[A::path()])
  }

  // only returns an Error if there are broken invariants
  fn extract_attributes(obj: &mut T) -> Result<Self> {
    let tokens = ::deluxe::____private::parse_helpers::take_tokens::<Self, _>(obj)?;
    let mut count = 0;
    let mut values = quote::quote! {};
    let mut span = None;
    for (value, _key, s) in tokens {
      count += 1;
      span = Some(s);
      value.to_tokens(&mut values);
    }
    if count == 0 {
      Ok(Self::new(false, proc_macro2::Span::call_site()))
    } else if count == 1 && values.is_empty() {
      Ok(Self::new(true, span.unwrap()))
    } else if !values.is_empty() {
      Err(syn::Error::new(
        span.unwrap(),
        format!("#[{}] doesn't allow any extra fields", A::path()),
      ))
    } else {
      Err(syn::Error::new(
        span.unwrap(),
        format!("#[{}] should only be specified once", A::path()),
      ))
    }
  }
}

pub struct IdField;

impl AttributeFlag for IdField {
  fn path() -> &'static str {
    "id"
  }
}

pub struct StaticField;

impl AttributeFlag for StaticField {
  fn path() -> &'static str {
    "static_field"
  }
}

pub struct Indexed;

impl AttributeFlag for Indexed {
  fn path() -> &'static str {
    "indexed"
  }
}

pub struct Entity;

impl AttributeFlag for Entity {
  fn path() -> &'static str {
    "entity"
  }
}

pub struct Event;

impl AttributeFlag for Event {
  fn path() -> &'static str {
    "event"
  }
}

#[derive(Clone, Debug, ExtractAttributes, ParseAttributes)]
#[deluxe(attributes(sql))]
pub struct TransformerFieldSqlParseConfig {
  #[deluxe(alias = rename)]
  pub name: Option<SqlSafeName>,
  #[deluxe(with = deluxe::with::maybe_quoted, alias = type)]
  pub ty: Option<Expr>,
  pub nullable: deluxe::Flag,
}

#[derive(Debug, Clone, ExtractAttributes, ParseAttributes)]
#[deluxe(attributes(db))]
pub struct TransformerFieldDbParseConfig {
  #[deluxe(with = deluxe::with::maybe_quoted, alias = rename)]
  pub name: Option<Ident>,
  #[deluxe(alias = type)]
  pub ty: Option<Type>,
  #[deluxe(default)]
  pub db_to_field: Conversion,
  #[deluxe(default)]
  pub field_to_db: Conversion,
}

#[derive(Debug, Default, Clone, ExtractAttributes, ParseAttributes)]
#[deluxe(attributes(gql))]
pub struct TransformerFieldGraphqlParseConfig {
  #[deluxe(with = deluxe::with::maybe_quoted, alias = rename)]
  pub name: Option<Ident>,
  #[deluxe(default)]
  pub attrs: Attributes,
  #[deluxe(with = deluxe::with::maybe_quoted)]
  pub complexity: Option<Expr>,
  pub hidden: deluxe::Flag,
  #[deluxe(default)]
  pub input: TransformerFieldGraphqlParseInputConfig,
  #[deluxe(default)]
  pub output: TransformerFieldGraphqlParseOutputConfig,
}

#[derive(Debug, Default, Clone, ParseMetaItem)]
pub(super) struct TransformerFieldGraphqlParseInputConfig {
  #[deluxe(alias = type)]
  pub ty: Option<Type>,
  #[deluxe(default)]
  pub gql_to_field: Conversion,
}

#[derive(Debug, Default, Clone, ParseMetaItem)]
pub(super) struct TransformerFieldGraphqlParseOutputConfig {
  #[deluxe(alias = type)]
  pub ty: Option<Type>,
  #[deluxe(default)]
  pub field_to_gql: Conversion,
}

#[derive(Debug, Clone, ExtractAttributes, ParseAttributes)]
#[deluxe(attributes(derived_from))]
pub struct DerivedField {
  #[deluxe(with = deluxe::with::maybe_quoted)]
  pub field: Ident,
  #[deluxe(with = deluxe::with::maybe_quoted)]
  pub complexity: Option<Expr>,
  pub multiple: deluxe::Flag,
}