use itertools::Itertools;
use rustc_hir::{HirId, Impl, ItemKind, def_id::DefId};
use super::{
BridgeMarkedItem, BridgeMarkedItemKind, ExprAnalysisExt, MTyAnalysisExt,
structures::{self, AxumRoute, HttpMethod, InertiaProp, InertiaPropDestination},
};
use rustc_middle::ty::{Ty as MTy, TyKind as MTyKind};
pub fn try_bridge_marked_item<'tcx>(
ra: &super::RiptAnalyzer<'tcx>,
item: &rustc_hir::Item<'tcx>,
) -> Option<structures::BridgeMarkedItem<'tcx>> {
match item.kind {
ItemKind::Impl(
ipl @ Impl {
of_trait: Some(trait_ref),
..
},
) => {
let path_res = trait_ref.path.res;
let dp = ra.tcx.def_path(path_res.def_id());
ra.def_path_and(&dp, "riptc_bridge::Riptc", Some(()))?;
let ty = ipl.self_ty;
let span = ty.span;
let ty = ra.tcx.type_of(ty.hir_id.owner);
Some(
BridgeMarkedItem::builder()
.id(ipl.self_ty.hir_id.owner.to_def_id())
.kind(BridgeMarkedItemKind::Ty(ty.skip_binder()))
.span(span)
.build(),
)
}
_ => None,
}
}
pub fn try_axum_share_middleware_registration<'tcx>(
ra: &super::RiptAnalyzer<'tcx>,
ex: rustc_hir::Expr<'tcx>,
) -> Option<DefId> {
let (_rx, mut args, _span) = ra.try_method_call(ex)?;
ra.try_assert_type_dependent_def_path(ex.hir_id, "axum::routing::{impl#3}::route_layer")?;
let map_response_expr = args.next()?;
let (dp, middleware_arg) = map_response_expr.extract_call_with_single_arg(ra)?;
let _ = ra.def_path_and(&dp, "axum::middleware::map_response::map_response", || ())?;
let middleware_id = middleware_arg.extract_qpath_did(ra)?;
Some(middleware_id)
}
pub fn try_axum_route_from_route_method_call<'tcx>(
ra: &super::RiptAnalyzer<'tcx>,
ex: rustc_hir::Expr<'tcx>,
) -> Option<structures::AxumRoute<'tcx>> {
let (_rx, mut args, span) = ra.try_method_call(ex)?;
ra.try_assert_type_dependent_def_path(ex.hir_id, "axum::routing::{impl#3}::route")?;
let route_path = args.next()?.extract_lit_str()?;
let (http_method_path, http_method_arg) = args.next()?.extract_call_with_single_arg(ra)?;
let http_method = ra
.def_path_and(
&http_method_path,
"axum::routing::method_routing::get",
HttpMethod::Get,
)
.or(ra.def_path_and(
&http_method_path,
"axum::routing::method_routing::post",
HttpMethod::Post,
))
.or(ra.def_path_and(
&http_method_path,
"axum::routing::method_routing::put",
HttpMethod::Put,
))
.or(ra.def_path_and(
&http_method_path,
"axum::routing::method_routing::patch",
HttpMethod::Patch,
))
.or(ra.def_path_and(
&http_method_path,
"axum::routing::method_routing::delete",
HttpMethod::Delete,
))?;
let route_handler_path = http_method_arg.extract_qpath_did(ra)?;
let route_handler_params = ra.fn_params(route_handler_path).collect_vec();
let is_inertia_route = route_handler_params
.iter()
.filter_map(|p| p.extract_adt())
.any(|(adt, _)| {
ra.get_def_path_and(adt.did(), "ript::inertia::Inertia", || Some(true))
.unwrap_or(false)
});
let path_tys = route_handler_params
.iter()
.filter_map(|p| p.extract_adt())
.filter_map(|(adt, mut genargs)| {
ra.get_def_path_and(adt.did(), "axum::extract::path::Path", || {
Some(genargs.next()?.expect_ty().peel_tuple())
})
})
.flatten()
.collect_vec();
let json_body_ty = route_handler_params
.iter()
.filter_map(|p| p.extract_adt())
.find_map(|(adt, mut genargs)| {
ra.get_def_path_and(adt.did(), "axum::json::Json", || {
genargs.next().map(|a| a.expect_ty())
})
});
let query_ty = route_handler_params
.iter()
.filter_map(|p| p.extract_adt())
.find_map(|(adt, mut genargs)| {
ra.get_def_path_and(adt.did(), "axum::extract::query::Query", || {
genargs.next().map(|a| a.expect_ty())
})
.or_else(|| {
ra.get_def_path_and(adt.did(), "axum_extra::extract::query::Query", || {
genargs.next().map(|a| a.expect_ty())
})
})
});
Some(
AxumRoute::builder()
.id(route_handler_path)
.http_method(http_method)
.span(span)
.route_path(route_path)
.path_tys(path_tys)
.maybe_json_body_ty(json_body_ty)
.maybe_query_ty(query_ty)
.inertia(is_inertia_route)
.build(),
)
}
pub fn try_inertia_prop_from_chain<'tcx>(
ra: &super::RiptAnalyzer<'tcx>,
ex: rustc_hir::Expr<'tcx>,
) -> Option<structures::InertiaProp<'tcx>> {
let (rx, mut args, span) = ra.try_method_call(ex)?;
ra.try_assert_type_dependent_def_path(ex.hir_id, "ript::inertia::{impl#4}::prop")?;
let prop_name = args.next()?.extract_lit_str()?;
let prop_value_closure = args.next()?;
let lazy = prop_value_closure
.extract_call_with_single_arg(ra)
.and_then(|(path, _call)| ra.def_path_and(&path, "ript::prop::lazy", true))
.unwrap_or(false);
let deferred = prop_value_closure
.extract_call_with_single_arg(ra)
.and_then(|(path, _call)| ra.def_path_and(&path, "ript::prop::defer", true))
.unwrap_or(false);
let prop_value_closure_ty = ra.typeof_expr(prop_value_closure);
let ty = resolve_final_prop_ty(ra, prop_value_closure_ty);
Some(
InertiaProp::builder()
.ty(ty)
.span(span)
.axum_handler(ex.hir_id.owner.to_def_id())
.name(prop_name)
.rx_id(rx.hir_id)
.id(ex.hir_id)
.lazy(lazy)
.deferred(deferred)
.flash_prop(false)
.build(),
)
}
pub fn try_inertia_flash_prop_from_chain<'tcx>(
ra: &super::RiptAnalyzer<'tcx>,
ex: rustc_hir::Expr<'tcx>,
) -> Option<structures::InertiaProp<'tcx>> {
let (rx, mut args, span) = ra.try_method_call(ex)?;
ra.try_assert_type_dependent_def_path(ex.hir_id, "ript::inertia::{impl#4}::flash_prop")?;
let prop_name = args.next()?.extract_lit_str()?;
let prop_value = args.next()?;
let ty = ra.typeof_expr(prop_value);
Some(
InertiaProp::builder()
.ty(ty)
.span(span)
.axum_handler(ex.hir_id.owner.to_def_id())
.name(prop_name)
.rx_id(rx.hir_id)
.id(ex.hir_id)
.flash_prop(true)
.lazy(false)
.deferred(false)
.build(),
)
}
fn resolve_final_prop_ty<'tcx>(
ra: &super::RiptAnalyzer<'tcx>,
prop_second_arg: MTy<'tcx>,
) -> MTy<'tcx> {
let closure_genargs = match prop_second_arg.kind() {
MTyKind::Adt(_adt_def, genargs) => match genargs.type_at(0).kind() {
MTyKind::CoroutineClosure(_def_id, genargs) => genargs,
_ => unreachable!("code would not compile prior to this point"),
},
MTyKind::CoroutineClosure(_def_id, genargs) => genargs,
_ => unreachable!("code would not compile prior to this point"),
};
let closure_return_ty_future = closure_genargs
.type_at(1)
.fn_sig(ra.tcx)
.output()
.skip_binder();
let mut closure_return_ty_future_walker = closure_return_ty_future.walk();
let closure_return_ty_future_output_ty = closure_return_ty_future_walker
.nth(2)
.map(|output| output.expect_ty())
.expect("code would not compile prior to this point");
closure_return_ty_future_output_ty
}
pub fn try_inertia_prop_dest_chain_definer<'tcx>(
ra: &super::RiptAnalyzer<'tcx>,
ex: rustc_hir::Expr<'tcx>,
) -> Option<(HirId, structures::InertiaPropDestination)> {
let (ex, _args, _span) = ra.try_method_call(ex)?;
ra.try_assert_type_dependent_def_path(ex.hir_id, "ript::inertia::{impl#1}::back")
.map(|_| InertiaPropDestination::Back)
.or(ra
.try_assert_type_dependent_def_path(ex.hir_id, "ript::inertia::{impl#1}::share")
.map(|_| InertiaPropDestination::Share))
.or(ra
.try_assert_type_dependent_def_path(ex.hir_id, "ript::inertia::{impl#1}::render")
.and_then(|_| {
let (_ex, mut args, _span) = ra.try_method_call(ex)?;
Some(InertiaPropDestination::Render({
args.next()?.extract_lit_str()?
}))
}))
.map(|dest| (ex.hir_id, dest))
}