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 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::{Parse, ParseBuffer, ParseStream};
|
||||||
use syn::parse::discouraged::Speculative;
|
use syn::parse::discouraged::Speculative;
|
||||||
use syn::token::Token;
|
use syn::token::Token;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub(crate) enum Value {
|
pub(crate) enum Value {
|
||||||
None,
|
None,
|
||||||
Lit(Lit),
|
Lit(Lit),
|
||||||
Ident(Ident),
|
Ident(Ident),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub(crate) struct Dict {
|
pub(crate) struct Dict {
|
||||||
pub(crate) inner: HashMap<String, Value>
|
pub(crate) inner: HashMap<String, (Value, Span)>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dict {
|
impl Dict {
|
||||||
fn require_keys(&self, keys: &[&str]) -> Result<(), String> {
|
pub(crate) fn new() -> Self {
|
||||||
match keys.iter().find(|key| !self.inner.contains_key(**key)) {
|
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()),
|
Some(absent_key) => Err(absent_key.to_string()),
|
||||||
None => Ok(())
|
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())) {
|
match self.inner.keys().find(|key| !keys.contains(&key.as_str())) {
|
||||||
Some(disallowed_key) => Err(disallowed_key.clone()),
|
Some(disallowed_key) => Err(disallowed_key.clone()),
|
||||||
None => Ok(())
|
None => Ok(())
|
||||||
@@ -37,7 +58,7 @@ impl Parse for Dict {
|
|||||||
inner: input
|
inner: input
|
||||||
.parse_terminated::<KeyValPair, Token![,]>(KeyValPair::parse)?
|
.parse_terminated::<KeyValPair, Token![,]>(KeyValPair::parse)?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|pair| (pair.key, pair.val))
|
.map(|pair| (pair.key, (pair.val, pair.span)))
|
||||||
.collect::<HashMap<_, _>>()
|
.collect::<HashMap<_, _>>()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -46,13 +67,13 @@ impl Parse for Dict {
|
|||||||
struct KeyValPair {
|
struct KeyValPair {
|
||||||
key: String,
|
key: String,
|
||||||
val: Value,
|
val: Value,
|
||||||
|
span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for KeyValPair {
|
impl Parse for KeyValPair {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let key = input
|
let key = input
|
||||||
.parse::<Ident>()?
|
.parse::<Ident>()?;
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let val = if input.peek(Token![=]) {
|
let val = if input.peek(Token![=]) {
|
||||||
input.parse::<Token![=]>()?;
|
input.parse::<Token![=]>()?;
|
||||||
@@ -61,13 +82,20 @@ impl Parse for KeyValPair {
|
|||||||
} else if let Ok(ident) = speculative_parse::<Ident>(input) {
|
} else if let Ok(ident) = speculative_parse::<Ident>(input) {
|
||||||
Value::Ident(ident)
|
Value::Ident(ident)
|
||||||
} else {
|
} 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 {
|
} else {
|
||||||
Value::None
|
Value::None
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(KeyValPair { key, val })
|
Ok(KeyValPair {
|
||||||
|
key: key.to_string(),
|
||||||
|
val,
|
||||||
|
span: key.span(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user