sea-query-derive 1.0.0

Derive macro for sea-query's Iden trait
Documentation
use std::convert::{TryFrom, TryInto};

use syn::spanned::Spanned;
use syn::{Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta};

use super::{error::ErrorMsg, path::IdenPath};

#[derive(PartialEq, Eq)]
pub(crate) enum IdenAttr {
    Rename(String),
    Method(Ident),
    Flatten,
}

impl IdenAttr {
    fn extract_method(meta: Meta) -> syn::Result<Self> {
        match meta {
            Meta::NameValue(nv) => match nv.value {
                Expr::Lit(ExprLit { lit, .. }) => match lit {
                    Lit::Str(name) => {
                        Ok(Self::Method(Ident::new(name.value().as_str(), name.span())))
                    }
                    _ => Err(Error::new_spanned(nv.eq_token, ErrorMsg::WrongLiteral)),
                },
                _ => Err(Error::new_spanned(nv, ErrorMsg::WrongLiteral)),
            },
            a => Err(Error::new_spanned(
                a,
                ErrorMsg::WrongNamedValueFormat(IdenPath::Method, IdenPath::Method),
            )),
        }
    }

    fn extract_iden(meta: Meta) -> syn::Result<Self> {
        match &meta {
            Meta::NameValue(nv) => match &nv.value {
                Expr::Lit(ExprLit { lit, .. }) => match lit {
                    Lit::Str(lit) => Ok(IdenAttr::Rename(lit.value())),
                    _ => Err(Error::new_spanned(&nv.value, ErrorMsg::WrongLiteral)),
                },
                _ => Err(Error::new_spanned(nv, ErrorMsg::WrongLiteral)),
            },
            Meta::List(list) if list.path.is_ident("iden") => {
                let mut iden_attr: Option<Self> = None;
                list.parse_nested_meta(|nested| {
                    if nested.path.is_ident(&IdenPath::Flatten) {
                        iden_attr = Some(IdenAttr::Flatten);
                        Ok(())
                    } else if nested.path.is_ident(&IdenPath::Rename) {
                        let value = nested.value()?;
                        let value: LitStr = value.parse()?;
                        iden_attr = Some(IdenAttr::Rename(value.value()));
                        Ok(())
                    } else if nested.path.is_ident(&IdenPath::Method) {
                        let value = nested.value()?;
                        let value: LitStr = value.parse()?;
                        iden_attr = Some(IdenAttr::Method(Ident::new(&value.value(), meta.span())));
                        Ok(())
                    } else {
                        Err(Error::new_spanned(
                            &meta,
                            ErrorMsg::UnsupportedKeyword(nested.path.get_ident().unwrap().clone()),
                        ))
                    }
                })?;
                iden_attr.ok_or(Error::new_spanned(meta, ErrorMsg::WrongListFormat))
            }
            a => Err(Error::new_spanned(a, ErrorMsg::WrongAttributeFormat)),
        }
    }
}

impl TryFrom<&Attribute> for IdenAttr {
    type Error = Error;

    fn try_from(value: &Attribute) -> Result<Self, Self::Error> {
        value.meta.clone().try_into()
    }
}

impl TryFrom<Meta> for IdenAttr {
    type Error = Error;

    fn try_from(value: Meta) -> Result<Self, Self::Error> {
        let path = value.path();
        if path.is_ident(&IdenPath::Method) {
            Self::extract_method(value)
        } else if path.is_ident(&IdenPath::Iden) {
            Self::extract_iden(value)
        } else {
            todo!()
        }
    }
}