predawn-macro-core 0.9.0

Core types and functions for macros in predawn
Documentation
use from_attr::FlagOrValue;
use proc_macro2::TokenStream;
use quote::quote;
use quote_use::quote_use;
use syn::{parse_quote, Attribute, Expr, ExprLit, Lit, Meta, MetaNameValue, Path, Type};

fn get_crate_name() -> TokenStream {
    #[cfg(not(feature = "schema"))]
    quote! { ::predawn }
    #[cfg(feature = "schema")]
    quote! { ::predawn_schema }
}

pub fn extract_description(attrs: &[Attribute]) -> String {
    let mut docs = String::new();

    attrs.iter().for_each(|attr| {
        let meta = if attr.path().is_ident("doc") {
            &attr.meta
        } else {
            return;
        };

        let doc = if let Meta::NameValue(MetaNameValue {
            value: Expr::Lit(ExprLit {
                lit: Lit::Str(doc), ..
            }),
            ..
        }) = meta
        {
            doc.value()
        } else {
            return;
        };

        if !docs.is_empty() {
            docs.push('\n');
        }

        docs.push_str(doc.trim());
    });

    docs
}

pub fn generate_string_expr(s: &str) -> Expr {
    parse_quote! {
        ::std::string::ToString::to_string(#s)
    }
}

pub fn generate_default_expr(
    ty: &Type,
    serde_default: FlagOrValue<String>,
    schema_default: FlagOrValue<Expr>,
) -> syn::Result<Option<Expr>> {
    let default_expr: Expr = match (serde_default, schema_default) {
        (FlagOrValue::None, FlagOrValue::None) => return Ok(None),

        (FlagOrValue::None, FlagOrValue::Flag { .. })
        | (FlagOrValue::Flag { .. }, FlagOrValue::Flag { .. })
        | (FlagOrValue::Flag { .. }, FlagOrValue::None) => {
            parse_quote! {
                <#ty as ::core::default::Default>::default()
            }
        }

        (FlagOrValue::Value { value, .. }, FlagOrValue::None)
        | (FlagOrValue::Value { value, .. }, FlagOrValue::Flag { .. }) => {
            let path = syn::parse_str::<Path>(&value)?;

            parse_quote! {
                #path()
            }
        }

        (FlagOrValue::None, FlagOrValue::Value { value: expr, .. })
        | (FlagOrValue::Flag { .. }, FlagOrValue::Value { value: expr, .. })
        | (FlagOrValue::Value { .. }, FlagOrValue::Value { value: expr, .. }) => expr,
    };

    Ok(Some(default_expr))
}

pub fn generate_json_value(ty: &Type, expr: &Expr) -> TokenStream {
    let crate_name = get_crate_name();

    quote_use! {
        # use std::{concat, stringify, file, line, column};
        # use #crate_name::__internal::serde_json;

        serde_json::to_value::<#ty>(#expr)
            .expect(concat!(
                "failed to serialize expression `", stringify!(#expr), "` of type `", stringify!(#ty),
                "`, at ", file!(), ":", line!(), ":", column!()
            ))
    }
}