tier-derive 0.1.17

Derive macros for tier Rust configuration metadata
Documentation
use quote::quote;
use syn::LitStr;

use super::{attr::TierAttrs, serde_attrs::SerdeFieldAttrs};

pub(crate) fn direct_field_metadata_tokens(
    accumulator: &proc_macro2::Ident,
    field_name: &LitStr,
    aliases: &[LitStr],
    serde_attrs: &SerdeFieldAttrs,
    attrs: &TierAttrs,
    secret_type: bool,
) -> syn::Result<proc_macro2::TokenStream> {
    let mut builder = quote! {
        ::tier::FieldMetadata::new(#field_name)
    };

    for alias in aliases {
        builder = quote! { #builder.alias(#alias) };
    }
    if attrs.secret || secret_type {
        builder = quote! { #builder.secret() };
    }
    if let Some(env) = &attrs.env {
        let env = LitStr::new(env, field_name.span());
        builder = quote! { #builder.env(#env) };
    }
    if let Some(doc) = &attrs.doc {
        let doc = LitStr::new(doc, field_name.span());
        builder = quote! { #builder.doc(#doc) };
    }
    if let Some(example) = &attrs.example {
        let example = LitStr::new(example, field_name.span());
        builder = quote! { #builder.example(#example) };
    }
    if let Some(deprecated) = &attrs.deprecated {
        let deprecated = LitStr::new(deprecated, field_name.span());
        builder = quote! { #builder.deprecated(#deprecated) };
    }
    if serde_attrs.has_default {
        builder = quote! { #builder.defaulted() };
    }
    if let Some(merge) = &attrs.merge {
        let merge_strategy = match merge.as_str() {
            "merge" => quote! { ::tier::MergeStrategy::Merge },
            "replace" => quote! { ::tier::MergeStrategy::Replace },
            "append" => quote! { ::tier::MergeStrategy::Append },
            _ => {
                return Err(syn::Error::new(
                    field_name.span(),
                    "unsupported tier merge strategy, expected merge|replace|append",
                ));
            }
        };
        builder = quote! { #builder.merge_strategy(#merge_strategy) };
    }
    if !attrs.sources.is_empty() {
        let sources = attrs
            .sources
            .iter()
            .map(|source| source.tokens())
            .collect::<Vec<_>>();
        builder = quote! { #builder.allow_sources([#(#sources),*]) };
    }
    if !attrs.deny_sources.is_empty() {
        let sources = attrs
            .deny_sources
            .iter()
            .map(|source| source.tokens())
            .collect::<Vec<_>>();
        builder = quote! { #builder.deny_sources([#(#sources),*]) };
    }
    if attrs.non_empty {
        builder = quote! { #builder.non_empty() };
    }
    if let Some(min) = &attrs.min {
        let min = &min.tokens;
        builder = quote! { #builder.min(#min) };
    }
    if let Some(max) = &attrs.max {
        let max = &max.tokens;
        builder = quote! { #builder.max(#max) };
    }
    if let Some(min_length) = attrs.min_length {
        builder = quote! { #builder.min_length(#min_length) };
    }
    if let Some(max_length) = attrs.max_length {
        builder = quote! { #builder.max_length(#max_length) };
    }
    if let Some(min_items) = attrs.min_items {
        builder = quote! { #builder.min_items(#min_items) };
    }
    if let Some(max_items) = attrs.max_items {
        builder = quote! { #builder.max_items(#max_items) };
    }
    if let Some(min_properties) = attrs.min_properties {
        builder = quote! { #builder.min_properties(#min_properties) };
    }
    if let Some(max_properties) = attrs.max_properties {
        builder = quote! { #builder.max_properties(#max_properties) };
    }
    if let Some(multiple_of) = &attrs.multiple_of {
        let multiple_of = &multiple_of.tokens;
        builder = quote! { #builder.multiple_of(#multiple_of) };
    }
    if let Some(pattern) = &attrs.pattern {
        let pattern = LitStr::new(pattern, field_name.span());
        builder = quote! { #builder.pattern(#pattern) };
    }
    if attrs.unique_items {
        builder = quote! { #builder.unique_items() };
    }
    if !attrs.one_of.is_empty() {
        let one_of = &attrs.one_of;
        builder = quote! { #builder.one_of([#(#one_of),*]) };
    }
    if attrs.hostname {
        builder = quote! { #builder.hostname() };
    }
    if attrs.url {
        builder = quote! { #builder.url() };
    }
    if attrs.email {
        builder = quote! { #builder.email() };
    }
    if attrs.ip_addr {
        builder = quote! { #builder.ip_addr() };
    }
    if attrs.socket_addr {
        builder = quote! { #builder.socket_addr() };
    }
    if attrs.absolute_path {
        builder = quote! { #builder.absolute_path() };
    }
    if let Some(env_decode) = &attrs.env_decode {
        let env_decode = match env_decode.as_str() {
            "csv" => quote! { ::tier::EnvDecoder::Csv },
            "path_list" => quote! { ::tier::EnvDecoder::PathList },
            "key_value_map" => quote! { ::tier::EnvDecoder::KeyValueMap },
            "whitespace" => quote! { ::tier::EnvDecoder::Whitespace },
            _ => {
                return Err(syn::Error::new(
                    field_name.span(),
                    "unsupported tier env decoder, expected csv|path_list|key_value_map|whitespace",
                ));
            }
        };
        builder = quote! { #builder.env_decoder(#env_decode) };
    }
    for (rule, message) in &attrs.validation_messages {
        let rule = LitStr::new(rule, field_name.span());
        let message = LitStr::new(message, field_name.span());
        builder = quote! { #builder.validation_message(#rule, #message) };
    }
    for (rule, level) in &attrs.validation_levels {
        let rule = LitStr::new(rule, field_name.span());
        let level = match level.as_str() {
            "error" => quote! { ::tier::ValidationLevel::Error },
            "warn" | "warning" => quote! { ::tier::ValidationLevel::Warning },
            _ => {
                return Err(syn::Error::new(
                    field_name.span(),
                    "unsupported validation level, expected error|warning",
                ));
            }
        };
        builder = quote! { #builder.validation_level(#rule, #level) };
    }
    for (rule, tags) in &attrs.validation_tags {
        let rule = LitStr::new(rule, field_name.span());
        let tags = tags
            .iter()
            .map(|tag| LitStr::new(tag, field_name.span()))
            .collect::<Vec<_>>();
        builder = quote! { #builder.validation_tags(#rule, [#(#tags),*]) };
    }

    Ok(quote! {
        #accumulator.push(#builder);
    })
}