mirror of https://github.com/rwf2/Rocket.git
Streamline raw identifier support in codegen.
This commit is contained in:
parent
97f6bc5dc0
commit
86ff66a69c
|
@ -45,9 +45,9 @@ struct Route {
|
||||||
function: syn::ItemFn,
|
function: syn::ItemFn,
|
||||||
/// The non-static parameters declared in the route segments.
|
/// The non-static parameters declared in the route segments.
|
||||||
segments: IndexSet<Segment>,
|
segments: IndexSet<Segment>,
|
||||||
/// The parsed inputs to the user's function. The first ident is the ident
|
/// The parsed inputs to the user's function. The name is the param as the
|
||||||
/// as the user wrote it, while the second ident is the identifier that
|
/// user wrote it, while the ident is the identifier that should be used
|
||||||
/// should be used during code generation, the `rocket_ident`.
|
/// during code generation, the `rocket_ident`.
|
||||||
inputs: Vec<(NameSource, syn::Ident, syn::Type)>,
|
inputs: Vec<(NameSource, syn::Ident, syn::Type)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> {
|
||||||
for segment in iter.filter(|s| s.is_dynamic()) {
|
for segment in iter.filter(|s| s.is_dynamic()) {
|
||||||
let span = segment.span;
|
let span = segment.span;
|
||||||
if let Some(previous) = set.replace(segment.clone()) {
|
if let Some(previous) = set.replace(segment.clone()) {
|
||||||
diags.push(span.error(format!("duplicate parameter: `{}`", previous.name.name()))
|
diags.push(span.error(format!("duplicate parameter: `{}`", previous.name))
|
||||||
.span_note(previous.span, "previous parameter with the same name here"))
|
.span_note(previous.span, "previous parameter with the same name here"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> {
|
||||||
let span = function.sig.paren_token.span;
|
let span = function.sig.paren_token.span;
|
||||||
for missing in segments.difference(&fn_segments) {
|
for missing in segments.difference(&fn_segments) {
|
||||||
diags.push(missing.span.error("unused dynamic parameter")
|
diags.push(missing.span.error("unused dynamic parameter")
|
||||||
.span_note(span, format!("expected argument named `{}` here", missing.name.name())))
|
.span_note(span, format!("expected argument named `{}` here", missing.name)))
|
||||||
}
|
}
|
||||||
|
|
||||||
diags.head_err_or(Route { attribute: attr, function, inputs, segments })
|
diags.head_err_or(Route { attribute: attr, function, inputs, segments })
|
||||||
|
@ -207,7 +207,6 @@ fn query_exprs(route: &Route) -> Option<TokenStream> {
|
||||||
let query_segments = route.attribute.path.query.as_ref()?;
|
let query_segments = route.attribute.path.query.as_ref()?;
|
||||||
let (mut decls, mut matchers, mut builders) = (vec![], vec![], vec![]);
|
let (mut decls, mut matchers, mut builders) = (vec![], vec![], vec![]);
|
||||||
for segment in query_segments {
|
for segment in query_segments {
|
||||||
let name = segment.name.name();
|
|
||||||
let (ident, ty, span) = if segment.kind != Kind::Static {
|
let (ident, ty, span) = if segment.kind != Kind::Static {
|
||||||
let (ident, ty) = route.inputs.iter()
|
let (ident, ty) = route.inputs.iter()
|
||||||
.find(|(name, _, _)| name == &segment.name)
|
.find(|(name, _, _)| name == &segment.name)
|
||||||
|
@ -232,6 +231,7 @@ fn query_exprs(route: &Route) -> Option<TokenStream> {
|
||||||
Kind::Static => quote!()
|
Kind::Static => quote!()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let name = segment.name.name();
|
||||||
let matcher = match segment.kind {
|
let matcher = match segment.kind {
|
||||||
Kind::Single => quote_spanned! { span =>
|
Kind::Single => quote_spanned! { span =>
|
||||||
(_, #name, __v) => {
|
(_, #name, __v) => {
|
||||||
|
@ -327,8 +327,9 @@ fn generate_internal_uri_macro(route: &Route) -> TokenStream {
|
||||||
.filter(|seg| seg.source == Source::Path || seg.source == Source::Query)
|
.filter(|seg| seg.source == Source::Path || seg.source == Source::Query)
|
||||||
.filter(|seg| seg.kind != Kind::Static)
|
.filter(|seg| seg.kind != Kind::Static)
|
||||||
.map(|seg| &seg.name)
|
.map(|seg| &seg.name)
|
||||||
.map(|name| route.inputs.iter().find(|(name2, ..)| name2 == name).unwrap())
|
.map(|seg_name| route.inputs.iter().find(|(in_name, ..)| in_name == seg_name).unwrap())
|
||||||
.map(|(name, _, ty)| { let id = name.ident().unwrap(); quote!(#id: #ty) });
|
.map(|(name, _, ty)| (name.ident(), ty))
|
||||||
|
.map(|(ident, ty)| quote!(#ident: #ty));
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
route.function.sig.ident.hash(&mut hasher);
|
route.function.sig.ident.hash(&mut hasher);
|
||||||
|
@ -386,7 +387,7 @@ fn codegen_route(route: Route) -> Result<TokenStream> {
|
||||||
let mut req_guard_definitions = vec![];
|
let mut req_guard_definitions = vec![];
|
||||||
let mut parameter_definitions = vec![];
|
let mut parameter_definitions = vec![];
|
||||||
for (name, rocket_ident, ty) in &route.inputs {
|
for (name, rocket_ident, ty) in &route.inputs {
|
||||||
let fn_segment: Segment = name.ident().unwrap().into();
|
let fn_segment: Segment = name.ident().into();
|
||||||
match route.segments.get(&fn_segment) {
|
match route.segments.get(&fn_segment) {
|
||||||
Some(seg) if seg.source == Source::Path => {
|
Some(seg) if seg.source == Source::Path => {
|
||||||
parameter_definitions.push(param_expr(seg, rocket_ident, &ty));
|
parameter_definitions.push(param_expr(seg, rocket_ident, &ty));
|
||||||
|
|
|
@ -36,7 +36,7 @@ impl Segment {
|
||||||
};
|
};
|
||||||
|
|
||||||
let (kind, index) = (segment.kind, segment.index);
|
let (kind, index) = (segment.kind, segment.index);
|
||||||
Segment { span, kind, source, index, name: NameSource::from(segment.name.to_string()) }
|
Segment { span, kind, source, index, name: NameSource::new(&segment.name, span) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_wild(&self) -> bool {
|
pub fn is_wild(&self) -> bool {
|
||||||
|
|
|
@ -231,9 +231,9 @@ impl InternalUriParams {
|
||||||
let (mut extra, mut dup) = (vec![], vec![]);
|
let (mut extra, mut dup) = (vec![], vec![]);
|
||||||
for (name, expr) in args.named().unwrap() {
|
for (name, expr) in args.named().unwrap() {
|
||||||
match params.get_mut(name) {
|
match params.get_mut(name) {
|
||||||
Some(ref entry) if entry.is_some() => dup.push(name.ident().unwrap()),
|
Some(ref entry) if entry.is_some() => dup.push(name.ident()),
|
||||||
Some(entry) => *entry = Some(expr),
|
Some(entry) => *entry = Some(expr),
|
||||||
None => extra.push(name.ident().unwrap()),
|
None => extra.push(name.ident()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,7 +339,10 @@ impl ToTokens for Arg {
|
||||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||||
match self {
|
match self {
|
||||||
Arg::Unnamed(e) => e.to_tokens(tokens),
|
Arg::Unnamed(e) => e.to_tokens(tokens),
|
||||||
Arg::Named(name, eq, expr) => { let id = name.ident().unwrap(); tokens.extend(quote!(#id #eq #expr)) },
|
Arg::Named(name, eq, expr) => {
|
||||||
|
let ident = name.ident();
|
||||||
|
tokens.extend(quote!(#ident #eq #expr))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -87,10 +87,10 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream {
|
||||||
define_vars_and_mods!(_None, _Some, _Ok, _Err);
|
define_vars_and_mods!(_None, _Some, _Ok, _Err);
|
||||||
let (constructors, matchers, builders) = fields.iter().map(|field| {
|
let (constructors, matchers, builders) = fields.iter().map(|field| {
|
||||||
let (ident, span) = (&field.ident, field.span());
|
let (ident, span) = (&field.ident, field.span());
|
||||||
let default_name_source = NameSource::from(ident.clone().expect("named"));
|
let default_name = NameSource::from(ident.clone().expect("named"));
|
||||||
let name_source = Form::from_attrs("form", &field.attrs)
|
let name = Form::from_attrs("form", &field.attrs)
|
||||||
.map(|result| result.map(|form| form.field.name))
|
.map(|result| result.map(|form| form.field.name))
|
||||||
.unwrap_or_else(|| Ok(default_name_source))?;
|
.unwrap_or_else(|| Ok(default_name))?;
|
||||||
|
|
||||||
let ty = field.ty.with_stripped_lifetimes();
|
let ty = field.ty.with_stripped_lifetimes();
|
||||||
let ty = quote_spanned! {
|
let ty = quote_spanned! {
|
||||||
|
@ -99,7 +99,7 @@ pub fn derive_from_form(input: proc_macro::TokenStream) -> TokenStream {
|
||||||
|
|
||||||
let constructor = quote_spanned!(span => let mut #ident = #_None;);
|
let constructor = quote_spanned!(span => let mut #ident = #_None;);
|
||||||
|
|
||||||
let name = name_source.name();
|
let name = name.name();
|
||||||
let matcher = quote_spanned! { span =>
|
let matcher = quote_spanned! { span =>
|
||||||
#name => { #ident = #_Some(#ty::from_form_value(__v)
|
#name => { #ident = #_Some(#ty::from_form_value(__v)
|
||||||
.map_err(|_| #form_error::BadValue(__k, __v))?); },
|
.map_err(|_| #form_error::BadValue(__k, __v))?); },
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub fn derive_from_form_value(input: proc_macro::TokenStream) -> TokenStream {
|
||||||
|
|
||||||
let variant_str = variant_name_source.name();
|
let variant_str = variant_name_source.name();
|
||||||
|
|
||||||
let builder = variant.builder(|_| unreachable!());
|
let builder = variant.builder(|_| unreachable!("no fields"));
|
||||||
Ok(quote! {
|
Ok(quote! {
|
||||||
if uncased == #variant_str {
|
if uncased == #variant_str {
|
||||||
return #_Ok(#builder);
|
return #_Ok(#builder);
|
||||||
|
|
|
@ -62,7 +62,6 @@ pub fn derive_uri_display_query(input: proc_macro::TokenStream) -> TokenStream {
|
||||||
.unwrap_or_else(|| Ok(ident.clone().into()))?;
|
.unwrap_or_else(|| Ok(ident.clone().into()))?;
|
||||||
|
|
||||||
let name = name_source.name();
|
let name = name_source.name();
|
||||||
|
|
||||||
quote_spanned!(span => f.write_named_value(#name, &#accessor)?;)
|
quote_spanned!(span => f.write_named_value(#name, &#accessor)?;)
|
||||||
} else {
|
} else {
|
||||||
quote_spanned!(span => f.write_value(&#accessor)?;)
|
quote_spanned!(span => f.write_value(&#accessor)?;)
|
||||||
|
|
|
@ -44,9 +44,9 @@ impl TokenStreamExt for crate::proc_macro2::TokenStream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents the source of a name; usually either a string or an Ident. It is
|
/// Represents the source of a name read by codegen, which may or may not be a
|
||||||
/// normally constructed using FromMeta, From<String>, or From<Ident> depending
|
/// valid identifier. A `NameSource` is typically constructed indirectly via
|
||||||
/// on the source.
|
/// FromMeta, or From<Ident> or directly from a string via `NameSource::new()`.
|
||||||
///
|
///
|
||||||
/// NameSource implements Hash, PartialEq, and Eq, and additionally PartialEq<S>
|
/// NameSource implements Hash, PartialEq, and Eq, and additionally PartialEq<S>
|
||||||
/// for all types `S: AsStr<str>`. These implementations all compare the value
|
/// for all types `S: AsStr<str>`. These implementations all compare the value
|
||||||
|
@ -58,24 +58,34 @@ pub struct NameSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NameSource {
|
impl NameSource {
|
||||||
/// Returns the name as a string. Notably, if this NameSource was
|
/// Creates a new `NameSource` from the string `name` and span `span`. If
|
||||||
/// constructed from an Ident this method returns a name *without* an `r#`
|
/// `name` is a valid ident, the ident is stored as well.
|
||||||
/// prefix.
|
pub fn new<S: AsRef<str>>(name: S, span: crate::proc_macro2::Span) -> Self {
|
||||||
|
let name = name.as_ref();
|
||||||
|
syn::parse_str::<Ident>(name)
|
||||||
|
.map(|mut ident| { ident.set_span(span); ident })
|
||||||
|
.map(|ident| NameSource::from(ident))
|
||||||
|
.unwrap_or_else(|_| NameSource { name: name.into(), ident: None })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the name as a string. Notably, if `self` was constructed from an
|
||||||
|
/// Ident this method returns a name *without* an `r#` prefix.
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the Ident this NameSource was originally constructed from,
|
/// Returns the Ident corresponding to `self`, if any, otherwise panics. If
|
||||||
/// if applicable.
|
/// `self` was constructed from an `Ident`, this never panics. Otherwise,
|
||||||
pub fn ident(&self) -> Option<&Ident> {
|
/// panics if the string `self` was constructed from was not a valid ident.
|
||||||
self.ident.as_ref()
|
pub fn ident(&self) -> &Ident {
|
||||||
|
self.ident.as_ref().expect("ident from namesource")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl devise::FromMeta for NameSource {
|
impl devise::FromMeta for NameSource {
|
||||||
fn from_meta(meta: devise::MetaItem<'_>) -> devise::Result<Self> {
|
fn from_meta(meta: devise::MetaItem<'_>) -> devise::Result<Self> {
|
||||||
if let syn::Lit::Str(s) = meta.lit()? {
|
if let syn::Lit::Str(s) = meta.lit()? {
|
||||||
return Ok(Self { name: s.value(), ident: None });
|
return Ok(NameSource::new(s.value(), s.span()));
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(meta.value_span().error("invalid value: expected string literal"))
|
Err(meta.value_span().error("invalid value: expected string literal"))
|
||||||
|
@ -84,25 +94,13 @@ impl devise::FromMeta for NameSource {
|
||||||
|
|
||||||
impl From<Ident> for NameSource {
|
impl From<Ident> for NameSource {
|
||||||
fn from(ident: Ident) -> Self {
|
fn from(ident: Ident) -> Self {
|
||||||
Self {
|
Self { name: ident.unraw().to_string(), ident: Some(ident), }
|
||||||
name: ident.unraw().to_string(),
|
|
||||||
ident: Some(ident),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for NameSource {
|
|
||||||
fn from(string: String) -> Self {
|
|
||||||
Self {
|
|
||||||
name: string,
|
|
||||||
ident: None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::hash::Hash for NameSource {
|
impl std::hash::Hash for NameSource {
|
||||||
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
|
fn hash<H: std::hash::Hasher>(&self, hasher: &mut H) {
|
||||||
self.name.hash(hasher)
|
self.name().hash(hasher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@ fn test_raw_ident() {
|
||||||
let rocket = rocket::ignite()
|
let rocket = rocket::ignite()
|
||||||
.mount("/", routes![get, swap])
|
.mount("/", routes![get, swap])
|
||||||
.register(catchers![catch]);
|
.register(catchers![catch]);
|
||||||
let client = Client::new(rocket).unwrap();
|
|
||||||
|
let client = Client::untracked(rocket).unwrap();
|
||||||
|
|
||||||
let response = client.get("/example?type=1").dispatch();
|
let response = client.get("/example?type=1").dispatch();
|
||||||
assert_eq!(response.into_string().unwrap(), "example is 1");
|
assert_eq!(response.into_string().unwrap(), "example is 1");
|
||||||
|
|
Loading…
Reference in New Issue