Stop throwing away span information when parsing metadata

This commit is contained in:
Jonas Platte 2019-07-20 12:41:54 +02:00
parent fd8367be4c
commit 777e9c4c70
2 changed files with 50 additions and 59 deletions

View File

@ -1,21 +1,22 @@
//! Details of the `metadata` section of the procedural macro. //! Details of the `metadata` section of the procedural macro.
use syn::{punctuated::Pair, Expr, FieldValue, Lit, Member}; use proc_macro2::Ident;
use syn::{Expr, ExprLit, FieldValue, Lit, LitBool, LitStr, Member};
/// The result of processing the `metadata` section of the macro. /// The result of processing the `metadata` section of the macro.
pub struct Metadata { pub struct Metadata {
/// The description field. /// The description field.
pub description: String, pub description: LitStr,
/// The method field. /// The method field.
pub method: String, pub method: Ident,
/// The name field. /// The name field.
pub name: String, pub name: LitStr,
/// The path field. /// The path field.
pub path: String, pub path: LitStr,
/// The rate_limited field. /// The rate_limited field.
pub rate_limited: bool, pub rate_limited: LitBool,
/// The description field. /// The description field.
pub requires_authentication: bool, pub requires_authentication: LitBool,
} }
impl From<Vec<FieldValue>> for Metadata { impl From<Vec<FieldValue>> for Metadata {
@ -35,15 +36,13 @@ impl From<Vec<FieldValue>> for Metadata {
match &identifier.to_string()[..] { match &identifier.to_string()[..] {
"description" => { "description" => {
let expr_lit = match field_value.expr { let literal = match field_value.expr {
Expr::Lit(expr_lit) => expr_lit, Expr::Lit(ExprLit {
_ => panic!("expected Expr::Lit"), lit: Lit::Str(s), ..
}) => s,
_ => panic!("expected string literal"),
}; };
let lit_str = match expr_lit.lit { description = Some(literal);
Lit::Str(lit_str) => lit_str,
_ => panic!("expected Lit::Str"),
};
description = Some(lit_str.value());
} }
"method" => { "method" => {
let expr_path = match field_value.expr { let expr_path = match field_value.expr {
@ -51,60 +50,49 @@ impl From<Vec<FieldValue>> for Metadata {
_ => panic!("expected Expr::Path"), _ => panic!("expected Expr::Path"),
}; };
let path = expr_path.path; let path = expr_path.path;
let segments = path.segments; let mut segments = path.segments.iter();
if segments.len() != 1 { let method_name = segments.next().expect("expected non-empty path");
panic!("ruma_api! expects a one component path for `metadata` `method`"); assert!(
} segments.next().is_none(),
let pair = segments.first().unwrap(); // safe because we just checked "ruma_api! expects a one-component path for `metadata` `method`"
let method_name = match pair { );
Pair::End(method_name) => method_name, method = Some(method_name.ident.clone());
_ => panic!("expected Pair::End"),
};
method = Some(method_name.ident.to_string());
} }
"name" => { "name" => {
let expr_lit = match field_value.expr { let literal = match field_value.expr {
Expr::Lit(expr_lit) => expr_lit, Expr::Lit(ExprLit {
_ => panic!("expected Expr::Lit"), lit: Lit::Str(s), ..
}) => s,
_ => panic!("expected string literal"),
}; };
let lit_str = match expr_lit.lit { name = Some(literal);
Lit::Str(lit_str) => lit_str,
_ => panic!("expected Lit::Str"),
};
name = Some(lit_str.value());
} }
"path" => { "path" => {
let expr_lit = match field_value.expr { let literal = match field_value.expr {
Expr::Lit(expr_lit) => expr_lit, Expr::Lit(ExprLit {
_ => panic!("expected Expr::Lit"), lit: Lit::Str(s), ..
}) => s,
_ => panic!("expected string literal"),
}; };
let lit_str = match expr_lit.lit { path = Some(literal);
Lit::Str(lit_str) => lit_str,
_ => panic!("expected Lit::Str"),
};
path = Some(lit_str.value());
} }
"rate_limited" => { "rate_limited" => {
let expr_lit = match field_value.expr { let literal = match field_value.expr {
Expr::Lit(expr_lit) => expr_lit, Expr::Lit(ExprLit {
lit: Lit::Bool(b), ..
}) => b,
_ => panic!("expected Expr::Lit"), _ => panic!("expected Expr::Lit"),
}; };
let lit_bool = match expr_lit.lit { rate_limited = Some(literal)
Lit::Bool(lit_bool) => lit_bool,
_ => panic!("expected Lit::Bool"),
};
rate_limited = Some(lit_bool.value)
} }
"requires_authentication" => { "requires_authentication" => {
let expr_lit = match field_value.expr { let literal = match field_value.expr {
Expr::Lit(expr_lit) => expr_lit, Expr::Lit(ExprLit {
lit: Lit::Bool(b), ..
}) => b,
_ => panic!("expected Expr::Lit"), _ => panic!("expected Expr::Lit"),
}; };
let lit_bool = match expr_lit.lit { requires_authentication = Some(literal)
Lit::Bool(lit_bool) => lit_bool,
_ => panic!("expected Lit::Bool"),
};
requires_authentication = Some(lit_bool.value)
} }
_ => panic!("ruma_api! metadata included unexpected field"), _ => panic!("ruma_api! metadata included unexpected field"),
} }

View File

@ -65,8 +65,11 @@ impl From<RawApi> for Api {
impl ToTokens for Api { impl ToTokens for Api {
fn to_tokens(&self, tokens: &mut TokenStream) { fn to_tokens(&self, tokens: &mut TokenStream) {
let description = &self.metadata.description; let description = &self.metadata.description;
let method = Ident::new(self.metadata.method.as_ref(), Span::call_site()); let method = &self.metadata.method;
let name = &self.metadata.name; // We don't (currently) use this literal as a literal in the generated code. Instead we just
// put it into doc comments, for which the span information is irrelevant. So we can work
// with only the literal's value from here on.
let name = &self.metadata.name.value();
let path = &self.metadata.path; let path = &self.metadata.path;
let rate_limited = &self.metadata.rate_limited; let rate_limited = &self.metadata.rate_limited;
let requires_authentication = &self.metadata.requires_authentication; let requires_authentication = &self.metadata.requires_authentication;
@ -85,7 +88,7 @@ impl ToTokens for Api {
}; };
let (set_request_path, parse_request_path) = if self.request.has_path_fields() { let (set_request_path, parse_request_path) = if self.request.has_path_fields() {
let path_str = path.as_str(); let path_str = path.value();
assert!(path_str.starts_with('/'), "path needs to start with '/'"); assert!(path_str.starts_with('/'), "path needs to start with '/'");
assert!( assert!(
@ -313,7 +316,7 @@ impl ToTokens for Api {
} }
}; };
let endpoint_doc = format!("The `{}` API endpoint.\n\n{}", name, description); let endpoint_doc = format!("The `{}` API endpoint.\n\n{}", name, description.value());
let request_doc = format!("Data for a request to the `{}` API endpoint.", name); let request_doc = format!("Data for a request to the `{}` API endpoint.", name);
let response_doc = format!("Data in the response from the `{}` API endpoint.", name); let response_doc = format!("Data in the response from the `{}` API endpoint.", name);