mirror of
https://github.com/rwf2/Rocket.git
synced 2025-01-18 07:29:09 +00:00
Streamline raw identifier support in codegen.
This commit is contained in:
parent
97f6bc5dc0
commit
86ff66a69c
@ -45,9 +45,9 @@ struct Route {
|
||||
function: syn::ItemFn,
|
||||
/// The non-static parameters declared in the route segments.
|
||||
segments: IndexSet<Segment>,
|
||||
/// 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`.
|
||||
/// The parsed inputs to the user's function. The name is the param as the
|
||||
/// user wrote it, while the ident is the identifier that should be used
|
||||
/// during code generation, the `rocket_ident`.
|
||||
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()) {
|
||||
let span = segment.span;
|
||||
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"))
|
||||
}
|
||||
}
|
||||
@ -118,7 +118,7 @@ fn parse_route(attr: RouteAttribute, function: syn::ItemFn) -> Result<Route> {
|
||||
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.name())))
|
||||
.span_note(span, format!("expected argument named `{}` here", missing.name)))
|
||||
}
|
||||
|
||||
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 (mut decls, mut matchers, mut builders) = (vec![], vec![], vec![]);
|
||||
for segment in query_segments {
|
||||
let name = segment.name.name();
|
||||
let (ident, ty, span) = if segment.kind != Kind::Static {
|
||||
let (ident, ty) = route.inputs.iter()
|
||||
.find(|(name, _, _)| name == &segment.name)
|
||||
@ -232,6 +231,7 @@ fn query_exprs(route: &Route) -> Option<TokenStream> {
|
||||
Kind::Static => quote!()
|
||||
};
|
||||
|
||||
let name = segment.name.name();
|
||||
let matcher = match segment.kind {
|
||||
Kind::Single => quote_spanned! { span =>
|
||||
(_, #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.kind != Kind::Static)
|
||||
.map(|seg| &seg.name)
|
||||
.map(|name| route.inputs.iter().find(|(name2, ..)| name2 == name).unwrap())
|
||||
.map(|(name, _, ty)| { let id = name.ident().unwrap(); quote!(#id: #ty) });
|
||||
.map(|seg_name| route.inputs.iter().find(|(in_name, ..)| in_name == seg_name).unwrap())
|
||||
.map(|(name, _, ty)| (name.ident(), ty))
|
||||
.map(|(ident, ty)| quote!(#ident: #ty));
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
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 parameter_definitions = vec![];
|
||||
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) {
|
||||
Some(seg) if seg.source == Source::Path => {
|
||||
parameter_definitions.push(param_expr(seg, rocket_ident, &ty));
|
||||
|
@ -36,7 +36,7 @@ impl Segment {
|
||||
};
|
||||
|
||||
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 {
|
||||
|
@ -231,9 +231,9 @@ impl InternalUriParams {
|
||||
let (mut extra, mut dup) = (vec![], vec![]);
|
||||
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(ref entry) if entry.is_some() => dup.push(name.ident()),
|
||||
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) {
|
||||
match self {
|
||||
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);
|
||||
let (constructors, matchers, builders) = fields.iter().map(|field| {
|
||||
let (ident, span) = (&field.ident, field.span());
|
||||
let default_name_source = NameSource::from(ident.clone().expect("named"));
|
||||
let name_source = Form::from_attrs("form", &field.attrs)
|
||||
let default_name = NameSource::from(ident.clone().expect("named"));
|
||||
let name = Form::from_attrs("form", &field.attrs)
|
||||
.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 = 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 name = name_source.name();
|
||||
let name = name.name();
|
||||
let matcher = quote_spanned! { span =>
|
||||
#name => { #ident = #_Some(#ty::from_form_value(__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 builder = variant.builder(|_| unreachable!());
|
||||
let builder = variant.builder(|_| unreachable!("no fields"));
|
||||
Ok(quote! {
|
||||
if uncased == #variant_str {
|
||||
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()))?;
|
||||
|
||||
let name = name_source.name();
|
||||
|
||||
quote_spanned!(span => f.write_named_value(#name, &#accessor)?;)
|
||||
} else {
|
||||
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
|
||||
/// normally constructed using FromMeta, From<String>, or From<Ident> depending
|
||||
/// on the source.
|
||||
/// Represents the source of a name read by codegen, which may or may not be a
|
||||
/// valid identifier. A `NameSource` is typically constructed indirectly via
|
||||
/// FromMeta, or From<Ident> or directly from a string via `NameSource::new()`.
|
||||
///
|
||||
/// NameSource implements Hash, PartialEq, and Eq, and additionally PartialEq<S>
|
||||
/// for all types `S: AsStr<str>`. These implementations all compare the value
|
||||
@ -58,24 +58,34 @@ pub struct NameSource {
|
||||
}
|
||||
|
||||
impl NameSource {
|
||||
/// Returns the name as a string. Notably, if this NameSource was
|
||||
/// constructed from an Ident this method returns a name *without* an `r#`
|
||||
/// prefix.
|
||||
/// Creates a new `NameSource` from the string `name` and span `span`. If
|
||||
/// `name` is a valid ident, the ident is stored as well.
|
||||
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 {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Returns the Ident this NameSource was originally constructed from,
|
||||
/// if applicable.
|
||||
pub fn ident(&self) -> Option<&Ident> {
|
||||
self.ident.as_ref()
|
||||
/// Returns the Ident corresponding to `self`, if any, otherwise panics. If
|
||||
/// `self` was constructed from an `Ident`, this never panics. Otherwise,
|
||||
/// panics if the string `self` was constructed from was not a valid ident.
|
||||
pub fn ident(&self) -> &Ident {
|
||||
self.ident.as_ref().expect("ident from namesource")
|
||||
}
|
||||
}
|
||||
|
||||
impl devise::FromMeta for NameSource {
|
||||
fn from_meta(meta: devise::MetaItem<'_>) -> devise::Result<Self> {
|
||||
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"))
|
||||
@ -84,25 +94,13 @@ impl devise::FromMeta for NameSource {
|
||||
|
||||
impl From<Ident> for NameSource {
|
||||
fn from(ident: Ident) -> Self {
|
||||
Self {
|
||||
name: ident.unraw().to_string(),
|
||||
ident: Some(ident),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for NameSource {
|
||||
fn from(string: String) -> Self {
|
||||
Self {
|
||||
name: string,
|
||||
ident: None,
|
||||
}
|
||||
Self { name: ident.unraw().to_string(), ident: Some(ident), }
|
||||
}
|
||||
}
|
||||
|
||||
impl std::hash::Hash for NameSource {
|
||||
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()
|
||||
.mount("/", routes![get, swap])
|
||||
.register(catchers![catch]);
|
||||
let client = Client::new(rocket).unwrap();
|
||||
|
||||
let client = Client::untracked(rocket).unwrap();
|
||||
|
||||
let response = client.get("/example?type=1").dispatch();
|
||||
assert_eq!(response.into_string().unwrap(), "example is 1");
|
||||
|
Loading…
Reference in New Issue
Block a user