rewrite parse_object

This should support arbitrary expresion, so gobble up
elements up to the next comma and then run it through
syn::parse2.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Wolfgang Bumiller 2019-06-10 14:10:01 +02:00
parent 1791c089b6
commit 10451f65eb
3 changed files with 117 additions and 92 deletions

View File

@ -1,22 +1,22 @@
use std::collections::HashMap;
use proc_macro2::{Ident, TokenStream};
use proc_macro2::TokenStream;
use derive_builder::Builder;
use failure::{bail, Error};
use quote::{quote, ToTokens};
use super::parsing::Value;
use super::parsing::Expression;
#[derive(Builder)]
pub struct ParameterDefinition {
pub description: syn::LitStr,
#[builder(default)]
pub validate: Option<Ident>,
pub validate: Option<syn::Expr>,
#[builder(default)]
pub minimum: Option<syn::Lit>,
pub minimum: Option<syn::Expr>,
#[builder(default)]
pub maximum: Option<syn::Lit>,
pub maximum: Option<syn::Expr>,
}
impl ParameterDefinition {
@ -24,7 +24,7 @@ impl ParameterDefinition {
ParameterDefinitionBuilder::default()
}
pub fn from_object(obj: HashMap<String, Value>) -> Result<Self, Error> {
pub fn from_object(obj: HashMap<String, Expression>) -> Result<Self, Error> {
let mut def = ParameterDefinition::builder();
for (key, value) in obj {
@ -33,13 +33,13 @@ impl ParameterDefinition {
def.description(value.expect_lit_str()?);
}
"validate" => {
def.validate(Some(value.expect_ident()?));
def.validate(Some(value.expect_expr()?));
}
"minimum" => {
def.minimum(Some(value.expect_lit()?));
def.minimum(Some(value.expect_expr()?));
}
"maximum" => {
def.maximum(Some(value.expect_lit()?));
def.maximum(Some(value.expect_expr()?));
}
other => bail!("invalid key in type definition: {}", other),
}

View File

@ -20,7 +20,7 @@ pub fn api_macro(attr: TokenStream, item: TokenStream) -> Result<TokenStream, Er
_ => bail!("expected api definition in braces"),
};
let definition = parse_object(definition)?;
let definition = parse_object2(definition)?;
// Now parse the item, based on which we decide whether this is an API method which needs a
// wrapper, or an API type which needs an ApiType implementation!
@ -39,7 +39,7 @@ pub fn api_macro(attr: TokenStream, item: TokenStream) -> Result<TokenStream, Er
}
fn handle_function(
mut definition: HashMap<String, Value>,
mut definition: HashMap<String, Expression>,
mut item: syn::ItemFn,
) -> Result<TokenStream, Error> {
if item.decl.generics.lt_token.is_some() {
@ -233,7 +233,7 @@ fn handle_function(
and specify its return type via the `returns` property",
)
})?
.expect_ident()?
.expect_type()?
.into_token_stream(),
});
@ -295,7 +295,7 @@ fn handle_function(
}
fn handle_struct(
definition: HashMap<String, Value>,
definition: HashMap<String, Expression>,
item: &syn::ItemStruct,
) -> Result<TokenStream, Error> {
if item.generics.lt_token.is_some() {
@ -312,7 +312,7 @@ fn handle_struct(
}
fn handle_struct_unnamed(
definition: HashMap<String, Value>,
definition: HashMap<String, Expression>,
name: &Ident,
item: &syn::FieldsUnnamed,
) -> Result<TokenStream, Error> {
@ -349,7 +349,7 @@ fn handle_struct_unnamed(
}
fn handle_struct_named(
definition: HashMap<String, Value>,
definition: HashMap<String, Expression>,
name: &Ident,
item: &syn::FieldsNamed,
) -> Result<TokenStream, Error> {
@ -396,7 +396,7 @@ fn handle_struct_named(
fn handle_named_struct_fields(
item: &syn::FieldsNamed,
mut field_def: HashMap<String, Value>,
mut field_def: HashMap<String, Expression>,
) -> Result<Vec<TokenStream>, Error> {
let mut verify_entries = Vec::new();

View File

@ -3,7 +3,7 @@ use std::collections::HashMap;
use proc_macro2::{Delimiter, Group, Ident, Span, TokenStream, TokenTree};
use failure::{bail, Error};
use syn::Lit;
use syn::{Expr, Lit};
pub type RawTokenIter = proc_macro2::token_stream::IntoIter;
pub type TokenIter = std::iter::Peekable<RawTokenIter>;
@ -136,108 +136,133 @@ pub fn finish_hyphenated_name(tokens: &mut TokenIter, name: Ident) -> Result<syn
Ok(syn::LitStr::new(&name, span))
}
// parse an object notation:
// object := '{' [ member * ] '}'
// member := <ident> ':' <member_value>
// member_value := [ "optional" ] ( <ident> | <literal> | <object> )
#[derive(Debug)]
pub enum Value {
//Ident(Ident), // eg. `string` or `integer`
//Description(syn::LitStr), // eg. `"some text"`
Ident(Ident), // eg. `foo`, for referencing stuff, may become `expression`?
Literal(syn::Lit), // eg. `123`
Negative(syn::Lit), // eg. `-123`
Object(HashMap<String, Value>), // eg. `{ key: value }`
pub enum Expression {
Expr(Expr),
Object(HashMap<String, Expression>),
}
impl Value {
pub fn expect_lit(self) -> Result<syn::Lit, Error> {
match self {
Value::Literal(lit) => Ok(lit),
other => bail!("expected string literal, got: {:?}", other),
}
}
impl Expression {
pub fn expect_lit_str(self) -> Result<syn::LitStr, Error> {
match self {
Value::Literal(syn::Lit::Str(lit)) => Ok(lit),
Value::Literal(other) => bail!("expected string literal, got: {:?}", other),
other => bail!("expected string literal, got: {:?}", other),
}
}
pub fn expect_ident(self) -> Result<Ident, Error> {
match self {
Value::Ident(ident) => Ok(ident),
other => bail!("expected ident, got: {:?}", other),
}
}
pub fn expect_object(self) -> Result<HashMap<String, Value>, Error> {
match self {
Value::Object(obj) => Ok(obj),
other => bail!("expected ident, got: {:?}", other),
Expression::Expr(expr) => match expr {
Expr::Lit(lit) => match lit.lit {
Lit::Str(lit) => Ok(lit),
other => bail!("expected string literal, got: {:?}", other),
}
other => bail!("expected string literal, got: {:?}", other),
}
_ => bail!("expected string literal"),
}
}
pub fn expect_lit_bool(self) -> Result<syn::LitBool, Error> {
match self {
Value::Literal(syn::Lit::Bool(lit)) => Ok(lit),
Value::Literal(other) => bail!("expected booleanliteral, got: {:?}", other),
other => bail!("expected boolean literal, got: {:?}", other),
Expression::Expr(expr) => match expr {
Expr::Lit(lit) => match lit.lit {
Lit::Bool(lit) => Ok(lit),
other => bail!("expected boolean literal, got: {:?}", other),
}
other => bail!("expected boolean literal, got: {:?}", other),
}
_ => bail!("expected boolean literal"),
}
}
pub fn expect_expr(self) -> Result<syn::Expr, Error> {
match self {
Expression::Expr(expr) => Ok(expr),
_ => bail!("expected expression, found {:?}", self),
}
}
pub fn expect_object(self) -> Result<HashMap<String, Expression>, Error> {
match self {
Expression::Object(obj) => Ok(obj),
_ => bail!("expected object, found an expression"),
}
}
pub fn expect_type(self) -> Result<syn::ExprPath, Error> {
match self {
Expression::Expr(expr) => match expr {
Expr::Path(path) => Ok(path),
other => bail!("expected a type name, got {:?}", other),
}
_ => bail!("expected a type name, got {:?}", self),
}
}
}
pub fn parse_object(tokens: TokenStream) -> Result<HashMap<String, Value>, Error> {
pub fn parse_object2(tokens: TokenStream) -> Result<HashMap<String, Expression>, Error> {
let mut tokens = tokens.into_iter().peekable();
let mut out = HashMap::new();
loop {
if tokens.peek().is_none() {
break;
}
let key = need_ident_or_string(&mut tokens)?;
match_colon(&mut tokens)?;
let key = match parse_object_key(&mut tokens)? {
Some(key) => key,
None => break,
};
let key_name = key.to_string();
let member = match tokens.next() {
Some(TokenTree::Group(group)) => {
if group.delimiter() == Delimiter::Brace {
Value::Object(parse_object(group.stream())?)
} else {
bail!("invalid group delimiter: {:?}", group.delimiter());
}
}
Some(TokenTree::Punct(ref punct)) if punct.as_char() == '-' => {
if let Some(TokenTree::Literal(literal)) = tokens.next() {
let lit = Lit::new(literal);
match lit {
Lit::Int(_) | Lit::Float(_) => Value::Negative(lit),
_ => bail!("expected literal after unary minus"),
}
} else {
bail!("expected literal value");
}
}
Some(TokenTree::Literal(literal)) => Value::Literal(Lit::new(literal)),
Some(TokenTree::Ident(ident)) => Value::Ident(ident),
Some(other) => bail!("expected member value at {}", other),
None => bail!("missing member value after {}", key_name),
};
let value = parse_object_value(&mut tokens, &key_name)?;
if out.insert(key_name.clone(), member).is_some() {
if out.insert(key_name.clone(), value).is_some() {
bail!("duplicate entry: {}", key_name);
}
comma_or_end(&mut tokens)?;
}
Ok(out)
}
fn parse_object_key(tokens: &mut TokenIter) -> Result<Option<Name>, Error> {
if tokens.peek().is_none() {
return Ok(None);
}
let key = need_ident_or_string(&mut *tokens)?;
match_colon(&mut *tokens)?;
Ok(Some(key))
}
fn parse_object_value(tokens: &mut TokenIter, key: &str) -> Result<Expression, Error> {
let mut value_tokens = TokenStream::new();
let mut first = true;
loop {
let token = match tokens.next() {
Some(token) => token,
None => {
if first {
bail!("missing value after key '{}'", key);
}
break;
}
};
if first {
first = false;
if let TokenTree::Group(group) = token {
let expr = parse_object2(group.stream())?;
comma_or_end(tokens)?;
return Ok(Expression::Object(expr));
}
}
match token {
TokenTree::Punct(ref punct) if punct.as_char() == ',' => {
// This is the end of the value!
break;
}
_ => value_tokens.extend(vec![token]),
}
}
let expr: Expr = syn::parse2(value_tokens)?;
Ok(Expression::Expr(expr))
}
fn need_ident_or_string(tokens: &mut TokenIter) -> Result<Name, Error> {
match tokens.next() {
Some(TokenTree::Ident(ident)) => Ok(ident.into()),
@ -249,7 +274,7 @@ fn need_ident_or_string(tokens: &mut TokenIter) -> Result<Name, Error> {
}
}
Some(other) => bail!(
"expected colon after key in api definition at {:?}",
"expected an identifier or a string: {:?}",
other.span()
),
None => bail!("ident expected"),