Attribute dictionary parsing
parent
7be516b237
commit
b1280ae1ca
@ -0,0 +1,87 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use syn::{Ident, Lit, Token};
|
||||
use syn::parse::{Parse, ParseBuffer, ParseStream};
|
||||
use syn::parse::discouraged::Speculative;
|
||||
use syn::token::Token;
|
||||
|
||||
pub(crate) enum Value {
|
||||
None,
|
||||
Lit(Lit),
|
||||
Ident(Ident),
|
||||
}
|
||||
|
||||
pub(crate) struct Dict {
|
||||
pub(crate) inner: HashMap<String, Value>
|
||||
}
|
||||
|
||||
impl Dict {
|
||||
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> {
|
||||
match self.inner.keys().find(|key| !keys.contains(&key.as_str())) {
|
||||
Some(disallowed_key) => Err(disallowed_key.clone()),
|
||||
None => Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Dict {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Ok(Dict {
|
||||
inner: input
|
||||
.parse_terminated::<KeyValPair, Token![,]>(KeyValPair::parse)?
|
||||
.into_iter()
|
||||
.map(|pair| (pair.key, pair.val))
|
||||
.collect::<HashMap<_, _>>()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
struct KeyValPair {
|
||||
key: String,
|
||||
val: Value,
|
||||
}
|
||||
|
||||
impl Parse for KeyValPair {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let key = input
|
||||
.parse::<Ident>()?
|
||||
.to_string();
|
||||
|
||||
let val = if input.peek(Token![=]) {
|
||||
input.parse::<Token![=]>()?;
|
||||
if let Ok(lit) = speculative_parse::<Lit>(input) {
|
||||
Value::Lit(lit)
|
||||
} 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)));
|
||||
}
|
||||
} else {
|
||||
Value::None
|
||||
};
|
||||
|
||||
Ok(KeyValPair { key, val })
|
||||
}
|
||||
}
|
||||
|
||||
fn speculative_parse<T>(input: ParseStream) -> syn::Result<T> where T: Parse {
|
||||
match fork_and_parse(input) {
|
||||
Ok((fork, parsed)) => {
|
||||
input.advance_to(&fork);
|
||||
Ok(parsed)
|
||||
}
|
||||
Err(err) => Err(err)
|
||||
}
|
||||
}
|
||||
|
||||
fn fork_and_parse<T>(input: ParseStream) -> syn::Result<(ParseBuffer, T)> where T: Parse {
|
||||
let fork = input.fork();
|
||||
T::parse(&fork).map(move |parsed| (fork, parsed))
|
||||
}
|
||||
Loading…
Reference in New Issue