rocket_codegen/bang/
uri_parsing.rs

1use std::ops::Deref;
2
3use indexmap::IndexMap;
4use devise::{Spanned, ext::TypeExt};
5use quote::{ToTokens, TokenStreamExt};
6use syn::{Expr, Ident, LitStr, Path, Token, Type};
7use syn::parse::{self, Parse, ParseStream, Parser};
8use syn::punctuated::Punctuated;
9use proc_macro2::{TokenStream, TokenTree, Span};
10use rocket_http::uri::{Error, Reference};
11
12use crate::http::uri::{Uri, Origin, Absolute, fmt};
13use crate::http::ext::IntoOwned;
14use crate::proc_macro_ext::StringLit;
15use crate::attribute::param::{Parameter, Dynamic};
16use crate::name::Name;
17
18// TODO(diag): Use 'Diagnostic' in place of syn::Error.
19
20#[derive(Debug)]
21pub enum ArgExpr {
22    Expr(Expr),
23    Ignored(Token![_]),
24}
25
26#[derive(Debug)]
27pub enum Arg {
28    Unnamed(ArgExpr),
29    Named(Name, Ident, Token![=], ArgExpr),
30}
31
32#[derive(Debug)]
33pub enum Args {
34    Unnamed(Punctuated<Arg, Token![,]>),
35    Named(Punctuated<Arg, Token![,]>),
36}
37
38/// A string literal parsed as a URI.
39#[derive(Debug)]
40pub struct UriLit(Uri<'static>, Span);
41
42/// An expression in a URI slot (prefix, suffix, or literal).
43#[derive(Debug)]
44pub enum UriExpr {
45    /// A string literal parsed as a URI.
46    Uri(UriLit),
47    /// An expression that will be typechecked to be some URI kind.
48    Expr(Expr),
49}
50
51/// See `UriMacro` for what each field represents.
52#[derive(Debug)]
53pub struct RouteInvocation {
54    pub path: Path,
55    pub args: Args,
56}
57
58/// See `UriMacro` for what each field represents.
59#[derive(Debug)]
60pub struct RoutedUri {
61    pub prefix: Option<UriExpr>,
62    pub route: RouteInvocation,
63    pub suffix: Option<UriExpr>,
64}
65
66// The macro can be invoked with 1, 2, or 3 arguments.
67//
68// As a `Literal`, with a single argument:
69//  uri!("/mount/point");
70//       ^-------------|
71//                 literal.0
72//
73// As `Routed`, with 1, 2, or 3 arguments: prefix/suffix optional.
74//  uri!("/mount/point", this::route(e1, e2, e3), "?some#suffix");
75//       ^-------------| ^---------|^----------|  |-----|------|
76//              routed.prefix      |           |   routed.suffix
77//                                 |   route.route.args
78//                        routed.route.path
79#[derive(Debug)]
80pub enum UriMacro {
81    Literal(UriLit),
82    Routed(RoutedUri),
83}
84
85#[derive(Debug)]
86pub enum Validation<'a> {
87    // Parameters that were ignored in a named argument setting.
88    NamedIgnored(Vec<&'a Dynamic>),
89    // Number expected, what we actually got.
90    Unnamed(usize, usize),
91    // (Missing, Extra, Duplicate)
92    Named(Vec<&'a Name>, Vec<&'a Ident>, Vec<&'a Ident>),
93    // Everything is okay; here are the expressions in the route decl order.
94    Ok(Vec<&'a ArgExpr>)
95}
96
97// This is invoked by Rocket itself. The `uri!` macro expands to a call to a
98// route-specific macro which in-turn expands to a call to `internal_uri!`,
99// passing along the user's invocation (`uri_mac`) from the original `uri!`
100// call. This is necessary so that we can converge the type information in the
101// route (from the route-specific macro) with the user's parameters (by
102// forwarding them to the internal_uri! call).
103//
104// `fn_args` are the URI arguments (excluding request guards) from the original
105// handler in the order they were declared in the URI (`<first>/<second>`).
106// `route_uri` is the full route URI itself.
107//
108// The syntax of `uri_mac` is that of `UriMacro`.
109//
110//  internal_uri!("/<one>/<_>?lang=en&<two>", (one: ty, two: ty), $($tt)*);
111//                ^----/----^ ^-----\-----^    ^-------/------^   ^-----|
112//               path_params    query_params       fn_args          uri_mac
113//                ^------ route_uri ------^
114#[derive(Debug)]
115pub struct InternalUriParams {
116    pub route_uri: Origin<'static>,
117    pub path_params: Vec<Parameter>,
118    pub query_params: Vec<Parameter>,
119    pub fn_args: Vec<FnArg>,
120    pub uri_mac: RoutedUri,
121}
122
123#[derive(Debug)]
124pub struct FnArg {
125    pub ident: Ident,
126    pub ty: Type,
127}
128
129fn err<T, S: AsRef<str>>(span: Span, s: S) -> parse::Result<T> {
130    Err(parse::Error::new(span, s.as_ref()))
131}
132
133impl Parse for ArgExpr {
134    fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
135        if input.peek(Token![_]) {
136            return Ok(ArgExpr::Ignored(input.parse::<Token![_]>()?));
137        }
138
139        input.parse::<Expr>().map(ArgExpr::Expr)
140    }
141}
142
143impl Parse for Arg {
144    fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
145        let has_key = input.peek2(Token![=]);
146        if has_key {
147            let ident = input.parse::<Ident>()?;
148            let eq_token = input.parse::<Token![=]>()?;
149            let expr = input.parse::<ArgExpr>()?;
150            Ok(Arg::Named(Name::from(&ident), ident, eq_token, expr))
151        } else {
152            let expr = input.parse::<ArgExpr>()?;
153            Ok(Arg::Unnamed(expr))
154        }
155    }
156}
157
158impl Parse for Args {
159    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
160        // If there are no arguments, finish early.
161        if input.cursor().eof() {
162            return Ok(Args::Unnamed(Punctuated::new()));
163        }
164
165        // Parse arguments. Ensure both types of args were not used at once.
166        let args = input.parse_terminated(Arg::parse, Token![,])?;
167        let mut first_is_named = None;
168        for arg in &args {
169            if let Some(first_is_named) = first_is_named {
170                if first_is_named != arg.is_named() {
171                    return err(args.span(), "named and unnamed parameters cannot be mixed");
172                }
173            } else {
174                first_is_named = Some(arg.is_named());
175            }
176        }
177
178        // Create the `Args` enum, which properly record one-kind-of-argument-ness.
179        match first_is_named {
180            Some(true) => Ok(Args::Named(args)),
181            _ => Ok(Args::Unnamed(args))
182        }
183    }
184}
185
186impl Parse for RouteInvocation {
187    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
188        let path = input.parse()?;
189        let args = if input.peek(syn::token::Paren) {
190            let args;
191            syn::parenthesized!(args in input);
192            args.parse()?
193        } else {
194            Args::Unnamed(Punctuated::new())
195        };
196
197        Ok(RouteInvocation { path, args })
198    }
199}
200
201impl Parse for UriLit {
202    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
203        let string = input.parse::<StringLit>()?;
204        let uri = match Uri::parse_any(&string) {
205            Ok(uri) => uri.into_owned(),
206            Err(e) => {
207                let span = string.subspan(e.index() + 1..(e.index() + 2));
208                return err(span, format!("invalid URI: {}", e));
209            }
210        };
211
212        Ok(UriLit(uri, string.span()))
213    }
214}
215
216impl UriMacro {
217    fn unary(input: ParseStream<'_>) -> parse::Result<Self> {
218        if input.peek(LitStr) {
219            Ok(UriMacro::Literal(input.parse()?))
220        } else {
221            Ok(UriMacro::Routed(RoutedUri {
222                prefix: None,
223                route: input.parse()?,
224                suffix: None,
225            }))
226        }
227    }
228
229    fn binary(prefix: TokenStream, middle: TokenStream) -> parse::Result<Self> {
230        Ok(UriMacro::Routed(RoutedUri {
231            prefix: UriExpr::parse_prefix.parse2(prefix)?,
232            route: syn::parse2(middle)?,
233            suffix: None,
234        }))
235    }
236
237    fn ternary(prefix: TokenStream, mid: TokenStream, suffix: TokenStream) -> parse::Result<Self> {
238        Ok(UriMacro::Routed(RoutedUri {
239            prefix: UriExpr::parse_prefix.parse2(prefix)?,
240            route: syn::parse2(mid)?,
241            suffix: UriExpr::parse_suffix.parse2(suffix)?
242        }))
243    }
244}
245
246impl Parse for UriMacro {
247    fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
248        use syn::buffer::Cursor;
249        use parse::{StepCursor, Result};
250
251        fn stream<'c>(cursor: StepCursor<'c, '_>) -> Result<(Option<TokenStream>, Cursor<'c>)> {
252            let mut stream = TokenStream::new();
253            let mut cursor = *cursor;
254            while let Some((tt, next)) = cursor.token_tree() {
255                cursor = next;
256                match tt {
257                    TokenTree::Punct(p) if p.as_char() == ',' => break,
258                    _ =>  stream.append(tt)
259                }
260            }
261
262            stream.is_empty()
263                .then(|| Ok((None, cursor)))
264                .unwrap_or(Ok((Some(stream), cursor)))
265        }
266
267        let mut args = vec![];
268        while let Some(tokens) = input.step(stream)? {
269            args.push(tokens);
270        }
271
272        let (arg_count, mut iter) = (args.len(), args.into_iter());
273        let mut next = || iter.next().unwrap();
274        match arg_count {
275            0 => err(Span::call_site(), "expected at least 1 argument, found none"),
276            1 => UriMacro::unary.parse2(next()),
277            2 => UriMacro::binary(next(), next()),
278            3 => UriMacro::ternary(next(), next(), next()),
279            n => err(iter.nth(3).unwrap().span(),
280                format!("expected 1, 2, or 3 arguments, found {}", n))
281        }
282    }
283}
284
285impl Parse for RoutedUri {
286    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
287        match UriMacro::parse(input)? {
288            UriMacro::Routed(route) => Ok(route),
289            UriMacro::Literal(uri) => err(uri.span(), "expected route URI, found literal")
290        }
291    }
292}
293
294impl Parse for FnArg {
295    fn parse(input: ParseStream<'_>) -> parse::Result<FnArg> {
296        let ident = input.parse::<Ident>()?;
297        input.parse::<Token![:]>()?;
298        let mut ty = input.parse::<Type>()?;
299        ty.strip_lifetimes();
300        Ok(FnArg { ident, ty })
301    }
302}
303
304impl Parse for InternalUriParams {
305    fn parse(input: ParseStream<'_>) -> parse::Result<InternalUriParams> {
306        let route_uri_str = input.parse::<StringLit>()?;
307        input.parse::<Token![,]>()?;
308
309        // Validation should always succeed since this macro can only be called
310        // if the route attribute succeeded, implying a valid route URI.
311        let route_uri = Origin::parse_route(&route_uri_str)
312            .map(|o| o.into_normalized().into_owned())
313            .map_err(|_| input.error("internal error: invalid route URI"))?;
314
315        let content;
316        syn::parenthesized!(content in input);
317        let fn_args = content.parse_terminated(FnArg::parse, Token![,])?;
318        let fn_args = fn_args.into_iter().collect();
319
320        input.parse::<Token![,]>()?;
321        let uri_params = input.parse::<RoutedUri>()?;
322
323        let span = route_uri_str.subspan(1..route_uri.path().len() + 1);
324        let path_params = Parameter::parse_many::<fmt::Path>(route_uri.path().as_str(), span)
325            .map(|p| p.expect("internal error: invalid path parameter"))
326            .collect::<Vec<_>>();
327
328        let query = route_uri.query();
329        let query_params = query.map(|query| {
330            let i = route_uri.path().len() + 2;
331            let span = route_uri_str.subspan(i..(i + query.len()));
332            Parameter::parse_many::<fmt::Query>(query.as_str(), span)
333                .map(|p| p.expect("internal error: invalid query parameter"))
334                .collect::<Vec<_>>()
335        }).unwrap_or_default();
336
337        Ok(InternalUriParams {
338            route_uri,
339            path_params,
340            query_params,
341            fn_args,
342            uri_mac: uri_params
343        })
344    }
345}
346
347impl InternalUriParams {
348    pub fn fn_args_str(&self) -> String {
349        self.fn_args.iter()
350            .map(|FnArg { ident, ty }| {
351                let ty = ty.with_stripped_lifetimes();
352                let ty_str = quote!(#ty).to_string();
353                let ty_str: String = ty_str.chars().filter(|c| !c.is_whitespace()).collect();
354                format!("{}: {}", ident, ty_str)
355            })
356            .collect::<Vec<_>>()
357            .join(", ")
358    }
359
360    pub fn dynamic_path_params(&self) -> impl Iterator<Item = &Dynamic> + Clone {
361        self.path_params.iter()
362            .filter_map(|p| p.dynamic().or_else(|| p.ignored()))
363    }
364
365    pub fn dynamic_query_params(&self) -> impl Iterator<Item = &Dynamic> + Clone {
366        self.query_params.iter().filter_map(|p| p.dynamic())
367    }
368
369    pub fn validate(&self) -> Validation<'_> {
370        let args = &self.uri_mac.route.args;
371        let all_params = self.dynamic_path_params().chain(self.dynamic_query_params());
372        match args {
373            Args::Unnamed(args) => {
374                let (expected, actual) = (all_params.count(), args.len());
375                let unnamed_args = args.iter().map(|arg| arg.unnamed());
376                match expected == actual {
377                    true => Validation::Ok(unnamed_args.collect()),
378                    false => Validation::Unnamed(expected, actual)
379                }
380            },
381            Args::Named(args) => {
382                let ignored = all_params.clone().filter(|p| p.is_wild());
383                if ignored.clone().count() > 0 {
384                    return Validation::NamedIgnored(ignored.collect());
385                }
386
387                let mut params = all_params.map(|p| (&p.name, None))
388                    .collect::<IndexMap<&Name, Option<&ArgExpr>>>();
389
390                let (mut extra, mut dup) = (vec![], vec![]);
391                let named_args = args.iter().map(|arg| arg.named());
392                for (name, ident, expr) in named_args {
393                    match params.get_mut(name) {
394                        Some(ref entry) if entry.is_some() => dup.push(ident),
395                        Some(entry) => *entry = Some(expr),
396                        None => extra.push(ident),
397                    }
398                }
399
400                let (mut missing, mut exprs) = (vec![], vec![]);
401                for (name, expr) in params {
402                    match expr {
403                        Some(expr) => exprs.push(expr),
404                        None => missing.push(name)
405                    }
406                }
407
408                if (extra.len() + dup.len() + missing.len()) == 0 {
409                    Validation::Ok(exprs)
410                } else {
411                    Validation::Named(missing, extra, dup)
412                }
413            }
414        }
415    }
416}
417
418impl RoutedUri {
419    /// The Span to use when referring to all of the arguments.
420    pub fn args_span(&self) -> Span {
421        match self.route.args.num() {
422            0 => self.route.path.span(),
423            _ => self.route.args.span()
424        }
425    }
426}
427
428impl Arg {
429    fn is_named(&self) -> bool {
430        match *self {
431            Arg::Named(..) => true,
432            _ => false
433        }
434    }
435
436    fn unnamed(&self) -> &ArgExpr {
437        match self {
438            Arg::Unnamed(expr) => expr,
439            _ => panic!("Called Arg::unnamed() on an Arg::Named!"),
440        }
441    }
442
443    fn named(&self) -> (&Name, &Ident, &ArgExpr) {
444        match self {
445            Arg::Named(name, ident, _, expr) => (name, ident, expr),
446            _ => panic!("Called Arg::named() on an Arg::Unnamed!"),
447        }
448    }
449}
450
451impl Args {
452    fn num(&self) -> usize {
453        match self {
454            Args::Named(inner) | Args::Unnamed(inner) => inner.len(),
455        }
456    }
457}
458
459impl ArgExpr {
460    pub fn as_expr(&self) -> Option<&Expr> {
461        match self {
462            ArgExpr::Expr(expr) => Some(expr),
463            _ => None
464        }
465    }
466
467    pub fn unwrap_expr(&self) -> &Expr {
468        match self {
469            ArgExpr::Expr(expr) => expr,
470            _ => panic!("Called ArgExpr::expr() on ArgExpr::Ignored!"),
471        }
472    }
473}
474
475fn uri_err<T>(lit: &StringLit, error: Error<'_>) -> parse::Result<T> {
476    let span = lit.subspan(error.index() + 1..(error.index() + 2));
477    err(span, format!("invalid URI: {}", error))
478}
479
480impl UriExpr {
481    fn parse_prefix(input: ParseStream<'_>) -> syn::Result<Option<Self>> {
482        if input.parse::<Token![_]>().is_ok() {
483            return Ok(None);
484        }
485
486        if !input.peek(LitStr) {
487            return input.parse::<Expr>().map(|e| Some(UriExpr::Expr(e)));
488        }
489
490        let lit = input.parse::<StringLit>()?;
491        let uri = Uri::parse::<Origin<'_>>(&lit)
492            .or_else(|e| Uri::parse::<Absolute<'_>>(&lit).map_err(|e2| (e, e2)))
493            .map_err(|(e1, e2)| lit.starts_with('/').then(|| e1).unwrap_or(e2))
494            .or_else(|e| uri_err(&lit, e))?;
495
496        if matches!(&uri, Uri::Origin(o) if o.query().is_some())
497            || matches!(&uri, Uri::Absolute(a) if a.query().is_some())
498        {
499            return err(lit.span(), "URI prefix cannot contain query part");
500        }
501
502        Ok(Some(UriExpr::Uri(UriLit(uri.into_owned(), lit.span()))))
503    }
504
505    fn parse_suffix(input: ParseStream<'_>) -> syn::Result<Option<Self>> {
506        if input.parse::<Token![_]>().is_ok() {
507            return Ok(None);
508        }
509
510        if !input.peek(LitStr) {
511            return input.parse::<Expr>().map(|e| Some(UriExpr::Expr(e)));
512        }
513
514        let lit = input.parse::<StringLit>()?;
515        let uri = Reference::parse(&lit).or_else(|e| uri_err(&lit, e))?;
516        if uri.scheme().is_some() || uri.authority().is_some() || !uri.path().is_empty() {
517            return err(lit.span(), "URI suffix must contain only query and/or fragment");
518        }
519
520        // This is a bit of finagling to get the types to match up how we'd
521        // like. A URI like `?foo` will parse as a `Reference`, since that's
522        // what it is. But if we left this as is, we'd convert Origins and
523        // Absolutes to References on suffix appendage when we don't need to.
524        // This is because anything + a Reference _must_ result in a Reference
525        // since the resulting URI could have a fragment. Since here we know
526        // that's not the case, we lie and say it's Absolute since an Absolute
527        // can't contain a fragment, so an Origin + Absolute suffix is still an
528        // Origin, and likewise for an Absolute.
529        let uri = match uri.fragment() {
530            None => {
531                let query = uri.query().map(|q| q.as_str());
532                Uri::Absolute(Absolute::const_new("", None, "", query))
533            }
534            Some(_) => Uri::Reference(uri)
535        };
536
537        Ok(Some(UriExpr::Uri(UriLit(uri.into_owned(), lit.span()))))
538    }
539}
540
541impl Deref for UriLit {
542    type Target = Uri<'static>;
543
544    fn deref(&self) -> &Self::Target {
545        &self.0
546    }
547}
548
549impl ToTokens for UriLit {
550    fn to_tokens(&self, t: &mut TokenStream) {
551        use crate::http_codegen::*;
552
553        let (uri, span) = (&self.0, self.1);
554        match uri {
555            Uri::Origin(o) => Origin(o, span).to_tokens(t),
556            Uri::Absolute(o) => Absolute(o, span).to_tokens(t),
557            Uri::Authority(o) => Authority(o, span).to_tokens(t),
558            Uri::Reference(r) => Reference(r, span).to_tokens(t),
559            Uri::Asterisk(a) => Asterisk(*a, span).to_tokens(t),
560        }
561    }
562}
563
564impl ToTokens for UriExpr {
565    fn to_tokens(&self, t: &mut TokenStream) {
566        match self {
567            UriExpr::Uri(uri) => uri.to_tokens(t),
568            UriExpr::Expr(e) => e.to_tokens(t),
569        }
570    }
571}
572
573impl ToTokens for ArgExpr {
574    fn to_tokens(&self, tokens: &mut TokenStream) {
575        match self {
576            ArgExpr::Expr(e) => e.to_tokens(tokens),
577            ArgExpr::Ignored(e) => e.to_tokens(tokens)
578        }
579    }
580}
581
582impl ToTokens for Arg {
583    fn to_tokens(&self, tokens: &mut TokenStream) {
584        match self {
585            Arg::Unnamed(e) => e.to_tokens(tokens),
586            Arg::Named(_, ident, eq, expr) => {
587                ident.to_tokens(tokens);
588                eq.to_tokens(tokens);
589                expr.to_tokens(tokens);
590            }
591        }
592    }
593}
594
595impl ToTokens for Args {
596    fn to_tokens(&self, tokens: &mut TokenStream) {
597        match self {
598            Args::Unnamed(e) | Args::Named(e) => e.to_tokens(tokens)
599        }
600    }
601}