Store span information to allow for better error messages
This commit is contained in:
1 parent
b1280ae1ca
commit
b6ed3ec698
1 file changed
+38
-10
+38
-10
@@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user