use crate::crate_paths::{get_reinhardt_crate, get_reinhardt_di_crate};
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::{FnArg, ItemFn, Pat, PatType, Result};
const MAX_URL_PARAMS: usize = 5;
fn gen_resolver_callback_arms(
struct_ident: &proc_macro2::Ident,
resolve_call: &TokenStream,
name_prefix: &TokenStream,
use_clause: &TokenStream,
) -> TokenStream {
let arms: Vec<TokenStream> = (0..=MAX_URL_PARAMS)
.map(|n| {
let param_matchers: Vec<TokenStream> = (1..=n)
.map(|i| {
let p = format_ident!("p{}", i);
quote! { $#p:literal }
})
.collect();
let fn_params: Vec<TokenStream> = (1..=n)
.map(|i| {
let p = format_ident!("p{}", i);
quote! { #p: &str }
})
.collect();
let pairs: Vec<TokenStream> = (1..=n)
.map(|i| {
let p = format_ident!("p{}", i);
quote! { ($#p, #p) }
})
.collect();
quote! {
($app_label:ident, $method:ident, $route_name:literal, #(#param_matchers),*) => {
impl #struct_ident<'_> {
pub fn $method(&self #(, #fn_params)*) -> String {
#use_clause
self.resolver.#resolve_call(
concat!(#name_prefix, ":", $route_name),
&[#(#pairs),*],
)
}
}
};
}
})
.collect();
quote! { #(#arms)* }
}
fn is_inject_attr(attr: &syn::Attribute) -> bool {
attr.path().is_ident("inject")
}
pub(crate) fn extract_depends_inner_type(ty: &syn::Type) -> Option<&syn::Type> {
if let syn::Type::Path(type_path) = ty {
let last_segment = type_path.path.segments.last()?;
if last_segment.ident == "Depends"
&& let syn::PathArguments::AngleBracketed(args) = &last_segment.arguments
&& args.args.len() == 1
&& let syn::GenericArgument::Type(inner) = args.args.first()?
{
return Some(inner);
}
}
None
}
pub(crate) fn routes_impl(args: TokenStream, input: ItemFn) -> Result<TokenStream> {
let mut standalone = false;
let mut client_inventory = false;
let mut client_inventory_ident: Option<syn::Ident> = None;
let mut server_only_seen = false;
let mut server_only_ident: Option<syn::Ident> = None;
let mut no_client_resolvers_seen = false;
let mut no_client_resolvers_ident: Option<syn::Ident> = None;
let mut no_ws_resolvers_seen = false;
let mut no_ws_resolvers_ident: Option<syn::Ident> = None;
if !args.is_empty() {
let parser = syn::punctuated::Punctuated::<syn::Ident, syn::Token![,]>::parse_terminated;
let parsed = syn::parse::Parser::parse2(parser, args).map_err(|e| {
syn::Error::new(
e.span(),
"invalid arguments for #[routes]; expected comma-separated flags from \
`standalone`, `client_inventory`, `server_only`, \
`no_client_resolvers`, `no_ws_resolvers`",
)
})?;
for ident in parsed {
if ident == "standalone" {
if standalone {
return Err(syn::Error::new_spanned(
ident,
"`standalone` specified twice",
));
}
standalone = true;
} else if ident == "client_inventory" {
if client_inventory {
return Err(syn::Error::new_spanned(
ident,
"`client_inventory` specified twice",
));
}
client_inventory = true;
client_inventory_ident = Some(ident);
} else if ident == "server_only" {
if server_only_seen {
return Err(syn::Error::new_spanned(
ident,
"`server_only` specified twice",
));
}
server_only_seen = true;
server_only_ident = Some(ident);
} else if ident == "no_client_resolvers" {
if no_client_resolvers_seen {
return Err(syn::Error::new_spanned(
ident,
"`no_client_resolvers` specified twice",
));
}
no_client_resolvers_seen = true;
no_client_resolvers_ident = Some(ident);
} else if ident == "no_ws_resolvers" {
if no_ws_resolvers_seen {
return Err(syn::Error::new_spanned(
ident,
"`no_ws_resolvers` specified twice",
));
}
no_ws_resolvers_seen = true;
no_ws_resolvers_ident = Some(ident);
} else {
return Err(syn::Error::new_spanned(
ident,
"unknown argument for #[routes]; expected `standalone`, \
`client_inventory`, `server_only`, `no_client_resolvers`, \
`no_ws_resolvers`, or no arguments",
));
}
}
}
let no_client_resolvers = server_only_seen || no_client_resolvers_seen;
let no_ws_resolvers = server_only_seen || no_ws_resolvers_seen;
if client_inventory && (no_client_resolvers || no_ws_resolvers) {
let err_msg = "`#[routes(client_inventory)]` cannot be combined with `server_only`, \
`no_client_resolvers`, or `no_ws_resolvers` — `client_inventory` \
registers the WASM `ClientRouter` surface that the suppression flags \
disable. Drop one of the flags.";
if let Some(ident) = client_inventory_ident {
return Err(syn::Error::new_spanned(ident, err_msg));
}
if let Some(ident) = server_only_ident {
return Err(syn::Error::new_spanned(ident, err_msg));
}
if let Some(ident) = no_client_resolvers_ident {
return Err(syn::Error::new_spanned(ident, err_msg));
}
if let Some(ident) = no_ws_resolvers_ident {
return Err(syn::Error::new_spanned(ident, err_msg));
}
return Err(syn::Error::new_spanned(&input.sig, err_msg));
}
let reinhardt = get_reinhardt_crate();
let fn_name = &input.sig.ident;
let fn_vis = &input.vis;
let fn_attrs = &input.attrs;
let fn_block = &input.block;
if matches!(input.sig.output, syn::ReturnType::Default) {
return Err(syn::Error::new_spanned(
&input.sig,
"#[routes] function must have a return type (-> UnifiedRouter)",
));
}
let is_async = input.sig.asyncness.is_some();
if is_async && client_inventory {
return Err(syn::Error::new_spanned(
&input.sig,
"`#[routes(client_inventory)]` is not supported on async `routes()` \
functions. The WASM `ClientRouterRegistration::submit!` factory must \
be a sync `fn() -> Arc<ClientRouter>`, but the annotated function is \
async. Either make `routes()` sync, or drop `client_inventory` and \
keep the async server-only behavior. Refs #4453.",
));
}
let mut inject_params = Vec::new();
let mut has_inject = false;
for arg in &input.sig.inputs {
if let FnArg::Typed(PatType { attrs, pat, ty, .. }) = arg
&& attrs.iter().any(is_inject_attr)
{
has_inject = true;
inject_params.push((pat.clone(), ty.clone()));
}
}
if !is_async && has_inject {
return Err(syn::Error::new_spanned(
&input.sig,
"Sync #[routes] functions cannot use #[inject] parameters. \
Make the function async to use dependency injection.",
));
}
let native_only = quote! {
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
};
let wasm_only = quote! {
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
};
let user_fn_and_marker_gate = if client_inventory {
quote! {}
} else {
quote! { #native_only }
};
let wasm_client_submit_block = if client_inventory {
quote! {
#wasm_only
#[allow(unsafe_attr_outside_unsafe)]
const _: () = {
fn __get_client_router() -> ::std::sync::Arc<#reinhardt::ClientRouter> {
::std::sync::Arc::new(#fn_name().into_client())
}
#reinhardt::inventory::submit! {
#reinhardt::ClientRouterRegistration::__macro_new(__get_client_router)
}
};
}
} else {
quote! {}
};
let expanded = if !is_async {
let fn_sig = &input.sig;
quote! {
#user_fn_and_marker_gate
#[allow(private_interfaces)]
#(#fn_attrs)*
#fn_vis #fn_sig #fn_block
#native_only
#[allow(unsafe_attr_outside_unsafe)]
const _: () = {
fn __get_server_router() -> ::std::sync::Arc<#reinhardt::ServerRouter> {
let unified = #fn_name();
::std::sync::Arc::new(unified.into_server())
}
#reinhardt::inventory::submit! {
#reinhardt::UrlPatternsRegistration::__macro_new(__get_server_router)
}
};
#wasm_client_submit_block
#user_fn_and_marker_gate
#[doc(hidden)]
#[unsafe(no_mangle)]
#[allow(non_upper_case_globals, dead_code)]
static __reinhardt_routes_registration_marker: () = ();
}
} else if !has_inject {
let fn_sig = &input.sig;
quote! {
#user_fn_and_marker_gate
#[allow(private_interfaces)]
#(#fn_attrs)*
#fn_vis #fn_sig #fn_block
#native_only
#[allow(unsafe_attr_outside_unsafe)]
const _: () = {
fn __get_server_router() -> ::std::pin::Pin<
::std::boxed::Box<
dyn ::std::future::Future<
Output = ::std::result::Result<
::std::sync::Arc<#reinhardt::ServerRouter>,
::std::boxed::Box<dyn ::std::error::Error + Send + Sync>,
>,
> + Send,
>,
> {
::std::boxed::Box::pin(async {
let unified = #fn_name().await;
::std::result::Result::Ok(::std::sync::Arc::new(unified.into_server()))
})
}
#reinhardt::inventory::submit! {
#reinhardt::UrlPatternsRegistration::__macro_new_async(__get_server_router)
}
};
#user_fn_and_marker_gate
#[doc(hidden)]
#[unsafe(no_mangle)]
#[allow(non_upper_case_globals, dead_code)]
static __reinhardt_routes_registration_marker: () = ();
}
} else {
let di_crate = get_reinhardt_di_crate();
let inject_resolutions: Vec<_> = inject_params
.iter()
.map(|(pat, ty)| {
if let Some(inner_ty) = extract_depends_inner_type(ty) {
quote! {
let #pat: #ty = #di_crate::Depends::<#inner_ty>::resolve_from_registry(&*__ctx, true).await
.map_err(|e| -> ::std::boxed::Box<dyn ::std::error::Error + Send + Sync> {
::std::boxed::Box::new(e)
})?;
}
} else {
quote! {
let #pat: #ty = {
let __arc = __ctx.resolve::<#ty>().await
.map_err(|e| -> ::std::boxed::Box<dyn ::std::error::Error + Send + Sync> {
::std::boxed::Box::new(e)
})?;
(*__arc).clone()
};
}
}
})
.collect();
let inject_param_names: Vec<_> = inject_params
.iter()
.map(|(pat, _)| {
if let Pat::Ident(pat_ident) = pat.as_ref() {
let ident = &pat_ident.ident;
quote! { #ident }
} else {
quote! { #pat }
}
})
.collect();
let fn_return = &input.sig.output;
let fn_generics = &input.sig.generics;
let stripped_params: Vec<_> = input
.sig
.inputs
.iter()
.map(|arg| {
if let FnArg::Typed(pat_type) = arg {
let attrs: Vec<_> = pat_type
.attrs
.iter()
.filter(|a| !is_inject_attr(a))
.collect();
let pat = &pat_type.pat;
let ty = &pat_type.ty;
quote! { #(#attrs)* #pat: #ty }
} else {
quote! { #arg }
}
})
.collect();
quote! {
#user_fn_and_marker_gate
#[allow(private_interfaces)]
#(#fn_attrs)*
#fn_vis async fn #fn_name #fn_generics(#(#stripped_params),*) #fn_return #fn_block
#native_only
#[allow(unsafe_attr_outside_unsafe)]
const _: () = {
fn __get_server_router() -> ::std::pin::Pin<
::std::boxed::Box<
dyn ::std::future::Future<
Output = ::std::result::Result<
::std::sync::Arc<#reinhardt::ServerRouter>,
::std::boxed::Box<dyn ::std::error::Error + Send + Sync>,
>,
> + Send,
>,
> {
::std::boxed::Box::pin(async {
let __scope = ::std::sync::Arc::new(
#di_crate::SingletonScope::new()
);
let __ctx = ::std::sync::Arc::new(
#di_crate::InjectionContext::builder(__scope).build()
);
#(#inject_resolutions)*
let unified = #fn_name(#(#inject_param_names),*).await;
::std::result::Result::Ok(::std::sync::Arc::new(unified.into_server()))
})
}
#reinhardt::inventory::submit! {
#reinhardt::UrlPatternsRegistration::__macro_new_async(__get_server_router)
}
};
#user_fn_and_marker_gate
#[doc(hidden)]
#[unsafe(no_mangle)]
#[allow(non_upper_case_globals, dead_code)]
static __reinhardt_routes_registration_marker: () = ();
}
};
let url_prelude_code = if standalone {
quote! {}
} else {
let app_labels = crate::macro_state::read_installed_apps().map_err(|e| {
syn::Error::new(
proc_macro2::Span::call_site(),
format!("Failed to read installed apps state: {e}"),
)
})?;
let app_idents: Vec<proc_macro2::Ident> = app_labels
.iter()
.filter(|s| !s.is_empty())
.map(|s| {
syn::parse_str::<syn::Ident>(s).map_err(|_| {
syn::Error::new(
proc_macro2::Span::call_site(),
format!(
"Invalid installed app label `{s}`: expected a valid Rust identifier"
),
)
})
})
.collect::<Result<Vec<_>>>()?;
if app_idents.is_empty() {
quote! {
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub mod url_prelude {
pub use super::ResolvedUrls;
}
}
} else {
let per_app_code: Vec<_> = app_idents
.iter()
.map(|app| {
let urls_struct_name =
crate::pascal_case::to_pascal_case_with_suffix(&app.to_string(), "Urls");
let urls_struct =
proc_macro2::Ident::new(&urls_struct_name, proc_macro2::Span::call_site());
let gen_method_macro = proc_macro2::Ident::new(
&format!("__gen_{}_method", app),
proc_macro2::Span::call_site(),
);
let server_callback_arms = gen_resolver_callback_arms(
&urls_struct,
"e! { resolve_url },
"e! { stringify!($app_label) },
"e! { use #reinhardt::UrlResolver as _; },
);
quote! {
pub struct #urls_struct<'a> {
resolver: &'a ResolvedUrls,
}
macro_rules! #gen_method_macro {
#server_callback_arms
}
crate::apps::#app::urls::url_resolvers::__for_each_url_resolver!(
#gen_method_macro, #app,
crate::apps::#app::urls::url_resolvers
);
impl ResolvedUrls {
#[deprecated(
since = "0.1.0-rc.16",
note = "use `urls.server().#app()` instead"
)]
pub fn #app(&self) -> #urls_struct<'_> {
#urls_struct { resolver: self }
}
}
}
})
.collect();
let per_app_client_code: Vec<_> = if no_client_resolvers {
Vec::new()
} else {
app_idents
.iter()
.map(|app| {
let client_urls_struct_name =
crate::pascal_case::to_pascal_case_with_suffix(
&app.to_string(),
"ClientUrls",
);
let client_urls_struct = proc_macro2::Ident::new(
&client_urls_struct_name,
proc_macro2::Span::call_site(),
);
let gen_client_method_macro = proc_macro2::Ident::new(
&format!("__gen_{}_client_method", app),
proc_macro2::Span::call_site(),
);
let accessor_method = proc_macro2::Ident::new(
&format!("{}_client", app),
proc_macro2::Span::call_site(),
);
let app_str = app.to_string();
let client_callback_arms = gen_resolver_callback_arms(
&client_urls_struct,
"e! { resolve_client_url },
"e! { #app_str },
"e! { use #reinhardt::ClientUrlResolver as _; },
);
quote! {
pub struct #client_urls_struct<'a> {
resolver: &'a ResolvedUrls,
}
impl #client_urls_struct<'_> {
pub fn resolve(&self, route_name: &str, params: &[(&str, &str)]) -> String {
use #reinhardt::ClientUrlResolver as _;
let full_name = ::std::format!("{}:{}", #app_str, route_name);
self.resolver.resolve_client_url(&full_name, params)
}
}
macro_rules! #gen_client_method_macro {
#client_callback_arms
}
crate::apps::#app::urls::client_url_resolvers::__for_each_client_url_resolver!(
#gen_client_method_macro, #app,
crate::apps::#app::urls::client_url_resolvers
);
impl ResolvedUrls {
#[deprecated(
since = "0.1.0-rc.16",
note = "use `urls.client().#app()` instead"
)]
pub fn #accessor_method(&self) -> #client_urls_struct<'_> {
#client_urls_struct { resolver: self }
}
}
}
})
.collect()
};
let prelude_exports: Vec<_> = app_idents
.iter()
.map(|app| {
let urls_struct_name =
crate::pascal_case::to_pascal_case_with_suffix(&app.to_string(), "Urls");
let urls_struct =
proc_macro2::Ident::new(&urls_struct_name, proc_macro2::Span::call_site());
let client_export = if no_client_resolvers {
quote! {}
} else {
let client_urls_struct_name =
crate::pascal_case::to_pascal_case_with_suffix(
&app.to_string(),
"ClientUrls",
);
let client_urls_struct = proc_macro2::Ident::new(
&client_urls_struct_name,
proc_macro2::Span::call_site(),
);
quote! {
#[cfg(feature = "client-router")]
pub use super::super::__namespaced_client_resolvers::#client_urls_struct;
}
};
quote! {
pub use super::#urls_struct;
#client_export
#[allow(deprecated)]
pub use crate::apps::#app::urls::url_resolvers::*;
}
})
.collect();
let per_app_ws_code: Vec<_> = if no_ws_resolvers {
Vec::new()
} else {
app_idents
.iter()
.map(|app| {
let ws_urls_struct_name = crate::pascal_case::to_pascal_case_with_suffix(
&app.to_string(),
"WsUrls",
);
let ws_urls_struct = proc_macro2::Ident::new(
&ws_urls_struct_name,
proc_macro2::Span::call_site(),
);
let gen_ws_method_macro = proc_macro2::Ident::new(
&format!("__gen_{}_ws_method", app),
proc_macro2::Span::call_site(),
);
let ws_callback_arms = gen_resolver_callback_arms(
&ws_urls_struct,
"e! { resolve_ws_url },
"e! { stringify!($app_label) },
"e! { use #reinhardt::WebSocketUrlResolver as _; },
);
quote! {
pub struct #ws_urls_struct<'a> {
resolver: &'a ResolvedUrls,
}
macro_rules! #gen_ws_method_macro {
#ws_callback_arms
}
crate::apps::#app::urls::ws_urls::ws_url_resolvers::__for_each_ws_url_resolver!(
#gen_ws_method_macro, #app,
crate::apps::#app::urls::ws_urls::ws_url_resolvers
);
}
})
.collect()
};
let server_app_accessors: Vec<_> = app_idents
.iter()
.map(|app| {
let urls_struct_name =
crate::pascal_case::to_pascal_case_with_suffix(&app.to_string(), "Urls");
let urls_struct =
proc_macro2::Ident::new(&urls_struct_name, proc_macro2::Span::call_site());
quote! {
pub fn #app(&self) -> #urls_struct<'_> {
#urls_struct { resolver: self.resolver }
}
}
})
.collect();
let client_app_accessors: Vec<_> = if no_client_resolvers {
Vec::new()
} else {
app_idents
.iter()
.map(|app| {
let client_urls_struct_name =
crate::pascal_case::to_pascal_case_with_suffix(
&app.to_string(),
"ClientUrls",
);
let client_urls_struct = proc_macro2::Ident::new(
&client_urls_struct_name,
proc_macro2::Span::call_site(),
);
quote! {
pub fn #app(&self) -> #client_urls_struct<'_> {
#client_urls_struct { resolver: self.resolver }
}
}
})
.collect()
};
let ws_app_accessors: Vec<_> = if no_ws_resolvers {
Vec::new()
} else {
app_idents
.iter()
.map(|app| {
let ws_urls_struct_name = crate::pascal_case::to_pascal_case_with_suffix(
&app.to_string(),
"WsUrls",
);
let ws_urls_struct = proc_macro2::Ident::new(
&ws_urls_struct_name,
proc_macro2::Span::call_site(),
);
quote! {
pub fn #app(&self) -> #ws_urls_struct<'_> {
#ws_urls_struct { resolver: self.resolver }
}
}
})
.collect()
};
let ws_block = if no_ws_resolvers {
quote! {}
} else {
quote! {
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
#[doc(hidden)]
mod __namespaced_ws_resolvers {
#![allow(unexpected_cfgs, dead_code)]
pub use super::ResolvedUrls;
#(#per_app_ws_code)*
pub struct WsUrls<'a> {
resolver: &'a ResolvedUrls,
}
impl WsUrls<'_> {
#(#ws_app_accessors)*
}
impl ResolvedUrls {
pub fn ws(&self) -> WsUrls<'_> {
WsUrls { resolver: self }
}
}
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub use __namespaced_ws_resolvers::*;
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
impl #reinhardt::WebSocketUrlResolver for ResolvedUrls {
fn resolve_ws_url(&self, _name: &str, _params: &[(&str, &str)]) -> String {
unimplemented!(
"WebSocket URL resolution requires reinhardt-websockets. \
Call impl_ws_url_resolver!(ResolvedUrls) to enable it."
)
}
}
}
};
let client_block = if no_client_resolvers {
quote! {}
} else {
quote! {
#[doc(hidden)]
mod __client_router_gate {
#![allow(unexpected_cfgs, deprecated)]
#[cfg(feature = "client-router")]
#[doc(hidden)]
pub mod __namespaced_client_resolvers {
pub use super::super::ResolvedUrls;
#(#per_app_client_code)*
pub struct ClientUrls<'a> {
resolver: &'a ResolvedUrls,
}
impl ClientUrls<'_> {
#(#client_app_accessors)*
}
impl ResolvedUrls {
pub fn client(&self) -> ClientUrls<'_> {
ClientUrls { resolver: self }
}
}
}
#[cfg(feature = "client-router")]
pub use __namespaced_client_resolvers::*;
}
pub use __client_router_gate::*;
}
};
quote! {
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
const _: &[u8] = include_bytes!(concat!(
env!("CARGO_MANIFEST_DIR"),
"/target/reinhardt/",
env!("CARGO_CRATE_NAME"),
"/.installed_apps",
));
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
#[doc(hidden)]
mod __namespaced_resolvers {
#![allow(unexpected_cfgs, deprecated)]
pub use super::ResolvedUrls;
#(#per_app_code)*
pub struct ServerUrls<'a> {
resolver: &'a ResolvedUrls,
}
impl ServerUrls<'_> {
#(#server_app_accessors)*
}
impl ResolvedUrls {
pub fn server(&self) -> ServerUrls<'_> {
ServerUrls { resolver: self }
}
}
#[allow(deprecated)]
impl #reinhardt::UrlResolverUnprefixed for ResolvedUrls {
fn resolve_url_unprefixed(
&self,
name: &str,
params: &[(&str, &str)],
) -> ::std::string::String {
#(
{
let full = ::std::format!(
"{}:{}", stringify!(#app_idents), name
);
if let ::std::option::Option::Some(u) =
#reinhardt::UrlResolver::try_resolve_url(self, &full, params)
{
return u;
}
}
)*
#reinhardt::UrlResolver::resolve_url(self, name, params)
}
}
pub mod url_prelude {
pub use super::ResolvedUrls;
#(#prelude_exports)*
}
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub use __namespaced_resolvers::*;
#ws_block
#client_block
}
}
};
let client_reverser_fallback = if no_client_resolvers {
quote! {
{
static EMPTY_REVERSER:
::std::sync::OnceLock<::std::sync::Arc<#reinhardt::ClientUrlReverser>>
= ::std::sync::OnceLock::new();
::std::sync::Arc::clone(
EMPTY_REVERSER.get_or_init(|| {
::std::sync::Arc::new(#reinhardt::ClientUrlReverser::new(
::std::collections::HashMap::new(),
))
}),
)
}
}
} else {
quote! {
panic!(
"Global client reverser not registered. Ensure the #[routes] function has been called."
)
}
};
let url_resolver_code = quote! {
#[doc(hidden)]
pub mod __url_resolver_support {
#![allow(unexpected_cfgs)]
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
pub struct ResolvedUrls {
router: ::std::sync::Arc<#reinhardt::ServerRouter>,
#[cfg(feature = "client-router")]
client_reverser: ::std::sync::Arc<#reinhardt::ClientUrlReverser>,
}
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
pub struct ResolvedUrls {
client_reverser: ::std::sync::Arc<#reinhardt::ClientUrlReverser>,
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
impl #reinhardt::UrlResolver for ResolvedUrls {
fn resolve_url(&self, name: &str, params: &[(&str, &str)]) -> String {
self.router
.reverse(name, params)
.unwrap_or_else(|| panic!("Route '{}' not found in router", name))
}
fn try_resolve_url(&self, name: &str, params: &[(&str, &str)]) -> Option<String> {
self.router.reverse(name, params)
}
}
#[cfg(feature = "client-router")]
impl #reinhardt::ClientUrlResolver for ResolvedUrls {
fn resolve_client_url(&self, name: &str, params: &[(&str, &str)]) -> String {
self.client_reverser
.reverse(name, params)
.unwrap_or_else(|| panic!("Client route '{}' not found in router", name))
}
}
#[cfg(not(all(target_family = "wasm", target_os = "unknown")))]
impl ResolvedUrls {
#[cfg(feature = "client-router")]
pub fn from_global() -> Self {
let router = #reinhardt::get_router()
.expect("Global router not registered. Ensure the #[routes] function has been called.");
let client_reverser = #reinhardt::get_client_reverser()
.unwrap_or_else(|| #client_reverser_fallback);
Self { router, client_reverser }
}
#[cfg(not(feature = "client-router"))]
pub fn from_global() -> Self {
let router = #reinhardt::get_router()
.expect("Global router not registered. Ensure the #[routes] function has been called.");
Self { router }
}
#[cfg(feature = "client-router")]
pub fn from_router(
router: ::std::sync::Arc<#reinhardt::ServerRouter>,
client_reverser: ::std::sync::Arc<#reinhardt::ClientUrlReverser>,
) -> Self {
Self { router, client_reverser }
}
#[cfg(not(feature = "client-router"))]
pub fn from_router(
router: ::std::sync::Arc<#reinhardt::ServerRouter>,
) -> Self {
Self { router }
}
#[cfg(feature = "streaming")]
pub fn streaming(&self) -> StreamingRef<'_> {
StreamingRef { _marker: ::core::marker::PhantomData }
}
}
#[cfg(all(not(all(target_family = "wasm", target_os = "unknown")), feature = "streaming"))]
pub struct StreamingRef<'a> {
_marker: ::core::marker::PhantomData<&'a ()>,
}
#[cfg(all(not(all(target_family = "wasm", target_os = "unknown")), feature = "streaming"))]
impl #reinhardt::streaming::StreamingTopicResolver for StreamingRef<'_> {
fn resolve_topic(&self, name: &str) -> &'static str {
#reinhardt::streaming::resolve_streaming_topic(name)
}
}
#[cfg(all(not(all(target_family = "wasm", target_os = "unknown")), feature = "streaming"))]
impl<'a> StreamingRef<'a> {
pub fn topic_for(&self, name: &str) -> &'static str {
#reinhardt::streaming::resolve_streaming_topic(name)
}
}
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
impl ResolvedUrls {
pub fn from_global() -> Self {
let client_reverser = #reinhardt::get_client_reverser()
.unwrap_or_else(|| #client_reverser_fallback);
Self { client_reverser }
}
pub fn from_reverser(
client_reverser: ::std::sync::Arc<#reinhardt::ClientUrlReverser>,
) -> Self {
Self { client_reverser }
}
}
#url_prelude_code
}
pub use __url_resolver_support::ResolvedUrls;
pub use __url_resolver_support::*;
};
let combined = quote! {
#expanded
#url_resolver_code
};
Ok(combined)
}