1
0

lib.rs 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. // TODO: Create README, uncomment this: #![doc = include_str!("../README.md")]
  2. #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")]
  3. #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")]
  4. //! This crate contains the dioxus implementation of the #[macro@crate::server] macro without additional context from the server.
  5. //! See the [server_fn_macro] crate for more information.
  6. use proc_macro::TokenStream;
  7. use server_fn_macro::server_macro_impl;
  8. use syn::__private::ToTokens;
  9. /// Declares that a function is a [server function](https://docs.rs/server_fn/).
  10. /// This means that its body will only run on the server, i.e., when the `ssr`
  11. /// feature is enabled on this crate.
  12. ///
  13. /// ## Usage
  14. /// ```rust,ignore
  15. /// # use dioxus::prelude::*;
  16. /// # #[derive(serde::Deserialize, serde::Serialize)]
  17. /// # pub struct BlogPost;
  18. /// # async fn load_posts(category: &str) -> Result<Vec<BlogPost>, ServerFnError> { unimplemented!() }
  19. /// #[server]
  20. /// pub async fn blog_posts(
  21. /// category: String,
  22. /// ) -> Result<Vec<BlogPost>, ServerFnError> {
  23. /// let posts = load_posts(&category).await?;
  24. /// // maybe do some other work
  25. /// Ok(posts)
  26. /// }
  27. /// ```
  28. ///
  29. /// ## Named Arguments
  30. ///
  31. /// You can any combination of the following named arguments:
  32. /// - `name`: sets the identifier for the server function’s type, which is a struct created
  33. /// to hold the arguments (defaults to the function identifier in PascalCase)
  34. /// - `prefix`: a prefix at which the server function handler will be mounted (defaults to `/api`)
  35. /// - `endpoint`: specifies the exact path at which the server function handler will be mounted,
  36. /// relative to the prefix (defaults to the function name followed by unique hash)
  37. /// - `input`: the encoding for the arguments (defaults to `PostUrl`)
  38. /// - `output`: the encoding for the response (defaults to `Json`)
  39. /// - `client`: a custom `Client` implementation that will be used for this server fn
  40. /// - `encoding`: (legacy, may be deprecated in future) specifies the encoding, which may be one
  41. /// of the following (not case sensitive)
  42. /// - `"Url"`: `POST` request with URL-encoded arguments and JSON response
  43. /// - `"GetUrl"`: `GET` request with URL-encoded arguments and JSON response
  44. /// - `"Cbor"`: `POST` request with CBOR-encoded arguments and response
  45. /// - `"GetCbor"`: `GET` request with URL-encoded arguments and CBOR response
  46. /// - `req` and `res` specify the HTTP request and response types to be used on the server (these
  47. /// should usually only be necessary if you are integrating with a server other than Actix/Axum)
  48. /// ```rust,ignore
  49. /// #[server(
  50. /// name = SomeStructName,
  51. /// prefix = "/my_api",
  52. /// endpoint = "my_fn",
  53. /// input = Cbor,
  54. /// output = Json
  55. /// )]
  56. /// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<usize, ServerFnError> {
  57. /// unimplemented!()
  58. /// }
  59. ///
  60. /// // expands to
  61. /// #[derive(Deserialize, Serialize)]
  62. /// struct SomeStructName {
  63. /// input: Vec<String>
  64. /// }
  65. ///
  66. /// impl ServerFn for SomeStructName {
  67. /// const PATH: &'static str = "/my_api/my_fn";
  68. ///
  69. /// // etc.
  70. /// }
  71. /// ```
  72. ///
  73. /// ## Adding layers to server functions
  74. ///
  75. /// Layers allow you to transform the request and response of a server function. You can use layers
  76. /// to add authentication, logging, or other functionality to your server functions. Server functions integrate
  77. /// with the tower ecosystem, so you can use any layer that is compatible with tower.
  78. ///
  79. /// Common layers include:
  80. /// - [`tower_http::trace::TraceLayer`](https://docs.rs/tower-http/latest/tower_http/trace/struct.TraceLayer.html) for tracing requests and responses
  81. /// - [`tower_http::compression::CompressionLayer`](https://docs.rs/tower-http/latest/tower_http/compression/struct.CompressionLayer.html) for compressing large responses
  82. /// - [`tower_http::cors::CorsLayer`](https://docs.rs/tower-http/latest/tower_http/cors/struct.CorsLayer.html) for adding CORS headers to responses
  83. /// - [`tower_http::timeout::TimeoutLayer`](https://docs.rs/tower-http/latest/tower_http/timeout/struct.TimeoutLayer.html) for adding timeouts to requests
  84. /// - [`tower_sessions::service::SessionManagerLayer`](https://docs.rs/tower-sessions/0.13.0/tower_sessions/service/struct.SessionManagerLayer.html) for adding session management to requests
  85. ///
  86. /// You can add a tower [`Layer`](https://docs.rs/tower/latest/tower/trait.Layer.html) to your server function with the middleware attribute:
  87. ///
  88. /// ```rust,ignore
  89. /// # use dioxus::prelude::*;
  90. /// #[server]
  91. /// // The TraceLayer will log all requests to the console
  92. /// #[middleware(tower_http::timeout::TimeoutLayer::new(std::time::Duration::from_secs(5)))]
  93. /// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<usize, ServerFnError> {
  94. /// unimplemented!()
  95. /// }
  96. /// ```
  97. ///
  98. /// ## Extracting additional data from requests
  99. ///
  100. /// Server functions automatically handle serialization and deserialization of arguments and responses.
  101. /// However, you may want to extract additional data from the request, such as the user's session or
  102. /// authentication information. You can do this with the `extract` function. This function returns any
  103. /// type that implements the [`FromRequestParts`](https://docs.rs/axum/latest/axum/extract/trait.FromRequestParts.html)
  104. /// trait:
  105. ///
  106. /// ```rust,ignore
  107. /// # use dioxus::prelude::*;
  108. /// #[server]
  109. /// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<String, ServerFnError> {
  110. /// let headers: axum::http::header::HeaderMap = extract().await?;
  111. /// Ok(format!("The server got a request with headers: {:?}", headers))
  112. /// }
  113. /// ```
  114. ///
  115. /// ## Sharing data with server functions
  116. ///
  117. /// You may need to share context with your server functions like a database pool. Server
  118. /// functions can access any context provided through the launch builder. You can access
  119. /// this context with the `FromContext` extractor:
  120. ///
  121. /// ```rust,ignore
  122. /// # use dioxus::prelude::*;
  123. /// # fn app() -> Element { unimplemented!() }
  124. /// #[derive(Clone, Copy, Debug)]
  125. /// struct DatabasePool;
  126. ///
  127. /// fn main() {
  128. /// LaunchBuilder::new()
  129. /// .with_context(server_only! {
  130. /// DatabasePool
  131. /// })
  132. /// .launch(app);
  133. /// }
  134. ///
  135. /// #[server]
  136. /// pub async fn my_wacky_server_fn(input: Vec<String>) -> Result<String, ServerFnError> {
  137. /// let FromContext(pool): FromContext<DatabasePool> = extract().await?;
  138. /// Ok(format!("The server read {:?} from the shared context", pool))
  139. /// }
  140. /// ```
  141. #[proc_macro_attribute]
  142. pub fn server(args: proc_macro::TokenStream, s: TokenStream) -> TokenStream {
  143. match server_macro_impl(
  144. args.into(),
  145. s.into(),
  146. Some(syn::parse_quote!(server_fn)),
  147. "/api",
  148. None,
  149. None,
  150. ) {
  151. Err(e) => e.to_compile_error().into(),
  152. Ok(s) => s.to_token_stream().into(),
  153. }
  154. }