Store span information to allow for better error messages

rename
Pantonshire 5 years ago
parent b1280ae1ca
commit b6ed3ec698

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

Loading…
Cancel
Save