use std::ops::Deref;
use indexmap::IndexMap;
use devise::{Spanned, ext::TypeExt};
use quote::{ToTokens, TokenStreamExt};
use syn::{Expr, Ident, LitStr, Path, Token, Type};
use syn::parse::{self, Parse, ParseStream, Parser};
use syn::punctuated::Punctuated;
use proc_macro2::{TokenStream, TokenTree, Span};
use rocket_http::uri::{Error, Reference};
use crate::http::uri::{Uri, Origin, Absolute, fmt};
use crate::http::ext::IntoOwned;
use crate::proc_macro_ext::StringLit;
use crate::attribute::param::{Parameter, Dynamic};
use crate::name::Name;
#[derive(Debug)]
pub enum ArgExpr {
Expr(Expr),
Ignored(Token![_]),
}
#[derive(Debug)]
pub enum Arg {
Unnamed(ArgExpr),
Named(Name, Ident, Token![=], ArgExpr),
}
#[derive(Debug)]
pub enum Args {
Unnamed(Punctuated<Arg, Token![,]>),
Named(Punctuated<Arg, Token![,]>),
}
#[derive(Debug)]
pub struct UriLit(Uri<'static>, Span);
#[derive(Debug)]
pub enum UriExpr {
Uri(UriLit),
Expr(Expr),
}
#[derive(Debug)]
pub struct RouteInvocation {
pub path: Path,
pub args: Args,
}
#[derive(Debug)]
pub struct RoutedUri {
pub prefix: Option<UriExpr>,
pub route: RouteInvocation,
pub suffix: Option<UriExpr>,
}
#[derive(Debug)]
pub enum UriMacro {
Literal(UriLit),
Routed(RoutedUri),
}
#[derive(Debug)]
pub enum Validation<'a> {
NamedIgnored(Vec<&'a Dynamic>),
Unnamed(usize, usize),
Named(Vec<&'a Name>, Vec<&'a Ident>, Vec<&'a Ident>),
Ok(Vec<&'a ArgExpr>)
}
#[derive(Debug)]
pub struct InternalUriParams {
pub route_uri: Origin<'static>,
pub path_params: Vec<Parameter>,
pub query_params: Vec<Parameter>,
pub fn_args: Vec<FnArg>,
pub uri_mac: RoutedUri,
}
#[derive(Debug)]
pub struct FnArg {
pub ident: Ident,
pub ty: Type,
}
fn err<T, S: AsRef<str>>(span: Span, s: S) -> parse::Result<T> {
Err(parse::Error::new(span, s.as_ref()))
}
impl Parse for ArgExpr {
fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
if input.peek(Token![_]) {
return Ok(ArgExpr::Ignored(input.parse::<Token![_]>()?));
}
input.parse::<Expr>().map(ArgExpr::Expr)
}
}
impl Parse for Arg {
fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
let has_key = input.peek2(Token![=]);
if has_key {
let ident = input.parse::<Ident>()?;
let eq_token = input.parse::<Token![=]>()?;
let expr = input.parse::<ArgExpr>()?;
Ok(Arg::Named(Name::from(&ident), ident, eq_token, expr))
} else {
let expr = input.parse::<ArgExpr>()?;
Ok(Arg::Unnamed(expr))
}
}
}
impl Parse for Args {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
if input.cursor().eof() {
return Ok(Args::Unnamed(Punctuated::new()));
}
let args = input.parse_terminated(Arg::parse, Token![,])?;
let mut first_is_named = None;
for arg in &args {
if let Some(first_is_named) = first_is_named {
if first_is_named != arg.is_named() {
return err(args.span(), "named and unnamed parameters cannot be mixed");
}
} else {
first_is_named = Some(arg.is_named());
}
}
match first_is_named {
Some(true) => Ok(Args::Named(args)),
_ => Ok(Args::Unnamed(args))
}
}
}
impl Parse for RouteInvocation {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let path = input.parse()?;
let args = if input.peek(syn::token::Paren) {
let args;
syn::parenthesized!(args in input);
args.parse()?
} else {
Args::Unnamed(Punctuated::new())
};
Ok(RouteInvocation { path, args })
}
}
impl Parse for UriLit {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let string = input.parse::<StringLit>()?;
let uri = match Uri::parse_any(&string) {
Ok(uri) => uri.into_owned(),
Err(e) => {
let span = string.subspan(e.index() + 1..(e.index() + 2));
return err(span, format!("invalid URI: {}", e));
}
};
Ok(UriLit(uri, string.span()))
}
}
impl UriMacro {
fn unary(input: ParseStream<'_>) -> parse::Result<Self> {
if input.peek(LitStr) {
Ok(UriMacro::Literal(input.parse()?))
} else {
Ok(UriMacro::Routed(RoutedUri {
prefix: None,
route: input.parse()?,
suffix: None,
}))
}
}
fn binary(prefix: TokenStream, middle: TokenStream) -> parse::Result<Self> {
Ok(UriMacro::Routed(RoutedUri {
prefix: UriExpr::parse_prefix.parse2(prefix)?,
route: syn::parse2(middle)?,
suffix: None,
}))
}
fn ternary(prefix: TokenStream, mid: TokenStream, suffix: TokenStream) -> parse::Result<Self> {
Ok(UriMacro::Routed(RoutedUri {
prefix: UriExpr::parse_prefix.parse2(prefix)?,
route: syn::parse2(mid)?,
suffix: UriExpr::parse_suffix.parse2(suffix)?
}))
}
}
impl Parse for UriMacro {
fn parse(input: ParseStream<'_>) -> parse::Result<Self> {
use syn::buffer::Cursor;
use parse::{StepCursor, Result};
fn stream<'c>(cursor: StepCursor<'c, '_>) -> Result<(Option<TokenStream>, Cursor<'c>)> {
let mut stream = TokenStream::new();
let mut cursor = *cursor;
while let Some((tt, next)) = cursor.token_tree() {
cursor = next;
match tt {
TokenTree::Punct(p) if p.as_char() == ',' => break,
_ => stream.append(tt)
}
}
stream.is_empty()
.then(|| Ok((None, cursor)))
.unwrap_or(Ok((Some(stream), cursor)))
}
let mut args = vec![];
while let Some(tokens) = input.step(stream)? {
args.push(tokens);
}
let (arg_count, mut iter) = (args.len(), args.into_iter());
let mut next = || iter.next().unwrap();
match arg_count {
0 => err(Span::call_site(), "expected at least 1 argument, found none"),
1 => UriMacro::unary.parse2(next()),
2 => UriMacro::binary(next(), next()),
3 => UriMacro::ternary(next(), next(), next()),
n => err(iter.nth(3).unwrap().span(),
format!("expected 1, 2, or 3 arguments, found {}", n))
}
}
}
impl Parse for RoutedUri {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
match UriMacro::parse(input)? {
UriMacro::Routed(route) => Ok(route),
UriMacro::Literal(uri) => err(uri.span(), "expected route URI, found literal")
}
}
}
impl Parse for FnArg {
fn parse(input: ParseStream<'_>) -> parse::Result<FnArg> {
let ident = input.parse::<Ident>()?;
input.parse::<Token![:]>()?;
let mut ty = input.parse::<Type>()?;
ty.strip_lifetimes();
Ok(FnArg { ident, ty })
}
}
impl Parse for InternalUriParams {
fn parse(input: ParseStream<'_>) -> parse::Result<InternalUriParams> {
let route_uri_str = input.parse::<StringLit>()?;
input.parse::<Token![,]>()?;
let route_uri = Origin::parse_route(&route_uri_str)
.map(|o| o.into_normalized().into_owned())
.map_err(|_| input.error("internal error: invalid route URI"))?;
let content;
syn::parenthesized!(content in input);
let fn_args = content.parse_terminated(FnArg::parse, Token![,])?;
let fn_args = fn_args.into_iter().collect();
input.parse::<Token![,]>()?;
let uri_mac = input.parse::<RoutedUri>()?;
let span = route_uri_str.subspan(1..route_uri.path().len() + 1);
let path_params = Parameter::parse_many::<fmt::Path>(route_uri.path().as_str(), span)
.map(|p| p.expect("internal error: invalid path parameter"))
.collect::<Vec<_>>();
let query = route_uri.query();
let query_params = query.map(|query| {
let i = route_uri.path().len() + 2;
let span = route_uri_str.subspan(i..(i + query.len()));
Parameter::parse_many::<fmt::Query>(query.as_str(), span)
.map(|p| p.expect("internal error: invalid query parameter"))
.collect::<Vec<_>>()
}).unwrap_or_default();
Ok(InternalUriParams { route_uri, path_params, query_params, fn_args, uri_mac })
}
}
impl InternalUriParams {
pub fn fn_args_str(&self) -> String {
self.fn_args.iter()
.map(|FnArg { ident, ty }| {
let ty = ty.with_stripped_lifetimes();
let ty_str = quote!(#ty).to_string();
let ty_str: String = ty_str.chars().filter(|c| !c.is_whitespace()).collect();
format!("{}: {}", ident, ty_str)
})
.collect::<Vec<_>>()
.join(", ")
}
pub fn dynamic_path_params(&self) -> impl Iterator<Item = &Dynamic> + Clone {
self.path_params.iter()
.filter_map(|p| p.dynamic().or_else(|| p.ignored()))
}
pub fn dynamic_query_params(&self) -> impl Iterator<Item = &Dynamic> + Clone {
self.query_params.iter().filter_map(|p| p.dynamic())
}
pub fn validate(&self) -> Validation<'_> {
let args = &self.uri_mac.route.args;
let all_params = self.dynamic_path_params().chain(self.dynamic_query_params());
match args {
Args::Unnamed(args) => {
let (expected, actual) = (all_params.count(), args.len());
let unnamed_args = args.iter().map(|arg| arg.unnamed());
match expected == actual {
true => Validation::Ok(unnamed_args.collect()),
false => Validation::Unnamed(expected, actual)
}
},
Args::Named(args) => {
let ignored = all_params.clone().filter(|p| p.is_wild());
if ignored.clone().count() > 0 {
return Validation::NamedIgnored(ignored.collect());
}
let mut params = all_params.map(|p| (&p.name, None))
.collect::<IndexMap<&Name, Option<&ArgExpr>>>();
let (mut extra, mut dup) = (vec![], vec![]);
let named_args = args.iter().map(|arg| arg.named());
for (name, ident, expr) in named_args {
match params.get_mut(name) {
Some(ref entry) if entry.is_some() => dup.push(ident),
Some(entry) => *entry = Some(expr),
None => extra.push(ident),
}
}
let (mut missing, mut exprs) = (vec![], vec![]);
for (name, expr) in params {
match expr {
Some(expr) => exprs.push(expr),
None => missing.push(name)
}
}
if (extra.len() + dup.len() + missing.len()) == 0 {
Validation::Ok(exprs)
} else {
Validation::Named(missing, extra, dup)
}
}
}
}
}
impl RoutedUri {
pub fn args_span(&self) -> Span {
match self.route.args.num() {
0 => self.route.path.span(),
_ => self.route.args.span()
}
}
}
impl Arg {
fn is_named(&self) -> bool {
match *self {
Arg::Named(..) => true,
_ => false
}
}
fn unnamed(&self) -> &ArgExpr {
match self {
Arg::Unnamed(expr) => expr,
_ => panic!("Called Arg::unnamed() on an Arg::named!"),
}
}
fn named(&self) -> (&Name, &Ident, &ArgExpr) {
match self {
Arg::Named(name, ident, _, expr) => (name, ident, expr),
_ => panic!("Called Arg::named() on an Arg::Unnamed!"),
}
}
}
impl Args {
fn num(&self) -> usize {
match self {
Args::Named(inner) | Args::Unnamed(inner) => inner.len(),
}
}
}
impl ArgExpr {
pub fn as_expr(&self) -> Option<&Expr> {
match self {
ArgExpr::Expr(expr) => Some(expr),
_ => None
}
}
pub fn unwrap_expr(&self) -> &Expr {
match self {
ArgExpr::Expr(expr) => expr,
_ => panic!("Called ArgExpr::expr() on ArgExpr::Ignored!"),
}
}
}
fn uri_err<T>(lit: &StringLit, error: Error<'_>) -> parse::Result<T> {
let span = lit.subspan(error.index() + 1..(error.index() + 2));
err(span, format!("invalid URI: {}", error))
}
impl UriExpr {
fn parse_prefix(input: ParseStream<'_>) -> syn::Result<Option<Self>> {
if input.parse::<Token![_]>().is_ok() {
return Ok(None);
}
if !input.peek(LitStr) {
return input.parse::<Expr>().map(|e| Some(UriExpr::Expr(e)));
}
let lit = input.parse::<StringLit>()?;
let uri = Uri::parse::<Origin<'_>>(&lit)
.or_else(|e| Uri::parse::<Absolute<'_>>(&lit).map_err(|e2| (e, e2)))
.map_err(|(e1, e2)| lit.starts_with('/').then(|| e1).unwrap_or(e2))
.or_else(|e| uri_err(&lit, e))?;
if matches!(&uri, Uri::Origin(o) if o.query().is_some())
|| matches!(&uri, Uri::Absolute(a) if a.query().is_some())
{
return err(lit.span(), "URI prefix cannot contain query part");
}
Ok(Some(UriExpr::Uri(UriLit(uri.into_owned(), lit.span()))))
}
fn parse_suffix(input: ParseStream<'_>) -> syn::Result<Option<Self>> {
if input.parse::<Token![_]>().is_ok() {
return Ok(None);
}
if !input.peek(LitStr) {
return input.parse::<Expr>().map(|e| Some(UriExpr::Expr(e)));
}
let lit = input.parse::<StringLit>()?;
let uri = Reference::parse(&lit).or_else(|e| uri_err(&lit, e))?;
if uri.scheme().is_some() || uri.authority().is_some() || !uri.path().is_empty() {
return err(lit.span(), "URI suffix must contain only query and/or fragment");
}
let uri = match uri.fragment() {
None => {
let query = uri.query().map(|q| q.as_str());
Uri::Absolute(Absolute::const_new("", None, "", query))
}
Some(_) => Uri::Reference(uri)
};
Ok(Some(UriExpr::Uri(UriLit(uri.into_owned(), lit.span()))))
}
}
impl Deref for UriLit {
type Target = Uri<'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl ToTokens for UriLit {
fn to_tokens(&self, t: &mut TokenStream) {
use crate::http_codegen::*;
let (uri, span) = (&self.0, self.1);
match uri {
Uri::Origin(o) => Origin(o, span).to_tokens(t),
Uri::Absolute(o) => Absolute(o, span).to_tokens(t),
Uri::Authority(o) => Authority(o, span).to_tokens(t),
Uri::Reference(r) => Reference(r, span).to_tokens(t),
Uri::Asterisk(a) => Asterisk(*a, span).to_tokens(t),
}
}
}
impl ToTokens for UriExpr {
fn to_tokens(&self, t: &mut TokenStream) {
match self {
UriExpr::Uri(uri) => uri.to_tokens(t),
UriExpr::Expr(e) => e.to_tokens(t),
}
}
}
impl ToTokens for ArgExpr {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
ArgExpr::Expr(e) => e.to_tokens(tokens),
ArgExpr::Ignored(e) => e.to_tokens(tokens)
}
}
}
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) => {
ident.to_tokens(tokens);
eq.to_tokens(tokens);
expr.to_tokens(tokens);
}
}
}
}
impl ToTokens for Args {
fn to_tokens(&self, tokens: &mut TokenStream) {
match self {
Args::Unnamed(e) | Args::Named(e) => e.to_tokens(tokens)
}
}
}