gluesql-macros 0.20.0

Derive macro for gluesql row conversion
Documentation
use {
    proc_macro::TokenStream,
    proc_macro_crate::{FoundCrate, crate_name},
    syn::{
        Attribute, DeriveInput, Expr, ExprLit, Lit, MetaNameValue, parse_macro_input,
        spanned::Spanned,
    },
};

mod from_glue_row;
mod to_glue_row;

fn resolve_gluesql_crate() -> Result<syn::Path, syn::Error> {
    if std::env::var("CARGO_PKG_NAME")
        .map(|name| name == "gluesql")
        .unwrap_or(false)
    {
        return Ok(syn::parse_quote!(::gluesql::core));
    }

    if let Ok(found) = crate_name("gluesql") {
        let path = match found {
            FoundCrate::Itself => syn::parse_quote!(crate::core),
            FoundCrate::Name(name) => {
                let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
                syn::parse_quote!(::#ident::core)
            }
        };

        return Ok(path);
    }

    let found = crate_name("gluesql_core")
        .or_else(|_| crate_name("gluesql-core"))
        .map_err(|_| {
            syn::Error::new(
                proc_macro2::Span::call_site(),
                "failed to locate `gluesql` crate; add a dependency on `gluesql` or `gluesql-core`",
            )
        })?;

    let path = match found {
        FoundCrate::Itself => syn::parse_quote!(crate),
        FoundCrate::Name(name) => {
            let ident = syn::Ident::new(&name, proc_macro2::Span::call_site());
            syn::parse_quote!(::#ident)
        }
    };

    Ok(path)
}

#[proc_macro_derive(FromGlueRow, attributes(glue))]
pub fn derive_from_glue_row(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    match from_glue_row::expand_from_glue_row(input) {
        Ok(ts) => TokenStream::from(ts),
        Err(e) => e.to_compile_error().into(),
    }
}

#[proc_macro_derive(ToGlueRow, attributes(glue))]
pub fn derive_to_glue_row(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    match to_glue_row::expand_to_glue_row(input) {
        Ok(ts) => TokenStream::from(ts),
        Err(e) => e.to_compile_error().into(),
    }
}

fn parse_glue_rename(attr: &Attribute) -> Option<Result<Option<String>, syn::Error>> {
    if !attr.path().is_ident("glue") {
        return None;
    }
    match attr.parse_args::<MetaNameValue>() {
        Ok(MetaNameValue { path, value, .. }) if path.is_ident("rename") => match value {
            Expr::Lit(ExprLit {
                lit: Lit::Str(s), ..
            }) => Some(Ok(Some(s.value()))),
            other => Some(Err(syn::Error::new(
                other.span(),
                "expected string literal for rename",
            ))),
        },
        Ok(_) => Some(Ok(None)),
        Err(e) => Some(Err(e)),
    }
}