Skip to content

Commit

Permalink
Support raw identifiers in forms, routes, uri.
Browse files Browse the repository at this point in the history
Resolves rwf2#881.
  • Loading branch information
jebrosen authored and SergioBenitez committed Nov 3, 2020
1 parent b5e4dde commit 97f6bc5
Show file tree
Hide file tree
Showing 10 changed files with 201 additions and 42 deletions.
22 changes: 11 additions & 11 deletions core/codegen/src/attribute/route.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use devise::ext::{SpanDiagnosticExt, TypeExt};
use indexmap::IndexSet;

use crate::proc_macro_ext::{Diagnostics, StringLit};
use crate::syn_ext::IdentExt;
use crate::syn_ext::{IdentExt, NameSource};
use crate::proc_macro2::{TokenStream, Span};
use crate::http_codegen::{Method, MediaType, RoutePath, DataSegment, Optional};
use crate::attribute::segments::{Source, Kind, Segment};
Expand Down Expand Up @@ -48,7 +48,7 @@ struct Route {
/// The parsed inputs to the user's function. The first ident is the ident
/// as the user wrote it, while the second ident is the identifier that
/// should be used during code generation, the `rocket_ident`.
inputs: Vec<(syn::Ident, syn::Ident, syn::Type)>,
inputs: Vec<(NameSource, syn::Ident, syn::Type)>,
}

fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> {
Expand All @@ -74,7 +74,7 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> {
for segment in iter.filter(|s| s.is_dynamic()) {
let span = segment.span;
if let Some(previous) = set.replace(segment.clone()) {
diags.push(span.error(format!("duplicate parameter: `{}`", previous.name))
diags.push(span.error(format!("duplicate parameter: `{}`", previous.name.name()))
.span_note(previous.span, "previous parameter with the same name here"))
}
}
Expand Down Expand Up @@ -110,15 +110,15 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> {
};

let rocket_ident = ident.prepend(ROCKET_PARAM_PREFIX);
inputs.push((ident.clone(), rocket_ident, ty.with_stripped_lifetimes()));
inputs.push((ident.clone().into(), rocket_ident, ty.with_stripped_lifetimes()));
fn_segments.insert(ident.into());
}

// Check that all of the declared parameters are function inputs.
let span = function.sig.paren_token.span;
for missing in segments.difference(&fn_segments) {
diags.push(missing.span.error("unused dynamic parameter")
.span_note(span, format!("expected argument named `{}` here", missing.name)))
.span_note(span, format!("expected argument named `{}` here", missing.name.name())))
}

diags.head_err_or(Route { attribute: attr, function, inputs, segments })
Expand Down Expand Up @@ -207,10 +207,10 @@ fn query_exprs(route: &Route) -> Option<TokenStream> {
let query_segments = route.attribute.path.query.as_ref()?;
let (mut decls, mut matchers, mut builders) = (vec![], vec![], vec![]);
for segment in query_segments {
let name = &segment.name;
let name = segment.name.name();
let (ident, ty, span) = if segment.kind != Kind::Static {
let (ident, ty) = route.inputs.iter()
.find(|(ident, _, _)| ident == &segment.name)
.find(|(name, _, _)| name == &segment.name)
.map(|(_, rocket_ident, ty)| (rocket_ident, ty))
.unwrap();

Expand Down Expand Up @@ -327,8 +327,8 @@ fn generate_internal_uri_macro(route: &Route) -> TokenStream {
.filter(|seg| seg.source == Source::Path || seg.source == Source::Query)
.filter(|seg| seg.kind != Kind::Static)
.map(|seg| &seg.name)
.map(|name| route.inputs.iter().find(|(ident, ..)| ident == name).unwrap())
.map(|(ident, _, ty)| quote!(#ident: #ty));
.map(|name| route.inputs.iter().find(|(name2, ..)| name2 == name).unwrap())
.map(|(name, _, ty)| { let id = name.ident().unwrap(); quote!(#id: #ty) });

let mut hasher = DefaultHasher::new();
route.function.sig.ident.hash(&mut hasher);
Expand Down Expand Up @@ -385,8 +385,8 @@ fn codegen_route(route: Route) -> Result<TokenStream> {
let mut data_stmt = None;
let mut req_guard_definitions = vec![];
let mut parameter_definitions = vec![];
for (ident, rocket_ident, ty) in &route.inputs {
let fn_segment: Segment = ident.into();
for (name, rocket_ident, ty) in &route.inputs {
let fn_segment: Segment = name.ident().unwrap().into();
match route.segments.get(&fn_segment) {
Some(seg) if seg.source == Source::Path => {
parameter_definitions.push(param_expr(seg, rocket_ident, &ty));
Expand Down
7 changes: 4 additions & 3 deletions core/codegen/src/attribute/segments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use crate::proc_macro2::Span;
use crate::http::uri::{self, UriPart};
use crate::http::route::RouteSegment;
use crate::proc_macro_ext::{Diagnostics, StringLit, PResult, DResult};
use crate::syn_ext::NameSource;

pub use crate::http::route::{Error, Kind};

Expand All @@ -14,7 +15,7 @@ pub struct Segment {
pub span: Span,
pub kind: Kind,
pub source: Source,
pub name: String,
pub name: NameSource,
pub index: Option<usize>,
}

Expand All @@ -35,7 +36,7 @@ impl Segment {
};

let (kind, index) = (segment.kind, segment.index);
Segment { span, kind, source, index, name: segment.name.into_owned() }
Segment { span, kind, source, index, name: NameSource::from(segment.name.to_string()) }
}

pub fn is_wild(&self) -> bool {
Expand All @@ -56,7 +57,7 @@ impl From<&syn::Ident> for Segment {
kind: Kind::Static,
source: Source::Unknown,
span: ident.span(),
name: ident.to_string(),
name: ident.clone().into(),
index: None,
}
}
Expand Down
27 changes: 14 additions & 13 deletions core/codegen/src/bang/uri_parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::syn::punctuated::Punctuated;

use crate::http::{uri::Origin, ext::IntoOwned};
use crate::proc_macro2::{TokenStream, Span};
use crate::syn_ext::NameSource;

// TODO(diag): Use 'Diagnostic' in place of syn::Error.

Expand All @@ -20,7 +21,7 @@ pub enum ArgExpr {
#[derive(Debug)]
pub enum Arg {
Unnamed(ArgExpr),
Named(Ident, Token![=], ArgExpr),
Named(NameSource, Token![=], ArgExpr),
}

#[derive(Debug)]
Expand Down Expand Up @@ -51,7 +52,7 @@ pub enum Validation<'a> {
// Number expected, what we actually got.
Unnamed(usize, usize),
// (Missing, Extra, Duplicate)
Named(Vec<&'a Ident>, Vec<&'a Ident>, Vec<&'a Ident>),
Named(Vec<NameSource>, Vec<&'a Ident>, Vec<&'a Ident>),
// Everything is okay; here are the expressions in the route decl order.
Ok(Vec<&'a ArgExpr>)
}
Expand Down Expand Up @@ -94,7 +95,7 @@ impl Parse for Arg {
let ident = input.parse::<Ident>()?;
let eq_token = input.parse::<Token![=]>()?;
let expr = input.parse::<ArgExpr>()?;
Ok(Arg::Named(ident, eq_token, expr))
Ok(Arg::Named(ident.into(), eq_token, expr))
} else {
let expr = input.parse::<ArgExpr>()?;
Ok(Arg::Unnamed(expr))
Expand Down Expand Up @@ -223,16 +224,16 @@ impl InternalUriParams {
else { Validation::Ok(args.unnamed().unwrap().collect()) }
},
Args::Named(_) => {
let mut params: IndexMap<&Ident, Option<&ArgExpr>> = self.fn_args.iter()
.map(|FnArg { ident, .. }| (ident, None))
let mut params: IndexMap<NameSource, Option<&ArgExpr>> = self.fn_args.iter()
.map(|FnArg { ident, .. }| (ident.clone().into(), None))
.collect();

let (mut extra, mut dup) = (vec![], vec![]);
for (ident, expr) in args.named().unwrap() {
match params.get_mut(ident) {
Some(ref entry) if entry.is_some() => dup.push(ident),
for (name, expr) in args.named().unwrap() {
match params.get_mut(name) {
Some(ref entry) if entry.is_some() => dup.push(name.ident().unwrap()),
Some(entry) => *entry = Some(expr),
None => extra.push(ident),
None => extra.push(name.ident().unwrap()),
}
}

Expand Down Expand Up @@ -279,9 +280,9 @@ impl Arg {
}
}

fn named(&self) -> (&Ident, &ArgExpr) {
fn named(&self) -> (&NameSource, &ArgExpr) {
match self {
Arg::Named(ident, _, expr) => (ident, expr),
Arg::Named(name, _, expr) => (name, expr),
_ => panic!("Called Arg::named() on an Arg::Unnamed!"),
}
}
Expand All @@ -294,7 +295,7 @@ impl Args {
}
}

fn named(&self) -> Option<impl Iterator<Item = (&Ident, &ArgExpr)>> {
fn named(&self) -> Option<impl Iterator<Item = (&NameSource, &ArgExpr)>> {
match self {
Args::Named(args) => Some(args.iter().map(|arg| arg.named())),
_ => None
Expand Down Expand Up @@ -338,7 +339,7 @@ impl ToTokens for Arg {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Arg::Unnamed(e) => e.to_tokens(tokens),
Arg::Named(ident, eq, expr) => tokens.extend(quote!(#ident #eq #expr)),
Arg::Named(name, eq, expr) => { let id = name.ident().unwrap(); tokens.extend(quote!(#id #eq #expr)) },
}
}
}
Expand Down
18 changes: 10 additions & 8 deletions core/codegen/src/derive/from_form.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use devise::{*, ext::{TypeExt, Split3, SpanDiagnosticExt}};

use crate::proc_macro2::{Span, TokenStream};
use crate::syn_ext::NameSource;

#[derive(FromMeta)]
pub struct Form {
Expand All @@ -9,7 +10,7 @@ pub struct Form {

pub struct FormField {
pub span: Span,
pub name: String
pub name: NameSource,
}

fn is_valid_field_name(s: &str) -> bool {
Expand All @@ -25,12 +26,12 @@ fn is_valid_field_name(s: &str) -> bool {

impl FromMeta for FormField {
fn from_meta(meta: MetaItem<'_>) -> Result<Self> {
let string = String::from_meta(meta)?;
if !is_valid_field_name(&string) {
let name = NameSource::from_meta(meta)?;
if !is_valid_field_name(name.name()) {
return Err(meta.value_span().error("invalid form field name"));
}

Ok(FormField { span: meta.value_span(), name: string })
Ok(FormField { span: meta.value_span(), name })
}
}

Expand All @@ -44,7 +45,7 @@ fn validate_struct(_: &DeriveGenerator, data: Struct<'_>) -> Result<()> {
let id = field.ident.as_ref().expect("named field");
let field = match Form::from_attrs("form", &field.attrs) {
Some(result) => result?.field,
None => FormField { span: Spanned::span(&id), name: id.to_string() }
None => FormField { span: Spanned::span(&id), name: id.clone().into() }
};

if let Some(span) = names.get(&field.name) {
Expand Down Expand Up @@ -86,10 +87,10 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream {
define_vars_and_mods!(_None, _Some, _Ok, _Err);
let (constructors, matchers, builders) = fields.iter().map(|field| {
let (ident, span) = (&field.ident, field.span());
let default_name = ident.as_ref().expect("named").to_string();
let name = Form::from_attrs("form", &field.attrs)
let default_name_source = NameSource::from(ident.clone().expect("named"));
let name_source = Form::from_attrs("form", &field.attrs)
.map(|result| result.map(|form| form.field.name))
.unwrap_or_else(|| Ok(default_name))?;
.unwrap_or_else(|| Ok(default_name_source))?;

let ty = field.ty.with_stripped_lifetimes();
let ty = quote_spanned! {
Expand All @@ -98,6 +99,7 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream {

let constructor = quote_spanned!(span => let mut #ident = #_None;);

let name = name_source.name();
let matcher = quote_spanned! { span =>
#name => { #ident = #_Some(#ty::from_form_value(__v)
.map_err(|_| #form_error::BadValue(__k, __v))?); },
Expand Down
9 changes: 6 additions & 3 deletions core/codegen/src/derive/from_form_value.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use devise::{*, ext::SpanDiagnosticExt};

use crate::proc_macro2::TokenStream;
use crate::syn_ext::NameSource;

#[derive(FromMeta)]
struct Form {
value: String,
value: NameSource,
}

pub fn derive_from_form_value(input: proc_macro::TokenStream) -> TokenStream {
Expand Down Expand Up @@ -41,10 +42,12 @@ pub fn derive_from_form_value(input: proc_macro::TokenStream) -> TokenStream {
.try_map_enum(null_enum_mapper)
.try_map_variant(|_, variant| {
define_vars_and_mods!(_Ok);
let variant_str = Form::from_attrs("form", &variant.attrs)
.unwrap_or_else(|| Ok(Form { value: variant.ident.to_string() }))?
let variant_name_source = Form::from_attrs("form", &variant.attrs)
.unwrap_or_else(|| Ok(Form { value: variant.ident.clone().into() }))?
.value;

let variant_str = variant_name_source.name();

let builder = variant.builder(|_| unreachable!());
Ok(quote! {
if uncased == #variant_str {
Expand Down
6 changes: 4 additions & 2 deletions core/codegen/src/derive/uri_display.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ pub fn derive_uri_display_query(input: proc_macro::TokenStream) -> TokenStream {
let span = field.span().into();
let accessor = field.accessor();
let tokens = if let Some(ref ident) = field.ident {
let name = Form::from_attrs("form", &field.attrs)
let name_source = Form::from_attrs("form", &field.attrs)
.map(|result| result.map(|form| form.field.name))
.unwrap_or_else(|| Ok(ident.to_string()))?;
.unwrap_or_else(|| Ok(ident.clone().into()))?;

let name = name_source.name();

quote_spanned!(span => f.write_named_value(#name, &#accessor)?;)
} else {
Expand Down
Loading

0 comments on commit 97f6bc5

Please sign in to comment.