|
|
|
|
@ -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<String, Value>
|
|
|
|
|
pub(crate) inner: HashMap<String, (Value, Span)>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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<Self> {
|
|
|
|
|
let mut dict = Dict::new();
|
|
|
|
|
|
|
|
|
|
let sub_dicts = attrs.iter()
|
|
|
|
|
.filter(|attr| attr.path.is_ident(name))
|
|
|
|
|
.map(|attr| attr.parse_args::<Dict>());
|
|
|
|
|
|
|
|
|
|
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, Token![,]>(KeyValPair::parse)?
|
|
|
|
|
.into_iter()
|
|
|
|
|
.map(|pair| (pair.key, pair.val))
|
|
|
|
|
.map(|pair| (pair.key, (pair.val, pair.span)))
|
|
|
|
|
.collect::<HashMap<_, _>>()
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
@ -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<Self> {
|
|
|
|
|
let key = input
|
|
|
|
|
.parse::<Ident>()?
|
|
|
|
|
.to_string();
|
|
|
|
|
.parse::<Ident>()?;
|
|
|
|
|
|
|
|
|
|
let val = if input.peek(Token![=]) {
|
|
|
|
|
input.parse::<Token![=]>()?;
|
|
|
|
|
@ -61,13 +82,20 @@ impl Parse for KeyValPair {
|
|
|
|
|
} else if let Ok(ident) = speculative_parse::<Ident>(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(),
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|