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()
}