use bon::Builder;
use matchit::InsertError;
use rustc_errors::DiagCtxtHandle;
use rustc_hir::{HirId, def_id::DefId};
use rustc_middle::ty::Ty as MTy;
use rustc_span::{Span, Symbol};
#[derive(Debug, Builder, Clone, Copy)]
pub struct InertiaProp<'tcx> {
pub(super) rx_id: HirId,
pub(super) axum_handler: DefId,
pub(super) id: HirId,
name: Symbol,
span: Span,
ty: MTy<'tcx>,
flash_prop: bool,
lazy: bool,
deferred: bool,
}
impl<'tcx> InertiaProp<'tcx> {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn id(&self) -> HirId {
self.id
}
pub fn lazy(&self) -> bool {
self.lazy
}
pub fn deferred(&self) -> bool {
self.deferred
}
pub fn flash_prop(&self) -> bool {
self.flash_prop
}
pub fn span(&self) -> Span {
self.span
}
pub(super) fn emit_fatal_chain_broken(self, dcx: DiagCtxtHandle) -> ! {
dcx.span_fatal(
self.span,
"prop chain broken apart so analysis cannot be done confidently",
)
}
pub(super) fn emit_err_diverged(self, dcx: DiagCtxtHandle, component: &str) {
let name = self.name();
dcx.struct_span_err(
self.span,
format!("prop `{name}` in `{component}` is not consistent across all branches"),
)
.emit();
}
pub fn ty(&self) -> MTy<'tcx> {
self.ty
}
}
#[derive(Debug, Clone, Builder)]
pub struct AxumRoute<'tcx> {
pub(super) id: DefId,
http_method: HttpMethod,
route_path: Symbol,
span: rustc_span::Span,
path_tys: Vec<MTy<'tcx>>,
json_body_ty: Option<MTy<'tcx>>,
query_ty: Option<MTy<'tcx>>,
inertia: bool,
}
impl<'tcx> AxumRoute<'tcx> {
pub fn inertia(&self) -> bool {
self.inertia
}
pub fn id(&self) -> DefId {
self.id
}
pub fn route_path_symbol(&self) -> Symbol {
self.route_path
}
pub fn path_tys(&self) -> impl Iterator<Item = MTy<'tcx>> {
self.path_tys.iter().copied()
}
pub(super) fn route_path<'a>(
&'a self,
ra: &super::RiptAnalyzer<'tcx>,
) -> impl Iterator<Item = AxumRoutePathSegment<'tcx>> + 'tcx {
let mut matchit_router = ra.matchit_router.write();
let path_str = self.route_path.as_str();
if path_str.contains("*") {
unimplemented!("wildcard routes are not currently supported");
}
match matchit_router.insert(path_str, ()) {
Ok(_) => {}
Err(InsertError::Conflict { .. })
if ra
.axum_routes
.values()
.any(|r| r.http_method == self.http_method) =>
{
ra.tcx.dcx().span_warn(self.span, "duplicate route path")
}
Err(e) => ra
.tcx
.dcx()
.span_fatal(self.span, format!("unable to parse route path: {e}")),
}
let extracted_match = matchit_router
.at(path_str)
.expect("always valid as we inserted it right above");
if extracted_match.params.is_empty() && !self.path_tys.is_empty() {
ra.tcx.dcx().span_fatal(
self.span,
"path definition has no parameters, but there is a path extractor on the route",
)
}
let mut segments = vec![];
let mut current_part_head = path_str;
for (i, (param_name, param_def_slice)) in extracted_match.params.iter().enumerate() {
let (this_head, this_tail) =
unsafe { Option::unwrap_unchecked(current_part_head.split_once(param_def_slice)) };
let ty = match self.path_tys.get(i) {
Some(ty) => ty,
None => {
ra.tcx.dcx().span_warn(
self.span,
format!("path param `{param_name}` does not have a type"),
);
continue;
}
};
segments.push(AxumRoutePathSegment::Static(Symbol::intern(this_head)));
segments.push(AxumRoutePathSegment::Dynamic(
Symbol::intern(param_name),
*ty,
));
current_part_head = this_tail;
}
segments.push(AxumRoutePathSegment::Static(Symbol::intern(
current_part_head,
)));
segments.into_iter()
}
pub fn span(&self) -> Span {
self.span
}
pub fn data_ty(&self, ra: &super::RiptAnalyzer<'tcx>) -> Option<MTy<'tcx>> {
match (self.query_ty, self.json_body_ty) {
(Some(_qt), Some(_jt)) => {
ra.tcx.dcx().span_err(
self.span,
"cannot have both query and json extractors at the same time",
);
None
}
(Some(t), None) | (None, Some(t)) => Some(t),
(None, None) => None,
}
}
pub fn http_method(&self) -> HttpMethod {
self.http_method
}
}
#[derive(Clone, Copy, Debug)]
pub enum AxumRoutePathSegment<'tcx> {
Static(Symbol),
Dynamic(Symbol, MTy<'tcx>),
}
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum HttpMethod {
Get,
Post,
Put,
Patch,
Delete,
}
impl HttpMethod {
pub fn static_str(&self) -> &'static str {
match self {
HttpMethod::Get => "get",
HttpMethod::Post => "post",
HttpMethod::Put => "put",
HttpMethod::Patch => "patch",
HttpMethod::Delete => "delete",
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum InertiaPropDestination {
Render(Symbol),
Back,
Share,
}
#[derive(Debug, Clone, Copy, Builder)]
pub struct BridgeMarkedItem<'tcx> {
pub(super) id: DefId,
pub(super) span: Span,
pub(super) kind: BridgeMarkedItemKind<'tcx>,
}
impl<'tcx> BridgeMarkedItem<'tcx> {
pub fn kind(&self) -> &BridgeMarkedItemKind<'tcx> {
&self.kind
}
pub fn span(&self) -> Span {
self.span
}
}
#[derive(Debug, Clone, Copy)]
pub enum BridgeMarkedItemKind<'tcx> {
Ty(MTy<'tcx>),
}