diff --git a/src/attribute.rs b/src/attribute.rs index cea9060..71ae7df 100644 --- a/src/attribute.rs +++ b/src/attribute.rs @@ -1,29 +1,50 @@ use std::collections::HashMap; -use syn::{Ident, Lit, Token}; +use proc_macro2::Span; +use syn::{Attribute, Ident, Lit, Token}; use syn::parse::{Parse, ParseBuffer, ParseStream}; use syn::parse::discouraged::Speculative; use syn::token::Token; +#[derive(Clone)] pub(crate) enum Value { None, Lit(Lit), Ident(Ident), } +#[derive(Clone)] pub(crate) struct Dict { - pub(crate) inner: HashMap + pub(crate) inner: HashMap } impl Dict { - fn require_keys(&self, keys: &[&str]) -> Result<(), String> { - match keys.iter().find(|key| !self.inner.contains_key(**key)) { + pub(crate) fn new() -> Self { + Dict { inner: HashMap::new() } + } + + pub(crate) fn from_attrs(name: &str, attrs: &[Attribute]) -> syn::Result { + let mut dict = Dict::new(); + + let sub_dicts = attrs.iter() + .filter(|attr| attr.path.is_ident(name)) + .map(|attr| attr.parse_args::()); + + for sub_dict in sub_dicts { + dict.inner.extend(sub_dict?.inner.into_iter()); + } + + Ok(dict) + } + + pub(crate) fn require_keys(&self, keys: &[&str]) -> Result<(), String> { + match keys.iter().find(|&&key| !self.inner.contains_key(key)) { Some(absent_key) => Err(absent_key.to_string()), None => Ok(()) } } - fn allow_keys(&self, keys: &[&str]) -> Result<(), String> { + pub(crate) fn allow_keys(&self, keys: &[&str]) -> Result<(), String> { match self.inner.keys().find(|key| !keys.contains(&key.as_str())) { Some(disallowed_key) => Err(disallowed_key.clone()), None => Ok(()) @@ -37,7 +58,7 @@ impl Parse for Dict { inner: input .parse_terminated::(KeyValPair::parse)? .into_iter() - .map(|pair| (pair.key, pair.val)) + .map(|pair| (pair.key, (pair.val, pair.span))) .collect::>() }) } @@ -46,13 +67,13 @@ impl Parse for Dict { struct KeyValPair { key: String, val: Value, + span: Span, } impl Parse for KeyValPair { fn parse(input: ParseStream) -> syn::Result { let key = input - .parse::()? - .to_string(); + .parse::()?; let val = if input.peek(Token![=]) { input.parse::()?; @@ -61,13 +82,20 @@ impl Parse for KeyValPair { } else if let Ok(ident) = speculative_parse::(input) { Value::Ident(ident) } else { - return Err(input.error(format!("expected either a literal or identifier as the value corresponding to the key \"{}\", but found neither", key))); + let err_msg = format!( + "expected either a literal or identifier as the value corresponding to the \ + key \"{}\", but found neither", key); + return Err(input.error(err_msg)); } } else { Value::None }; - Ok(KeyValPair { key, val }) + Ok(KeyValPair { + key: key.to_string(), + val, + span: key.span(), + }) } }