use std::borrow::Cow;
use regex::Captures;
use syn::{punctuated::Punctuated, token::Comma};
use crate::component::{TypeTree, ValueType};
use super::{
fn_arg::{self, FnArg, FnArgType},
ArgValue, ArgumentResolver, MacroArg, MacroPath, PathOperations, PathResolver, ValueArgument,
};
impl ArgumentResolver for PathOperations {
fn resolve_arguments(
args: &'_ Punctuated<syn::FnArg, Comma>,
macro_args: Option<Vec<super::MacroArg>>,
_: String,
) -> (
Option<Vec<super::ValueArgument<'_>>>,
Option<Vec<super::IntoParamsType<'_>>>,
Option<super::RequestBody<'_>>,
) {
let (into_params_args, value_args): (Vec<FnArg>, Vec<FnArg>) =
fn_arg::get_fn_args(args).partition(fn_arg::is_into_params);
let (value_args, body) = split_value_args_and_request_body(value_args);
(
Some(
value_args
.zip(macro_args.unwrap_or_default().into_iter())
.map(|(value_arg, macro_arg)| ValueArgument {
name: match macro_arg {
MacroArg::Path(path) => Some(Cow::Owned(path.name)),
},
argument_in: value_arg.argument_in,
type_tree: value_arg.type_tree,
})
.collect(),
),
Some(
into_params_args
.into_iter()
.flat_map(fn_arg::with_parameter_in)
.map(Into::into)
.collect(),
),
body.into_iter().next().map(Into::into),
)
}
}
fn split_value_args_and_request_body(
value_args: Vec<FnArg>,
) -> (
impl Iterator<Item = super::ValueArgument<'_>>,
impl Iterator<Item = TypeTree<'_>>,
) {
let (path_args, body_types): (Vec<FnArg>, Vec<FnArg>) = value_args
.into_iter()
.filter(|arg| {
arg.ty.is("Path") || arg.ty.is("Json") || arg.ty.is("Form") || arg.ty.is("Bytes")
})
.partition(|arg| arg.ty.is("Path"));
(
path_args
.into_iter()
.filter(|arg| arg.ty.is("Path"))
.flat_map(|path_arg| {
match (
path_arg.arg_type,
path_arg.ty.children.expect("Path must have children"),
) {
(FnArgType::Single(name), path_children) => path_children
.into_iter()
.flat_map(|ty| match ty.value_type {
ValueType::Tuple => ty
.children
.expect("ValueType::Tuple will always have children")
.into_iter()
.map(|ty| to_value_argument(None, ty))
.collect(),
ValueType::Primitive => {
vec![to_value_argument(Some(Cow::Owned(name.to_string())), ty)]
}
ValueType::Object | ValueType::Value => unreachable!("Cannot get here"),
})
.collect::<Vec<_>>(),
(FnArgType::Destructed(tuple), path_children) => tuple
.iter()
.zip(path_children.into_iter().flat_map(|child| {
child
.children
.expect("ValueType::Tuple will always have children")
}))
.map(|(name, ty)| to_value_argument(Some(Cow::Owned(name.to_string())), ty))
.collect::<Vec<_>>(),
}
}),
body_types.into_iter().map(|body| body.ty),
)
}
fn to_value_argument<'a>(name: Option<Cow<'a, str>>, ty: TypeTree<'a>) -> ValueArgument<'a> {
ValueArgument {
name,
type_tree: Some(ty),
argument_in: super::ArgumentIn::Path,
}
}
impl PathResolver for PathOperations {
fn resolve_path(path: &Option<String>) -> Option<MacroPath> {
path.as_ref().map(|path| {
let regex = regex::Regex::new(r"\{[a-zA-Z0-9][^{}]*}").unwrap();
let mut args = Vec::<MacroArg>::with_capacity(regex.find_iter(path).count());
MacroPath {
path: regex
.replace_all(path, |captures: &Captures| {
let capture = &captures[0];
let original_name = String::from(capture);
args.push(MacroArg::Path(ArgValue {
name: String::from(&capture[1..capture.len() - 1]),
original_name,
}));
capture.to_string()
})
.to_string(),
args,
}
})
}
}