tier-derive 0.1.17

Derive macros for tier Rust configuration metadata
Documentation
use syn::{Attribute, spanned::Spanned};

use super::model::{TierAttrs, TierSourceKind};
use crate::syntax::{
    consume_unused_meta, parse_literal_expr_list, parse_numeric_literal,
    parse_rule_key_string_list, parse_rule_key_string_value, parse_string_list_call,
    parse_string_value, parse_usize_value,
};

pub(crate) fn parse_tier_attrs(attributes: &[Attribute]) -> syn::Result<TierAttrs> {
    let mut attrs = TierAttrs::default();
    for attribute in attributes {
        if !attribute.path().is_ident("tier") {
            continue;
        }
        attribute.parse_nested_meta(|meta| {
            if meta.path.is_ident("secret") {
                attrs.secret = true;
                return Ok(());
            }
            if meta.path.is_ident("leaf") {
                attrs.leaf = true;
                consume_unused_meta(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("sources") {
                attrs.sources = parse_source_kind_list(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("deny_sources") {
                attrs.deny_sources = parse_source_kind_list(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("env") {
                attrs.env = Some(parse_string_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("doc") {
                attrs.doc = Some(parse_string_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("example") {
                attrs.example = Some(parse_string_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("deprecated") {
                attrs.deprecated = Some(if meta.input.peek(syn::Token![=]) {
                    parse_string_value(meta)?
                } else {
                    "this field is deprecated".to_owned()
                });
                return Ok(());
            }
            if meta.path.is_ident("merge") {
                attrs.merge = Some(parse_string_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("non_empty") {
                attrs.non_empty = true;
                consume_unused_meta(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("min") {
                attrs.min = Some(parse_numeric_literal(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("max") {
                attrs.max = Some(parse_numeric_literal(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("min_length") {
                attrs.min_length = Some(parse_usize_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("max_length") {
                attrs.max_length = Some(parse_usize_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("min_items") {
                attrs.min_items = Some(parse_usize_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("max_items") {
                attrs.max_items = Some(parse_usize_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("min_properties") {
                attrs.min_properties = Some(parse_usize_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("max_properties") {
                attrs.max_properties = Some(parse_usize_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("multiple_of") {
                attrs.multiple_of = Some(parse_numeric_literal(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("pattern") {
                attrs.pattern = Some(parse_string_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("unique_items") {
                attrs.unique_items = true;
                consume_unused_meta(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("one_of") {
                attrs.one_of = parse_literal_expr_list(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("hostname") {
                attrs.hostname = true;
                consume_unused_meta(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("url") {
                attrs.url = true;
                consume_unused_meta(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("email") {
                attrs.email = true;
                consume_unused_meta(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("ip_addr") {
                attrs.ip_addr = true;
                consume_unused_meta(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("socket_addr") {
                attrs.socket_addr = true;
                consume_unused_meta(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("absolute_path") {
                attrs.absolute_path = true;
                consume_unused_meta(meta)?;
                return Ok(());
            }
            if meta.path.is_ident("env_decode") {
                attrs.env_decode = Some(parse_string_value(meta)?);
                return Ok(());
            }
            if meta.path.is_ident("validation_message") {
                let (rule, value) = parse_rule_key_string_value(meta)?;
                attrs.validation_messages.push((rule, value));
                return Ok(());
            }
            if meta.path.is_ident("validation_level") {
                let (rule, value) = parse_rule_key_string_value(meta)?;
                attrs.validation_levels.push((rule, value));
                return Ok(());
            }
            if meta.path.is_ident("validation_tags") {
                let (rule, values) = parse_rule_key_string_list(meta)?;
                attrs.validation_tags.push((rule, values));
                return Ok(());
            }
            Err(meta.error("unsupported tier attribute"))
        })?;
    }
    Ok(attrs)
}

fn parse_source_kind_list(
    meta: syn::meta::ParseNestedMeta<'_>,
) -> syn::Result<Vec<TierSourceKind>> {
    let span = meta.path.span();
    parse_string_list_call(meta)?
        .into_iter()
        .map(|value| TierSourceKind::parse(&value, span))
        .collect()
}