1#![cfg_attr(all(feature = "nightly", rustc_nightly), feature(proc_macro_span))]
2#![forbid(unsafe_code)]
3#![deny(missing_docs)]
4
5use convert_case::{Case, Converter};
10use proc_macro2::{Span, TokenStream as TokenStream2};
11use quote::{format_ident, quote, quote_spanned, ToTokens};
12use syn::{
13 parse::{Parse, ParseStream},
14 punctuated::Punctuated,
15 spanned::Spanned,
16 *,
17};
18
19pub struct ServerFnCall {
21 args: ServerFnArgs,
22 body: ServerFnBody,
23 default_path: String,
24 server_fn_path: Option<Path>,
25 preset_server: Option<Type>,
26 default_protocol: Option<Type>,
27 default_input_encoding: Option<Type>,
28 default_output_encoding: Option<Type>,
29}
30
31impl ServerFnCall {
32 pub fn parse(
48 default_path: &str,
49 args: TokenStream2,
50 body: TokenStream2,
51 ) -> Result<Self> {
52 let args = syn::parse2(args)?;
53 let body = syn::parse2(body)?;
54 let mut myself = ServerFnCall {
55 default_path: default_path.into(),
56 args,
57 body,
58 server_fn_path: None,
59 preset_server: None,
60 default_protocol: None,
61 default_input_encoding: None,
62 default_output_encoding: None,
63 };
64
65 if cfg!(feature = "actix") {
69 let server_fn_path = myself.server_fn_path();
70 let block = myself.body.block.to_token_stream();
71 myself.body.block = quote! {
72 {
73 #server_fn_path::actix::SendWrapper::new(async move {
74 #block
75 })
76 .await
77 }
78 };
79 }
80
81 Ok(myself)
82 }
83
84 pub fn get_args(&self) -> &ServerFnArgs {
86 &self.args
87 }
88
89 pub fn get_args_mut(&mut self) -> &mut ServerFnArgs {
91 &mut self.args
92 }
93
94 pub fn get_body(&self) -> &ServerFnBody {
96 &self.body
97 }
98
99 pub fn get_body_mut(&mut self) -> &mut ServerFnBody {
101 &mut self.body
102 }
103
104 pub fn default_server_fn_path(mut self, path: Option<Path>) -> Self {
106 self.server_fn_path = path;
107 self
108 }
109
110 pub fn default_server_type(mut self, server: Option<Type>) -> Self {
112 self.preset_server = server;
113 self
114 }
115
116 pub fn default_protocol(mut self, protocol: Option<Type>) -> Self {
118 self.default_protocol = protocol;
119 self
120 }
121
122 pub fn default_input_encoding(mut self, protocol: Option<Type>) -> Self {
127 self.default_input_encoding = protocol;
128 self
129 }
130
131 pub fn default_output_encoding(mut self, protocol: Option<Type>) -> Self {
136 self.default_output_encoding = protocol;
137 self
138 }
139
140 pub fn client_type(&self) -> Type {
142 let server_fn_path = self.server_fn_path();
143 if let Some(client) = self.args.client.clone() {
144 client
145 } else if cfg!(feature = "reqwest") {
146 parse_quote! {
147 #server_fn_path::client::reqwest::ReqwestClient
148 }
149 } else {
150 parse_quote! {
151 #server_fn_path::client::browser::BrowserClient
152 }
153 }
154 }
155
156 pub fn server_type(&self) -> Type {
158 let server_fn_path = self.server_fn_path();
159 if !cfg!(feature = "ssr") {
160 parse_quote! {
161 #server_fn_path::mock::BrowserMockServer
162 }
163 } else if cfg!(feature = "axum") {
164 parse_quote! {
165 #server_fn_path::axum::AxumServerFnBackend
166 }
167 } else if cfg!(feature = "actix") {
168 parse_quote! {
169 #server_fn_path::actix::ActixServerFnBackend
170 }
171 } else if cfg!(feature = "generic") {
172 parse_quote! {
173 #server_fn_path::axum::AxumServerFnBackend
174 }
175 } else if let Some(server) = &self.args.server {
176 server.clone()
177 } else if let Some(server) = &self.preset_server {
178 server.clone()
179 } else {
180 parse_quote! {
184 #server_fn_path::mock::BrowserMockServer
185 }
186 }
187 }
188
189 pub fn server_fn_path(&self) -> Path {
191 self.server_fn_path
192 .clone()
193 .unwrap_or_else(|| parse_quote! { server_fn })
194 }
195
196 fn input_http_encoding(&self) -> Type {
198 let server_fn_path = self.server_fn_path();
199 self.args
200 .input
201 .as_ref()
202 .map(|n| {
203 if self.args.builtin_encoding {
204 parse_quote! { #server_fn_path::codec::#n }
205 } else {
206 n.clone()
207 }
208 })
209 .unwrap_or_else(|| {
210 self.default_input_encoding.clone().unwrap_or_else(
211 || parse_quote!(#server_fn_path::codec::PostUrl),
212 )
213 })
214 }
215
216 fn output_http_encoding(&self) -> Type {
218 let server_fn_path = self.server_fn_path();
219 self.args
220 .output
221 .as_ref()
222 .map(|n| {
223 if self.args.builtin_encoding {
224 parse_quote! { #server_fn_path::codec::#n }
225 } else {
226 n.clone()
227 }
228 })
229 .unwrap_or_else(|| {
230 self.default_output_encoding.clone().unwrap_or_else(
231 || parse_quote!(#server_fn_path::codec::Json),
232 )
233 })
234 }
235
236 pub fn http_encodings(&self) -> Option<(Type, Type)> {
239 self.args
240 .protocol
241 .is_none()
242 .then(|| (self.input_http_encoding(), self.output_http_encoding()))
243 }
244
245 pub fn protocol(&self) -> Type {
247 let server_fn_path = self.server_fn_path();
248 let default_protocol = &self.default_protocol;
249 self.args.protocol.clone().unwrap_or_else(|| {
250 if self.args.input.is_none() && self.args.output.is_none() {
253 default_protocol.clone().unwrap_or_else(|| {
254 parse_quote! {
255 #server_fn_path::Http<#server_fn_path::codec::PostUrl, #server_fn_path::codec::Json>
256 }
257 })
258 } else {
259 let input = self.input_http_encoding();
262 let output = self.output_http_encoding();
263 parse_quote! {
264 #server_fn_path::Http<#input, #output>
265 }
266 }
267 })
268 }
269
270 fn input_ident(&self) -> Option<String> {
271 match &self.args.input {
272 Some(Type::Path(path)) => {
273 path.path.segments.last().map(|seg| seg.ident.to_string())
274 }
275 None => Some("PostUrl".to_string()),
276 _ => None,
277 }
278 }
279
280 fn websocket_protocol(&self) -> bool {
281 if let Type::Path(path) = self.protocol() {
282 path.path
283 .segments
284 .iter()
285 .any(|segment| segment.ident == "Websocket")
286 } else {
287 false
288 }
289 }
290
291 fn serde_path(&self) -> String {
292 let path = self
293 .server_fn_path()
294 .segments
295 .iter()
296 .map(|segment| segment.ident.to_string())
297 .collect::<Vec<_>>();
298 let path = path.join("::");
299 format!("{path}::serde")
300 }
301
302 pub fn docs(&self) -> TokenStream2 {
304 self.body
306 .docs
307 .iter()
308 .map(|(doc, span)| quote_spanned!(*span=> #[doc = #doc]))
309 .collect::<TokenStream2>()
310 }
311
312 fn fn_name_as_str(&self) -> String {
313 self.body.ident.to_string()
314 }
315
316 fn struct_tokens(&self) -> TokenStream2 {
317 let server_fn_path = self.server_fn_path();
318 let fn_name_as_str = self.fn_name_as_str();
319 let link_to_server_fn = format!(
320 "Serialized arguments for the [`{fn_name_as_str}`] server \
321 function.\n\n"
322 );
323 let args_docs = quote! {
324 #[doc = #link_to_server_fn]
325 };
326
327 let docs = self.docs();
328
329 let input_ident = self.input_ident();
330
331 enum PathInfo {
332 Serde,
333 Rkyv,
334 None,
335 }
336
337 let (path, derives) = match input_ident.as_deref() {
338 Some("Rkyv") => (
339 PathInfo::Rkyv,
340 quote! {
341 Clone, #server_fn_path::rkyv::Archive, #server_fn_path::rkyv::Serialize, #server_fn_path::rkyv::Deserialize
342 },
343 ),
344 Some("MultipartFormData")
345 | Some("Streaming")
346 | Some("StreamingText") => (PathInfo::None, quote! {}),
347 Some("SerdeLite") => (
348 PathInfo::Serde,
349 quote! {
350 Clone, #server_fn_path::serde_lite::Serialize, #server_fn_path::serde_lite::Deserialize
351 },
352 ),
353 _ => match &self.args.input_derive {
354 Some(derives) => {
355 let d = &derives.elems;
356 (PathInfo::None, quote! { #d })
357 }
358 None => {
359 if self.websocket_protocol() {
360 (PathInfo::None, quote! {})
361 } else {
362 (
363 PathInfo::Serde,
364 quote! {
365 Clone, #server_fn_path::serde::Serialize, #server_fn_path::serde::Deserialize
366 },
367 )
368 }
369 }
370 },
371 };
372 let addl_path = match path {
373 PathInfo::Serde => {
374 let serde_path = self.serde_path();
375 quote! {
376 #[serde(crate = #serde_path)]
377 }
378 }
379 PathInfo::Rkyv => quote! {},
380 PathInfo::None => quote! {},
381 };
382
383 let vis = &self.body.vis;
384 let struct_name = self.struct_name();
385 let fields = self
386 .body
387 .inputs
388 .iter()
389 .map(|server_fn_arg| {
390 let mut typed_arg = server_fn_arg.arg.clone();
391 if let Pat::Ident(ident) = &mut *typed_arg.pat {
393 ident.mutability = None;
394 }
395 let attrs = &server_fn_arg.server_fn_attributes;
396 quote! { #(#attrs ) * #vis #typed_arg }
397 })
398 .collect::<Vec<_>>();
399
400 quote! {
401 #args_docs
402 #docs
403 #[derive(Debug, #derives)]
404 #addl_path
405 #vis struct #struct_name {
406 #(#fields),*
407 }
408 }
409 }
410
411 pub fn struct_name(&self) -> Ident {
416 self.args.struct_name.clone().unwrap_or_else(|| {
418 let upper_camel_case_name = Converter::new()
419 .from_case(Case::Snake)
420 .to_case(Case::UpperCamel)
421 .convert(self.body.ident.to_string());
422 Ident::new(&upper_camel_case_name, self.body.ident.span())
423 })
424 }
425
426 fn wrapped_struct_name(&self) -> TokenStream2 {
429 let struct_name = self.struct_name();
430 if let Some(wrapper) = self.args.custom_wrapper.as_ref() {
431 quote! { #wrapper<#struct_name> }
432 } else {
433 quote! { #struct_name }
434 }
435 }
436
437 fn wrapped_struct_name_turbofish(&self) -> TokenStream2 {
440 let struct_name = self.struct_name();
441 if let Some(wrapper) = self.args.custom_wrapper.as_ref() {
442 quote! { #wrapper::<#struct_name> }
443 } else {
444 quote! { #struct_name }
445 }
446 }
447
448 pub fn submit_to_inventory(&self) -> TokenStream2 {
450 if cfg!(feature = "ssr") {
452 let server_fn_path = self.server_fn_path();
453 let wrapped_struct_name = self.wrapped_struct_name();
454 let wrapped_struct_name_turbofish =
455 self.wrapped_struct_name_turbofish();
456 quote! {
457 #server_fn_path::inventory::submit! {{
458 use #server_fn_path::{ServerFn, codec::Encoding};
459 #server_fn_path::ServerFnTraitObj::new::<#wrapped_struct_name>(
460 |req| Box::pin(#wrapped_struct_name_turbofish::run_on_server(req)),
461 )
462 }}
463 }
464 } else {
465 quote! {}
466 }
467 }
468
469 pub fn server_fn_url(&self) -> TokenStream2 {
473 let default_path = &self.default_path;
474 let prefix =
475 self.args.prefix.clone().unwrap_or_else(|| {
476 LitStr::new(default_path, Span::call_site())
477 });
478 let server_fn_path = self.server_fn_path();
479 let fn_path = self.args.fn_path.clone().map(|fn_path| {
480 let fn_path = fn_path.value();
481 let fn_path = "/".to_string() + fn_path.trim_start_matches('/');
483 fn_path
484 });
485
486 let enable_server_fn_mod_path =
487 option_env!("SERVER_FN_MOD_PATH").is_some();
488 let mod_path = if enable_server_fn_mod_path {
489 quote! {
490 #server_fn_path::const_format::concatcp!(
491 #server_fn_path::const_str::replace!(module_path!(), "::", "/"),
492 "/"
493 )
494 }
495 } else {
496 quote! { "" }
497 };
498
499 let enable_hash = option_env!("DISABLE_SERVER_FN_HASH").is_none();
500 let key_env_var = match option_env!("SERVER_FN_OVERRIDE_KEY") {
501 Some(_) => "SERVER_FN_OVERRIDE_KEY",
502 None => "CARGO_MANIFEST_DIR",
503 };
504 let hash = if enable_hash {
505 quote! {
506 #server_fn_path::xxhash_rust::const_xxh64::xxh64(
507 concat!(env!(#key_env_var), ":", module_path!()).as_bytes(),
508 0
509 )
510 }
511 } else {
512 quote! { "" }
513 };
514
515 let fn_name_as_str = self.fn_name_as_str();
516 if let Some(fn_path) = fn_path {
517 quote! {
518 #server_fn_path::const_format::concatcp!(
519 #prefix,
520 #mod_path,
521 #fn_path
522 )
523 }
524 } else {
525 quote! {
526 #server_fn_path::const_format::concatcp!(
527 #prefix,
528 "/",
529 #mod_path,
530 #fn_name_as_str,
531 #hash
532 )
533 }
534 }
535 }
536
537 fn field_names(&self) -> Vec<&std::boxed::Box<syn::Pat>> {
539 self.body
540 .inputs
541 .iter()
542 .map(|f| &f.arg.pat)
543 .collect::<Vec<_>>()
544 }
545
546 fn server_fn_impl(&self) -> TokenStream2 {
548 let server_fn_path = self.server_fn_path();
549 let struct_name = self.struct_name();
550
551 let protocol = self.protocol();
552 let middlewares = &self.body.middlewares;
553 let return_ty = &self.body.return_ty;
554 let output_ty = self.body.output_ty
555 .as_ref()
556 .map_or_else(|| {
557 quote! {
558 <#return_ty as #server_fn_path::error::ServerFnMustReturnResult>::Ok
559 }
560 },
561 ToTokens::to_token_stream,
562 );
563 let error_ty = &self.body.error_ty;
564 let error_ty = error_ty
565 .as_ref()
566 .map_or_else(|| {
567 quote! {
568 <#return_ty as #server_fn_path::error::ServerFnMustReturnResult>::Err
569 }
570 },
571 ToTokens::to_token_stream,
572 );
573 let error_ws_in_ty = if self.websocket_protocol() {
574 self.body
575 .error_ws_in_ty
576 .as_ref()
577 .map(ToTokens::to_token_stream)
578 .unwrap_or(error_ty.clone())
579 } else {
580 error_ty.clone()
581 };
582 let error_ws_out_ty = if self.websocket_protocol() {
583 self.body
584 .error_ws_out_ty
585 .as_ref()
586 .map(ToTokens::to_token_stream)
587 .unwrap_or(error_ty.clone())
588 } else {
589 error_ty.clone()
590 };
591 let field_names = self.field_names();
592
593 let run_body = if cfg!(feature = "ssr") {
595 let destructure =
596 if let Some(wrapper) = self.args.custom_wrapper.as_ref() {
597 quote! {
598 let #wrapper(#struct_name { #(#field_names),* }) = self;
599 }
600 } else {
601 quote! {
602 let #struct_name { #(#field_names),* } = self;
603 }
604 };
605 let dummy_name = self.body.to_dummy_ident();
606
607 let body = quote! {
616 async move {
617 #destructure
618 #dummy_name(#(#field_names),*).await
619 }
620 };
621 quote! {
622 #[allow(clippy::manual_async_fn)]
625 fn run_body(self) -> impl std::future::Future<Output = #return_ty> + Send {
626 #body
627 }
628 }
629 } else {
630 quote! {
631 #[allow(unused_variables)]
632 async fn run_body(self) -> #return_ty {
633 unreachable!()
634 }
635 }
636 };
637 let client = self.client_type();
638
639 let server = self.server_type();
640
641 let path = self.server_fn_url();
643
644 let middlewares = if cfg!(feature = "ssr") {
645 quote! {
646 vec![
647 #(
648 std::sync::Arc::new(#middlewares)
649 ),*
650 ]
651 }
652 } else {
653 quote! { vec![] }
654 };
655 let wrapped_struct_name = self.wrapped_struct_name();
656
657 quote! {
658 impl #server_fn_path::ServerFn for #wrapped_struct_name {
659 const PATH: &'static str = #path;
660
661 type Client = #client;
662 type Server = #server;
663 type Protocol = #protocol;
664 type Output = #output_ty;
665 type Error = #error_ty;
666 type InputStreamError = #error_ws_in_ty;
667 type OutputStreamError = #error_ws_out_ty;
668
669 fn middlewares() -> Vec<std::sync::Arc<dyn #server_fn_path::middleware::Layer<<Self::Server as #server_fn_path::server::Server<Self::Error>>::Request, <Self::Server as #server_fn_path::server::Server<Self::Error>>::Response>>> {
670 #middlewares
671 }
672
673 #run_body
674 }
675 }
676 }
677
678 fn single_field(&self) -> Option<(&Pat, &Type)> {
680 self.body
681 .inputs
682 .first()
683 .filter(|_| self.body.inputs.len() == 1)
684 .map(|field| (&*field.arg.pat, &*field.arg.ty))
685 }
686
687 fn deref_impl(&self) -> TokenStream2 {
688 let impl_deref = self
689 .args
690 .impl_deref
691 .as_ref()
692 .map(|v| v.value)
693 .unwrap_or(true)
694 || self.websocket_protocol();
695 if !impl_deref {
696 return quote! {};
697 }
698 let Some(single_field) = self.single_field() else {
700 return quote! {};
701 };
702 let struct_name = self.struct_name();
703 let (name, ty) = single_field;
704 quote! {
705 impl std::ops::Deref for #struct_name {
706 type Target = #ty;
707 fn deref(&self) -> &Self::Target {
708 &self.#name
709 }
710 }
711 }
712 }
713
714 fn impl_from(&self) -> TokenStream2 {
715 let impl_from = self
716 .args
717 .impl_from
718 .as_ref()
719 .map(|v| v.value)
720 .unwrap_or(true)
721 || self.websocket_protocol();
722 if !impl_from {
723 return quote! {};
724 }
725 let Some(single_field) = self.single_field() else {
727 return quote! {};
728 };
729 let struct_name = self.struct_name();
730 let (name, ty) = single_field;
731 quote! {
732 impl From<#struct_name> for #ty {
733 fn from(value: #struct_name) -> Self {
734 let #struct_name { #name } = value;
735 #name
736 }
737 }
738
739 impl From<#ty> for #struct_name {
740 fn from(#name: #ty) -> Self {
741 #struct_name { #name }
742 }
743 }
744 }
745 }
746
747 fn func_tokens(&self) -> TokenStream2 {
748 let body = &self.body;
749 let struct_name = self.struct_name();
751
752 let fn_name = &body.ident;
754 let attrs = &body.attrs;
755
756 let fn_args = body.inputs.iter().map(|f| &f.arg).collect::<Vec<_>>();
757
758 let field_names = self.field_names();
759
760 let output_arrow = body.output_arrow;
762 let return_ty = &body.return_ty;
763 let vis = &body.vis;
764
765 let docs = self.docs();
767
768 if cfg!(feature = "ssr") {
770 let dummy_name = body.to_dummy_ident();
771 quote! {
772 #docs
773 #(#attrs)*
774 #vis async fn #fn_name(#(#fn_args),*) #output_arrow #return_ty {
775 #dummy_name(#(#field_names),*).await
776 }
777 }
778 } else {
779 let restructure = if let Some(custom_wrapper) =
780 self.args.custom_wrapper.as_ref()
781 {
782 quote! {
783 let data = #custom_wrapper(#struct_name { #(#field_names),* });
784 }
785 } else {
786 quote! {
787 let data = #struct_name { #(#field_names),* };
788 }
789 };
790 let server_fn_path = self.server_fn_path();
791 quote! {
792 #docs
793 #(#attrs)*
794 #[allow(unused_variables)]
795 #vis async fn #fn_name(#(#fn_args),*) #output_arrow #return_ty {
796 use #server_fn_path::ServerFn;
797 #restructure
798 data.run_on_client().await
799 }
800 }
801 }
802 }
803}
804
805impl ToTokens for ServerFnCall {
806 fn to_tokens(&self, tokens: &mut TokenStream2) {
807 let body = &self.body;
808
809 let dummy = cfg!(feature = "ssr").then(|| body.to_dummy_output());
811
812 let impl_from = self.impl_from();
813
814 let deref_impl = self.deref_impl();
815
816 let inventory = self.submit_to_inventory();
817
818 let func = self.func_tokens();
819
820 let server_fn_impl = self.server_fn_impl();
821
822 let struct_tokens = self.struct_tokens();
823
824 tokens.extend(quote! {
825 #struct_tokens
826
827 #impl_from
828
829 #deref_impl
830
831 #server_fn_impl
832
833 #inventory
834
835 #func
836
837 #dummy
838 });
839 }
840}
841
842pub fn server_macro_impl(
857 args: TokenStream2,
858 body: TokenStream2,
859 server_fn_path: Option<Path>,
860 default_path: &str,
861 preset_server: Option<Type>,
862 default_protocol: Option<Type>,
863) -> Result<TokenStream2> {
864 let body = ServerFnCall::parse(default_path, args, body)?
865 .default_server_fn_path(server_fn_path)
866 .default_server_type(preset_server)
867 .default_protocol(default_protocol);
868
869 Ok(body.to_token_stream())
870}
871
872fn type_from_ident(ident: Ident) -> Type {
873 let mut segments = Punctuated::new();
874 segments.push(PathSegment {
875 ident,
876 arguments: PathArguments::None,
877 });
878 Type::Path(TypePath {
879 qself: None,
880 path: Path {
881 leading_colon: None,
882 segments,
883 },
884 })
885}
886
887#[derive(Debug, Clone)]
889pub struct Middleware {
890 expr: syn::Expr,
891}
892
893impl ToTokens for Middleware {
894 fn to_tokens(&self, tokens: &mut TokenStream2) {
895 let expr = &self.expr;
896 tokens.extend(quote::quote! {
897 #expr
898 });
899 }
900}
901
902impl Parse for Middleware {
903 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
904 let arg: syn::Expr = input.parse()?;
905 Ok(Middleware { expr: arg })
906 }
907}
908
909fn output_type(return_ty: &Type) -> Option<&Type> {
910 if let syn::Type::Path(pat) = &return_ty {
911 if pat.path.segments[0].ident == "Result" {
912 if pat.path.segments.is_empty() {
913 panic!("{:#?}", pat.path);
914 } else if let PathArguments::AngleBracketed(args) =
915 &pat.path.segments[0].arguments
916 {
917 if let GenericArgument::Type(ty) = &args.args[0] {
918 return Some(ty);
919 }
920 }
921 }
922 };
923
924 None
925}
926
927fn err_type(return_ty: &Type) -> Option<&Type> {
928 if let syn::Type::Path(pat) = &return_ty {
929 if pat.path.segments[0].ident == "Result" {
930 if let PathArguments::AngleBracketed(args) =
931 &pat.path.segments[0].arguments
932 {
933 if args.args.len() == 1 {
935 return None;
936 }
937 else if let GenericArgument::Type(ty) = &args.args[1] {
939 return Some(ty);
940 }
941 }
942 }
943 };
944
945 None
946}
947
948fn err_ws_in_type(
949 inputs: &Punctuated<ServerFnArg, syn::token::Comma>,
950) -> Option<Type> {
951 inputs.into_iter().find_map(|pat| {
952 if let syn::Type::Path(ref pat) = *pat.arg.ty {
953 if pat.path.segments[0].ident != "BoxedStream" {
954 return None;
955 }
956
957 if let PathArguments::AngleBracketed(args) =
958 &pat.path.segments[0].arguments
959 {
960 if args.args.len() == 1 {
962 return None;
963 }
964 else if let GenericArgument::Type(ty) = &args.args[1] {
966 return Some(ty.clone());
967 }
968 };
969 };
970
971 None
972 })
973}
974
975fn err_ws_out_type(output_ty: &Option<Type>) -> Result<Option<Type>> {
976 if let Some(syn::Type::Path(ref pat)) = output_ty {
977 if pat.path.segments[0].ident == "BoxedStream" {
978 if let PathArguments::AngleBracketed(args) =
979 &pat.path.segments[0].arguments
980 {
981 if args.args.len() == 1 {
983 return Ok(None);
984 }
985 else if let GenericArgument::Type(ty) = &args.args[1] {
987 return Ok(Some(ty.clone()));
988 }
989
990 return Err(syn::Error::new(
991 output_ty.span(),
992 "websocket server functions should return \
993 BoxedStream<Result<T, E>> where E: FromServerFnError",
994 ));
995 };
996 }
997 };
998
999 Ok(None)
1000}
1001
1002#[derive(Debug)]
1004#[non_exhaustive]
1005pub struct ServerFnArgs {
1006 pub struct_name: Option<Ident>,
1009 pub prefix: Option<LitStr>,
1011 pub input: Option<Type>,
1013 pub input_derive: Option<ExprTuple>,
1015 pub output: Option<Type>,
1017 pub fn_path: Option<LitStr>,
1019 pub server: Option<Type>,
1021 pub client: Option<Type>,
1023 pub custom_wrapper: Option<Path>,
1025 pub impl_from: Option<LitBool>,
1027 pub impl_deref: Option<LitBool>,
1029 pub protocol: Option<Type>,
1031 builtin_encoding: bool,
1032}
1033
1034impl Parse for ServerFnArgs {
1035 fn parse(stream: ParseStream) -> syn::Result<Self> {
1036 let mut struct_name: Option<Ident> = None;
1038 let mut prefix: Option<LitStr> = None;
1039 let mut encoding: Option<LitStr> = None;
1040 let mut fn_path: Option<LitStr> = None;
1041
1042 let mut input: Option<Type> = None;
1044 let mut input_derive: Option<ExprTuple> = None;
1045 let mut output: Option<Type> = None;
1046 let mut server: Option<Type> = None;
1047 let mut client: Option<Type> = None;
1048 let mut custom_wrapper: Option<Path> = None;
1049 let mut impl_from: Option<LitBool> = None;
1050 let mut impl_deref: Option<LitBool> = None;
1051 let mut protocol: Option<Type> = None;
1052
1053 let mut use_key_and_value = false;
1054 let mut arg_pos = 0;
1055
1056 while !stream.is_empty() {
1057 arg_pos += 1;
1058 let lookahead = stream.lookahead1();
1059 if lookahead.peek(Ident) {
1060 let key_or_value: Ident = stream.parse()?;
1061
1062 let lookahead = stream.lookahead1();
1063 if lookahead.peek(Token![=]) {
1064 stream.parse::<Token![=]>()?;
1065 let key = key_or_value;
1066 use_key_and_value = true;
1067 if key == "name" {
1068 if struct_name.is_some() {
1069 return Err(syn::Error::new(
1070 key.span(),
1071 "keyword argument repeated: `name`",
1072 ));
1073 }
1074 struct_name = Some(stream.parse()?);
1075 } else if key == "prefix" {
1076 if prefix.is_some() {
1077 return Err(syn::Error::new(
1078 key.span(),
1079 "keyword argument repeated: `prefix`",
1080 ));
1081 }
1082 prefix = Some(stream.parse()?);
1083 } else if key == "encoding" {
1084 if encoding.is_some() {
1085 return Err(syn::Error::new(
1086 key.span(),
1087 "keyword argument repeated: `encoding`",
1088 ));
1089 }
1090 encoding = Some(stream.parse()?);
1091 } else if key == "endpoint" {
1092 if fn_path.is_some() {
1093 return Err(syn::Error::new(
1094 key.span(),
1095 "keyword argument repeated: `endpoint`",
1096 ));
1097 }
1098 fn_path = Some(stream.parse()?);
1099 } else if key == "input" {
1100 if encoding.is_some() {
1101 return Err(syn::Error::new(
1102 key.span(),
1103 "`encoding` and `input` should not both be \
1104 specified",
1105 ));
1106 } else if input.is_some() {
1107 return Err(syn::Error::new(
1108 key.span(),
1109 "keyword argument repeated: `input`",
1110 ));
1111 }
1112 input = Some(stream.parse()?);
1113 } else if key == "input_derive" {
1114 if input_derive.is_some() {
1115 return Err(syn::Error::new(
1116 key.span(),
1117 "keyword argument repeated: `input_derive`",
1118 ));
1119 }
1120 input_derive = Some(stream.parse()?);
1121 } else if key == "output" {
1122 if encoding.is_some() {
1123 return Err(syn::Error::new(
1124 key.span(),
1125 "`encoding` and `output` should not both be \
1126 specified",
1127 ));
1128 } else if output.is_some() {
1129 return Err(syn::Error::new(
1130 key.span(),
1131 "keyword argument repeated: `output`",
1132 ));
1133 }
1134 output = Some(stream.parse()?);
1135 } else if key == "server" {
1136 if server.is_some() {
1137 return Err(syn::Error::new(
1138 key.span(),
1139 "keyword argument repeated: `server`",
1140 ));
1141 }
1142 server = Some(stream.parse()?);
1143 } else if key == "client" {
1144 if client.is_some() {
1145 return Err(syn::Error::new(
1146 key.span(),
1147 "keyword argument repeated: `client`",
1148 ));
1149 }
1150 client = Some(stream.parse()?);
1151 } else if key == "custom" {
1152 if custom_wrapper.is_some() {
1153 return Err(syn::Error::new(
1154 key.span(),
1155 "keyword argument repeated: `custom`",
1156 ));
1157 }
1158 custom_wrapper = Some(stream.parse()?);
1159 } else if key == "impl_from" {
1160 if impl_from.is_some() {
1161 return Err(syn::Error::new(
1162 key.span(),
1163 "keyword argument repeated: `impl_from`",
1164 ));
1165 }
1166 impl_from = Some(stream.parse()?);
1167 } else if key == "impl_deref" {
1168 if impl_deref.is_some() {
1169 return Err(syn::Error::new(
1170 key.span(),
1171 "keyword argument repeated: `impl_deref`",
1172 ));
1173 }
1174 impl_deref = Some(stream.parse()?);
1175 } else if key == "protocol" {
1176 if protocol.is_some() {
1177 return Err(syn::Error::new(
1178 key.span(),
1179 "keyword argument repeated: `protocol`",
1180 ));
1181 }
1182 protocol = Some(stream.parse()?);
1183 } else {
1184 return Err(lookahead.error());
1185 }
1186 } else {
1187 let value = key_or_value;
1188 if use_key_and_value {
1189 return Err(syn::Error::new(
1190 value.span(),
1191 "positional argument follows keyword argument",
1192 ));
1193 }
1194 if arg_pos == 1 {
1195 struct_name = Some(value)
1196 } else {
1197 return Err(syn::Error::new(
1198 value.span(),
1199 "expected string literal",
1200 ));
1201 }
1202 }
1203 } else if lookahead.peek(LitStr) {
1204 if use_key_and_value {
1205 return Err(syn::Error::new(
1206 stream.span(),
1207 "If you use keyword arguments (e.g., `name` = \
1208 Something), then you can no longer use arguments \
1209 without a keyword.",
1210 ));
1211 }
1212 match arg_pos {
1213 1 => return Err(lookahead.error()),
1214 2 => prefix = Some(stream.parse()?),
1215 3 => encoding = Some(stream.parse()?),
1216 4 => fn_path = Some(stream.parse()?),
1217 _ => {
1218 return Err(syn::Error::new(
1219 stream.span(),
1220 "unexpected extra argument",
1221 ))
1222 }
1223 }
1224 } else {
1225 return Err(lookahead.error());
1226 }
1227
1228 if !stream.is_empty() {
1229 stream.parse::<Token![,]>()?;
1230 }
1231 }
1232
1233 let mut builtin_encoding = false;
1235 if let Some(encoding) = encoding {
1236 match encoding.value().to_lowercase().as_str() {
1237 "url" => {
1238 input = Some(type_from_ident(syn::parse_quote!(Url)));
1239 output = Some(type_from_ident(syn::parse_quote!(Json)));
1240 builtin_encoding = true;
1241 }
1242 "cbor" => {
1243 input = Some(type_from_ident(syn::parse_quote!(Cbor)));
1244 output = Some(type_from_ident(syn::parse_quote!(Cbor)));
1245 builtin_encoding = true;
1246 }
1247 "getcbor" => {
1248 input = Some(type_from_ident(syn::parse_quote!(GetUrl)));
1249 output = Some(type_from_ident(syn::parse_quote!(Cbor)));
1250 builtin_encoding = true;
1251 }
1252 "getjson" => {
1253 input = Some(type_from_ident(syn::parse_quote!(GetUrl)));
1254 output = Some(syn::parse_quote!(Json));
1255 builtin_encoding = true;
1256 }
1257 _ => {
1258 return Err(syn::Error::new(
1259 encoding.span(),
1260 "Encoding not found.",
1261 ))
1262 }
1263 }
1264 }
1265
1266 Ok(Self {
1267 struct_name,
1268 prefix,
1269 input,
1270 input_derive,
1271 output,
1272 fn_path,
1273 builtin_encoding,
1274 server,
1275 client,
1276 custom_wrapper,
1277 impl_from,
1278 impl_deref,
1279 protocol,
1280 })
1281 }
1282}
1283
1284#[derive(Debug, Clone)]
1286pub struct ServerFnArg {
1287 server_fn_attributes: Vec<Attribute>,
1289 arg: syn::PatType,
1291}
1292
1293impl ToTokens for ServerFnArg {
1294 fn to_tokens(&self, tokens: &mut TokenStream2) {
1295 let ServerFnArg { arg, .. } = self;
1296 tokens.extend(quote! {
1297 #arg
1298 });
1299 }
1300}
1301
1302impl Parse for ServerFnArg {
1303 fn parse(input: ParseStream) -> Result<Self> {
1304 let arg: syn::FnArg = input.parse()?;
1305 let mut arg = match arg {
1306 FnArg::Receiver(_) => {
1307 return Err(syn::Error::new(
1308 arg.span(),
1309 "cannot use receiver types in server function macro",
1310 ))
1311 }
1312 FnArg::Typed(t) => t,
1313 };
1314
1315 fn rename_path(path: Path, from_ident: Ident, to_ident: Ident) -> Path {
1316 if path.is_ident(&from_ident) {
1317 Path {
1318 leading_colon: None,
1319 segments: Punctuated::from_iter([PathSegment {
1320 ident: to_ident,
1321 arguments: PathArguments::None,
1322 }]),
1323 }
1324 } else {
1325 path
1326 }
1327 }
1328
1329 let server_fn_attributes = arg
1330 .attrs
1331 .iter()
1332 .cloned()
1333 .map(|attr| {
1334 if attr.path().is_ident("server") {
1335 let attr = Attribute {
1341 meta: match attr.meta {
1342 Meta::Path(path) => Meta::Path(rename_path(
1343 path,
1344 format_ident!("server"),
1345 format_ident!("serde"),
1346 )),
1347 Meta::List(mut list) => {
1348 list.path = rename_path(
1349 list.path,
1350 format_ident!("server"),
1351 format_ident!("serde"),
1352 );
1353 Meta::List(list)
1354 }
1355 Meta::NameValue(mut name_value) => {
1356 name_value.path = rename_path(
1357 name_value.path,
1358 format_ident!("server"),
1359 format_ident!("serde"),
1360 );
1361 Meta::NameValue(name_value)
1362 }
1363 },
1364 ..attr
1365 };
1366
1367 let args = attr.parse_args::<Meta>()?;
1368 match args {
1369 Meta::Path(path) if path.is_ident("default") => {
1371 Ok(attr.clone())
1372 }
1373 Meta::Path(path) if path.is_ident("flatten") => {
1375 Ok(attr.clone())
1376 }
1377 Meta::NameValue(name_value)
1379 if name_value.path.is_ident("default") =>
1380 {
1381 Ok(attr.clone())
1382 }
1383 Meta::Path(path) if path.is_ident("skip") => {
1385 Ok(attr.clone())
1386 }
1387 Meta::NameValue(name_value)
1389 if name_value.path.is_ident("rename") =>
1390 {
1391 Ok(attr.clone())
1392 }
1393 _ => Err(Error::new(
1394 attr.span(),
1395 "Unrecognized #[server] attribute, expected \
1396 #[server(default)] or #[server(rename = \
1397 \"fieldName\")]",
1398 )),
1399 }
1400 } else if attr.path().is_ident("doc") {
1401 Ok(attr.clone())
1403 } else if attr.path().is_ident("allow") {
1404 Ok(attr.clone())
1406 } else if attr.path().is_ident("deny") {
1407 Ok(attr.clone())
1409 } else if attr.path().is_ident("ignore") {
1410 Ok(attr.clone())
1412 } else {
1413 Err(Error::new(
1414 attr.span(),
1415 "Unrecognized attribute, expected #[server(...)]",
1416 ))
1417 }
1418 })
1419 .collect::<Result<Vec<_>>>()?;
1420 arg.attrs = vec![];
1421 Ok(ServerFnArg {
1422 arg,
1423 server_fn_attributes,
1424 })
1425 }
1426}
1427
1428#[derive(Debug, Clone)]
1430#[non_exhaustive]
1431pub struct ServerFnBody {
1432 pub attrs: Vec<Attribute>,
1434 pub vis: syn::Visibility,
1436 async_token: Token![async],
1437 fn_token: Token![fn],
1438 pub ident: Ident,
1440 pub generics: Generics,
1442 _paren_token: token::Paren,
1443 pub inputs: Punctuated<ServerFnArg, Token![,]>,
1445 output_arrow: Token![->],
1446 pub return_ty: syn::Type,
1448 pub output_ty: Option<syn::Type>,
1450 pub error_ty: Option<syn::Type>,
1452 pub error_ws_in_ty: Option<syn::Type>,
1454 pub error_ws_out_ty: Option<syn::Type>,
1456 pub block: TokenStream2,
1458 pub docs: Vec<(String, Span)>,
1460 pub middlewares: Vec<Middleware>,
1462}
1463
1464impl Parse for ServerFnBody {
1465 fn parse(input: ParseStream) -> Result<Self> {
1466 let mut attrs: Vec<Attribute> = input.call(Attribute::parse_outer)?;
1467
1468 let vis: Visibility = input.parse()?;
1469
1470 let async_token = input.parse()?;
1471
1472 let fn_token = input.parse()?;
1473 let ident = input.parse()?;
1474 let generics: Generics = input.parse()?;
1475
1476 let content;
1477 let _paren_token = syn::parenthesized!(content in input);
1478
1479 let inputs = syn::punctuated::Punctuated::parse_terminated(&content)?;
1480
1481 let output_arrow = input.parse()?;
1482 let return_ty = input.parse()?;
1483 let output_ty = output_type(&return_ty).cloned();
1484 let error_ty = err_type(&return_ty).cloned();
1485 let error_ws_in_ty = err_ws_in_type(&inputs);
1486 let error_ws_out_ty = err_ws_out_type(&output_ty)?;
1487
1488 let block = input.parse()?;
1489
1490 let docs = attrs
1491 .iter()
1492 .filter_map(|attr| {
1493 let Meta::NameValue(attr) = &attr.meta else {
1494 return None;
1495 };
1496 if !attr.path.is_ident("doc") {
1497 return None;
1498 }
1499
1500 let value = match &attr.value {
1501 syn::Expr::Lit(lit) => match &lit.lit {
1502 syn::Lit::Str(s) => Some(s.value()),
1503 _ => return None,
1504 },
1505 _ => return None,
1506 };
1507
1508 Some((value.unwrap_or_default(), attr.path.span()))
1509 })
1510 .collect();
1511 attrs.retain(|attr| {
1512 let Meta::NameValue(attr) = &attr.meta else {
1513 return true;
1514 };
1515 !attr.path.is_ident("doc")
1516 });
1517 let mut middlewares: Vec<Middleware> = vec![];
1519 attrs.retain(|attr| {
1520 if attr.meta.path().is_ident("middleware") {
1521 if let Ok(middleware) = attr.parse_args() {
1522 middlewares.push(middleware);
1523 false
1524 } else {
1525 true
1526 }
1527 } else {
1528 !(cfg!(feature = "ssr") && matches!(attr.meta.path().segments.last(), Some(PathSegment { ident, .. }) if ident == "lazy") )
1532 }
1533 });
1534
1535 Ok(Self {
1536 vis,
1537 async_token,
1538 fn_token,
1539 ident,
1540 generics,
1541 _paren_token,
1542 inputs,
1543 output_arrow,
1544 return_ty,
1545 output_ty,
1546 error_ty,
1547 error_ws_in_ty,
1548 error_ws_out_ty,
1549 block,
1550 attrs,
1551 docs,
1552 middlewares,
1553 })
1554 }
1555}
1556
1557impl ServerFnBody {
1558 fn to_dummy_ident(&self) -> Ident {
1559 Ident::new(&format!("__server_{}", self.ident), self.ident.span())
1560 }
1561
1562 fn to_dummy_output(&self) -> TokenStream2 {
1563 let ident = self.to_dummy_ident();
1564 let Self {
1565 attrs,
1566 vis,
1567 async_token,
1568 fn_token,
1569 generics,
1570 inputs,
1571 output_arrow,
1572 return_ty,
1573 block,
1574 ..
1575 } = &self;
1576 quote! {
1577 #[doc(hidden)]
1578 #(#attrs)*
1579 #vis #async_token #fn_token #ident #generics ( #inputs ) #output_arrow #return_ty
1580 #block
1581 }
1582 }
1583}