From f610c6b8c11bc0f9ef52936d2830d41e66b2f3f4 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Fri, 20 Jun 2025 20:37:51 -0500 Subject: [PATCH] Implement Into for the default server function error type (#4205) * implement Into for the server function error type * document the server function error type * fix tests --- examples/fullstack-auth/src/auth.rs | 2 +- examples/fullstack-auth/src/main.rs | 21 +- examples/fullstack-desktop/src/main.rs | 18 +- examples/fullstack-hello-world/src/main.rs | 14 +- examples/fullstack-router/src/main.rs | 34 +-- examples/fullstack-streaming/src/main.rs | 14 +- examples/fullstack-websockets/src/main.rs | 2 +- packages/fullstack/README.md | 4 +- packages/fullstack/docs/request_origin.md | 2 +- packages/fullstack/src/error.rs | 221 ++++++++++++++++++ packages/fullstack/src/lib.rs | 7 +- .../default-features-disabled/src/main.rs | 2 +- .../playwright-tests/fullstack/src/main.rs | 23 +- .../nested-suspense/src/lib.rs | 2 +- .../nested-suspense/src/ssg.rs | 2 +- packages/server-macro/src/lib.rs | 12 +- packages/server/docs/request_origin.md | 2 +- packages/server/src/config.rs | 6 +- packages/server/src/context.rs | 22 +- packages/server/src/lib.rs | 2 +- 20 files changed, 316 insertions(+), 96 deletions(-) create mode 100644 packages/fullstack/src/error.rs diff --git a/examples/fullstack-auth/src/auth.rs b/examples/fullstack-auth/src/auth.rs index a11ef7053..3c3c11c4e 100644 --- a/examples/fullstack-auth/src/auth.rs +++ b/examples/fullstack-auth/src/auth.rs @@ -196,7 +196,7 @@ pub async fn connect_to_database() -> SqlitePool { pub type Session = axum_session_auth::AuthSession; -pub async fn get_session() -> Result { +pub async fn get_session() -> ServerFnResult { extract::() .await .map_err(|_| ServerFnError::new("AuthSessionLayer was not found")) diff --git a/examples/fullstack-auth/src/main.rs b/examples/fullstack-auth/src/main.rs index d38a5a900..3f5189b82 100644 --- a/examples/fullstack-auth/src/main.rs +++ b/examples/fullstack-auth/src/main.rs @@ -75,7 +75,8 @@ fn app() -> Element { div { button { onclick: move |_| { async move { - login().await.unwrap(); + login().await?; + Ok(()) } }, "Login Test User" @@ -84,9 +85,9 @@ fn app() -> Element { div { button { onclick: move |_| async move { - if let Ok(data) = get_user_name().await { - user_name.set(data); - } + let data = get_user_name().await?; + user_name.set(data); + Ok(()) }, "Get User Name" } @@ -95,9 +96,9 @@ fn app() -> Element { div { button { onclick: move |_| async move { - if let Ok(data) = get_permissions().await { - permissions.set(data); - } + let data = get_permissions().await?; + permissions.set(data); + Ok(()) }, "Get Permissions" } @@ -107,20 +108,20 @@ fn app() -> Element { } #[server] -pub async fn get_user_name() -> Result { +pub async fn get_user_name() -> ServerFnResult { let auth = auth::get_session().await?; Ok(auth.current_user.unwrap().username.to_string()) } #[server] -pub async fn login() -> Result<(), ServerFnError> { +pub async fn login() -> ServerFnResult { let auth = auth::get_session().await?; auth.login_user(2); Ok(()) } #[server] -pub async fn get_permissions() -> Result { +pub async fn get_permissions() -> ServerFnResult { let method: axum::http::Method = extract().await?; let auth = auth::get_session().await?; let current_user = auth.current_user.clone().unwrap_or_default(); diff --git a/examples/fullstack-desktop/src/main.rs b/examples/fullstack-desktop/src/main.rs index f56451f19..6e21d8fc8 100644 --- a/examples/fullstack-desktop/src/main.rs +++ b/examples/fullstack-desktop/src/main.rs @@ -18,11 +18,11 @@ pub fn app() -> Element { button { onclick: move |_| count -= 1, "Down low!" } button { onclick: move |_| async move { - if let Ok(data) = get_server_data().await { - println!("Client received: {}", data); - text.set(data.clone()); - post_server_data(data).await.unwrap(); - } + let data = get_server_data().await?; + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await?; + Ok(()) }, "Run a server function" } @@ -30,14 +30,14 @@ pub fn app() -> Element { } } -#[server(PostServerData)] -async fn post_server_data(data: String) -> Result<(), ServerFnError> { +#[server] +async fn post_server_data(data: String) -> ServerFnResult { println!("Server received: {}", data); Ok(()) } -#[server(GetServerData)] -async fn get_server_data() -> Result { +#[server] +async fn get_server_data() -> ServerFnResult { Ok("Hello from the server!".to_string()) } diff --git a/examples/fullstack-hello-world/src/main.rs b/examples/fullstack-hello-world/src/main.rs index 1eaeb0563..d86a0d891 100644 --- a/examples/fullstack-hello-world/src/main.rs +++ b/examples/fullstack-hello-world/src/main.rs @@ -20,11 +20,11 @@ fn app() -> Element { button { onclick: move |_| count -= 1, "Down low!" } button { onclick: move |_| async move { - if let Ok(data) = get_server_data().await { - println!("Client received: {}", data); - text.set(data.clone()); - post_server_data(data).await.unwrap(); - } + let data = get_server_data().await?; + println!("Client received: {}", data); + text.set(data.clone()); + post_server_data(data).await?; + Ok(()) }, "Run a server function!" } @@ -33,14 +33,14 @@ fn app() -> Element { } #[server] -async fn post_server_data(data: String) -> Result<(), ServerFnError> { +async fn post_server_data(data: String) -> ServerFnResult { println!("Server received: {}", data); Ok(()) } #[server] -async fn get_server_data() -> Result { +async fn get_server_data() -> ServerFnResult { Ok(reqwest::get("https://httpbin.org/ip").await?.text().await?) } diff --git a/examples/fullstack-router/src/main.rs b/examples/fullstack-router/src/main.rs index 70a9898cf..baec38c83 100644 --- a/examples/fullstack-router/src/main.rs +++ b/examples/fullstack-router/src/main.rs @@ -52,34 +52,34 @@ fn Home() -> Element { let mut text = use_signal(|| "...".to_string()); rsx! { - Link { to: Route::Blog { id: count() }, "Go to blog" } - div { - h1 { "High-Five counter: {count}" } - button { onclick: move |_| count += 1, "Up high!" } - button { onclick: move |_| count -= 1, "Down low!" } - button { - onclick: move |_| async move { - if let Ok(data) = get_server_data().await { + Link { to: Route::Blog { id: count() }, "Go to blog" } + div { + h1 { "High-Five counter: {count}" } + button { onclick: move |_| count += 1, "Up high!" } + button { onclick: move |_| count -= 1, "Down low!" } + button { + onclick: move |_| async move { + let data = get_server_data().await?; println!("Client received: {}", data); text.set(data.clone()); - post_server_data(data).await.unwrap(); - } - }, - "Run server function!" + post_server_data(data).await?; + Ok(()) + }, + "Run server function!" + } + "Server said: {text}" } - "Server said: {text}" - } - } + } } #[server(PostServerData)] -async fn post_server_data(data: String) -> Result<(), ServerFnError> { +async fn post_server_data(data: String) -> ServerFnResult { println!("Server received: {}", data); Ok(()) } #[server(GetServerData)] -async fn get_server_data() -> Result { +async fn get_server_data() -> ServerFnResult { Ok("Hello from the server!".to_string()) } diff --git a/examples/fullstack-streaming/src/main.rs b/examples/fullstack-streaming/src/main.rs index 041ff2228..48a0a33e8 100644 --- a/examples/fullstack-streaming/src/main.rs +++ b/examples/fullstack-streaming/src/main.rs @@ -9,13 +9,13 @@ fn app() -> Element { button { onclick: move |_| async move { response.write().clear(); - if let Ok(stream) = test_stream().await { - response.write().push_str("Stream started\n"); - let mut stream = stream.into_inner(); - while let Some(Ok(text)) = stream.next().await { - response.write().push_str(&text); - } + let stream = test_stream().await?; + response.write().push_str("Stream started\n"); + let mut stream = stream.into_inner(); + while let Some(Ok(text)) = stream.next().await { + response.write().push_str(&text); } + Ok(()) }, "Start stream" } @@ -24,7 +24,7 @@ fn app() -> Element { } #[server(output = StreamingText)] -pub async fn test_stream() -> Result { +pub async fn test_stream() -> ServerFnResult> { let (tx, rx) = futures::channel::mpsc::unbounded(); tokio::spawn(async move { loop { diff --git a/examples/fullstack-websockets/src/main.rs b/examples/fullstack-websockets/src/main.rs index fc755f739..414d1a034 100644 --- a/examples/fullstack-websockets/src/main.rs +++ b/examples/fullstack-websockets/src/main.rs @@ -45,7 +45,7 @@ fn app() -> Element { #[server(protocol = Websocket)] async fn uppercase_ws( input: BoxedStream, -) -> Result, ServerFnError> { +) -> ServerFnResult> { let mut input = input; // Create a channel with the output of the websocket diff --git a/packages/fullstack/README.md b/packages/fullstack/README.md index aaadd1a4b..a4a8b3771 100644 --- a/packages/fullstack/README.md +++ b/packages/fullstack/README.md @@ -58,7 +58,7 @@ fn App() -> Element { } #[server] -async fn get_meaning(of: String) -> Result, ServerFnError> { +async fn get_meaning(of: String) -> ServerFnResult> { Ok(of.contains("life").then(|| 42)) } ``` @@ -131,7 +131,7 @@ fn App() -> Element { } #[server] -async fn get_meaning(of: String) -> Result, ServerFnError> { +async fn get_meaning(of: String) -> ServerFnResult> { Ok(of.contains("life").then(|| 42)) } ``` diff --git a/packages/fullstack/docs/request_origin.md b/packages/fullstack/docs/request_origin.md index fc6da9ab7..bdd57dfc8 100644 --- a/packages/fullstack/docs/request_origin.md +++ b/packages/fullstack/docs/request_origin.md @@ -23,7 +23,7 @@ fn PrintHtmlRequestInfo() -> Element { ```rust # use dioxus::prelude::*; #[server] -async fn read_headers() -> Result<(), ServerFnError> { +async fn read_headers() -> ServerFnResult { // Since we are calling this from a server function, the server context that is may be from the // initial request or a request from the client let context = server_context(); diff --git a/packages/fullstack/src/error.rs b/packages/fullstack/src/error.rs new file mode 100644 index 000000000..bf1fa2b2c --- /dev/null +++ b/packages/fullstack/src/error.rs @@ -0,0 +1,221 @@ +use std::{ + error::Error, + fmt::{Debug, Display}, + str::FromStr, +}; + +use dioxus_lib::prelude::{dioxus_core::CapturedError, RenderError}; +use serde::{de::DeserializeOwned, Serialize}; +use server_fn::{ + codec::JsonEncoding, + error::{FromServerFnError, ServerFnErrorErr}, +}; + +/// A default result type for server functions, which can either be successful or contain an error. The [`ServerFnResult`] type +/// is a convenient alias for a `Result` type that uses [`ServerFnError`] as the error type. +/// +/// # Example +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[server] +/// async fn parse_number(number: String) -> ServerFnResult { +/// let parsed_number: f32 = number.parse()?; +/// Ok(parsed_number) +/// } +/// ``` +pub type ServerFnResult = std::result::Result>; + +/// An error type for server functions. This may either be an error that occurred while running the server +/// function logic, or an error that occurred while communicating with the server inside the server function crate. +/// +/// ## Usage +/// +/// You can use the [`ServerFnError`] type in the Error type of your server function result or use the [`ServerFnResult`] +/// type as the return type of your server function. When you call the server function, you can handle the error directly +/// or convert it into a [`CapturedError`] to throw into the nearest [`ErrorBoundary`](dioxus_lib::prelude::ErrorBoundary). +/// +/// ```rust +/// use dioxus::prelude::*; +/// +/// #[server] +/// async fn parse_number(number: String) -> ServerFnResult { +/// // You can convert any error type into the `ServerFnError` with the `?` operator +/// let parsed_number: f32 = number.parse()?; +/// Ok(parsed_number) +/// } +/// +/// #[component] +/// fn ParseNumberServer() -> Element { +/// let mut number = use_signal(|| "42".to_string()); +/// let mut parsed = use_signal(|| None); +/// +/// rsx! { +/// input { +/// value: "{number}", +/// oninput: move |e| number.set(e.value()), +/// } +/// button { +/// onclick: move |_| async move { +/// // Call the server function to parse the number +/// // If the result is Ok, continue running the closure, otherwise bubble up the +/// // error to the nearest error boundary with `?` +/// let result = parse_number(number()).await?; +/// parsed.set(Some(result)); +/// Ok(()) +/// }, +/// "Parse Number" +/// } +/// if let Some(value) = parsed() { +/// p { "Parsed number: {value}" } +/// } else { +/// p { "No number parsed yet." } +/// } +/// } +/// } +/// ``` +/// +/// ## Differences from [`CapturedError`] +/// +/// Both this error type and [`CapturedError`] can be used to represent boxed errors in dioxus. However, this error type +/// is more strict about the kinds of errors it can represent. [`CapturedError`] can represent any error that implements +/// the [`Error`] trait or can be converted to a string. [`CapturedError`] holds onto the type information of the error +/// and lets you downcast the error to its original type. +/// +/// [`ServerFnError`] represents server function errors as [`String`]s by default without any additional type information. +/// This makes it easy to serialize the error to JSON and send it over the wire, but it means that you can't get the +/// original type information of the error back. If you need to preserve the type information of the error, you can use a +/// [custom error variant](#custom-error-variants) that holds onto the type information. +/// +/// ## Custom error variants +/// +/// The [`ServerFnError`] type accepts a generic type parameter `T` that is used to represent the error type used for server +/// functions. If you need to keep the type information of your error, you can create a custom error variant that implements +/// [`Serialize`] and [`DeserializeOwned`]. This allows you to serialize the error to JSON and send it over the wire, +/// while still preserving the type information. +/// +/// ```rust +/// use dioxus::prelude::*; +/// use serde::{Deserialize, Serialize}; +/// use std::fmt::Debug; +/// +/// #[derive(Clone, Debug, Serialize, Deserialize)] +/// pub struct MyCustomError { +/// message: String, +/// code: u32, +/// } +/// +/// impl MyCustomError { +/// pub fn new(message: String, code: u32) -> Self { +/// Self { message, code } +/// } +/// } +/// +/// #[server] +/// async fn server_function() -> ServerFnResult { +/// // Return your custom error +/// Err(ServerFnError::ServerError(MyCustomError::new( +/// "An error occurred".to_string(), +/// 404, +/// ))) +/// } +/// ``` +#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +pub enum ServerFnError { + /// An error running the server function + ServerError(T), + + /// An error communicating with the server + CommunicationError(ServerFnErrorErr), +} + +impl ServerFnError { + /// Creates a new `ServerFnError` from something that implements `ToString`. + /// + /// # Examples + /// ```rust + /// use dioxus::prelude::*; + /// + /// #[server] + /// async fn server_function() -> ServerFnResult { + /// // Return your custom error + /// Err(ServerFnError::new("Something went wrong")) + /// } + /// ``` + pub fn new(error: impl ToString) -> Self { + Self::ServerError(error.to_string()) + } +} + +impl ServerFnError { + /// Returns true if the error is a server error + pub fn is_server_error(&self) -> bool { + matches!(self, ServerFnError::ServerError(_)) + } + + /// Returns true if the error is a communication error + pub fn is_communication_error(&self) -> bool { + matches!(self, ServerFnError::CommunicationError(_)) + } + + /// Returns a reference to the server error if it is a server error + /// or `None` if it is a communication error. + pub fn server_error(&self) -> Option<&T> { + match self { + ServerFnError::ServerError(err) => Some(err), + ServerFnError::CommunicationError(_) => None, + } + } + + /// Returns a reference to the communication error if it is a communication error + /// or `None` if it is a server error. + pub fn communication_error(&self) -> Option<&ServerFnErrorErr> { + match self { + ServerFnError::ServerError(_) => None, + ServerFnError::CommunicationError(err) => Some(err), + } + } +} + +impl FromServerFnError for ServerFnError { + type Encoder = JsonEncoding; + + fn from_server_fn_error(err: ServerFnErrorErr) -> Self { + Self::CommunicationError(err) + } +} + +impl FromStr for ServerFnError { + type Err = ::Err; + + fn from_str(s: &str) -> std::result::Result { + std::result::Result::Ok(Self::ServerError(T::from_str(s)?)) + } +} + +impl Display for ServerFnError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ServerFnError::ServerError(err) => write!(f, "Server error: {}", err), + ServerFnError::CommunicationError(err) => write!(f, "Communication error: {}", err), + } + } +} + +impl From for CapturedError { + fn from(error: ServerFnError) -> Self { + Self::from_display(error) + } +} + +impl From for RenderError { + fn from(error: ServerFnError) -> Self { + RenderError::Aborted(CapturedError::from(error)) + } +} + +impl From for ServerFnError { + fn from(error: E) -> Self { + Self::ServerError(error.to_string()) + } +} diff --git a/packages/fullstack/src/lib.rs b/packages/fullstack/src/lib.rs index 5726dd91c..8c0e7843d 100644 --- a/packages/fullstack/src/lib.rs +++ b/packages/fullstack/src/lib.rs @@ -1,12 +1,14 @@ #![doc = include_str!("../README.md")] #![doc(html_logo_url = "https://avatars.githubusercontent.com/u/79236386")] #![doc(html_favicon_url = "https://avatars.githubusercontent.com/u/79236386")] -#![deny(missing_docs)] +#![warn(missing_docs)] #![cfg_attr(docsrs, feature(doc_cfg))] #[cfg(all(feature = "web", feature = "document"))] mod web; +mod error; + #[cfg(all(feature = "web", feature = "document"))] pub use web::FullstackWebDocument; @@ -19,8 +21,9 @@ pub use dioxus_fullstack_hooks::history::FullstackHistory; pub mod prelude { pub use dioxus_fullstack_hooks::prelude::*; + pub use crate::error::{ServerFnError, ServerFnResult}; pub use dioxus_server_macro::*; - pub use server_fn::{self, ServerFn as _, ServerFnError}; + pub use server_fn::{self, ServerFn as _}; #[cfg(feature = "server")] pub use dioxus_server::prelude::*; diff --git a/packages/playwright-tests/default-features-disabled/src/main.rs b/packages/playwright-tests/default-features-disabled/src/main.rs index 990bee2c0..ded9a7e7a 100644 --- a/packages/playwright-tests/default-features-disabled/src/main.rs +++ b/packages/playwright-tests/default-features-disabled/src/main.rs @@ -44,6 +44,6 @@ fn current_platform_features() -> Vec { } #[server] -async fn get_server_features() -> Result, ServerFnError> { +async fn get_server_features() -> ServerFnResult> { Ok(current_platform_features()) } diff --git a/packages/playwright-tests/fullstack/src/main.rs b/packages/playwright-tests/fullstack/src/main.rs index 89124c163..856a180c2 100644 --- a/packages/playwright-tests/fullstack/src/main.rs +++ b/packages/playwright-tests/fullstack/src/main.rs @@ -5,12 +5,9 @@ // - Hydration #![allow(non_snake_case)] -use dioxus::{ - prelude::{ - server_fn::{codec::JsonEncoding, BoxedStream, Websocket}, - *, - }, - CapturedError, +use dioxus::prelude::{ + server_fn::{codec::JsonEncoding, BoxedStream, Websocket}, + *, }; use futures::{channel::mpsc, SinkExt, StreamExt}; @@ -85,7 +82,7 @@ async fn assert_server_context_provided() { } #[server(PostServerData)] -async fn post_server_data(data: String) -> Result<(), ServerFnError> { +async fn post_server_data(data: String) -> ServerFnResult { assert_server_context_provided().await; println!("Server received: {}", data); @@ -93,7 +90,7 @@ async fn post_server_data(data: String) -> Result<(), ServerFnError> { } #[server(GetServerData)] -async fn get_server_data() -> Result { +async fn get_server_data() -> ServerFnResult { assert_server_context_provided().await; Ok("Hello from the server!".to_string()) } @@ -101,14 +98,14 @@ async fn get_server_data() -> Result { // Make sure the default codec work with empty data structures // Regression test for https://github.com/DioxusLabs/dioxus/issues/2628 #[server] -async fn get_server_data_empty_vec(empty_vec: Vec) -> Result, ServerFnError> { +async fn get_server_data_empty_vec(empty_vec: Vec) -> ServerFnResult> { assert_server_context_provided().await; assert!(empty_vec.is_empty()); Ok(Vec::new()) } #[server] -async fn server_error() -> Result { +async fn server_error() -> ServerFnResult { assert_server_context_provided().await; tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await; Err(ServerFnError::new("the server threw an error!")) @@ -142,9 +139,7 @@ fn Errors() -> Element { #[component] pub fn ThrowsError() -> Element { - use_server_future(server_error)? - .unwrap() - .map_err(CapturedError::from_display)?; + use_server_future(server_error)?.unwrap()?; rsx! { "success" } @@ -187,7 +182,7 @@ fn Assets() -> Element { #[server(protocol = Websocket)] async fn echo_ws( input: BoxedStream, -) -> Result, ServerFnError> { +) -> ServerFnResult> { let mut input = input; let (mut tx, rx) = mpsc::channel(1); diff --git a/packages/playwright-tests/nested-suspense/src/lib.rs b/packages/playwright-tests/nested-suspense/src/lib.rs index 7df137d47..263fd26e0 100644 --- a/packages/playwright-tests/nested-suspense/src/lib.rs +++ b/packages/playwright-tests/nested-suspense/src/lib.rs @@ -83,7 +83,7 @@ pub struct Content { } #[server] -async fn server_content(id: usize) -> Result { +async fn server_content(id: usize) -> ServerFnResult { let content_tree = [ Content { title: "The robot says hello world".to_string(), diff --git a/packages/playwright-tests/nested-suspense/src/ssg.rs b/packages/playwright-tests/nested-suspense/src/ssg.rs index 2f41115cb..ce74bb1c9 100644 --- a/packages/playwright-tests/nested-suspense/src/ssg.rs +++ b/packages/playwright-tests/nested-suspense/src/ssg.rs @@ -25,6 +25,6 @@ fn main() { } #[server(endpoint = "static_routes")] -async fn static_routes() -> Result, ServerFnError> { +async fn static_routes() -> ServerFnResult> { Ok(vec!["/".to_string()]) } diff --git a/packages/server-macro/src/lib.rs b/packages/server-macro/src/lib.rs index 3f1c665ba..9ccc66c79 100644 --- a/packages/server-macro/src/lib.rs +++ b/packages/server-macro/src/lib.rs @@ -18,11 +18,11 @@ use syn::{__private::ToTokens, parse_quote}; /// # use dioxus::prelude::*; /// # #[derive(serde::Deserialize, serde::Serialize)] /// # pub struct BlogPost; -/// # async fn load_posts(category: &str) -> Result, ServerFnError> { unimplemented!() } +/// # async fn load_posts(category: &str) -> ServerFnResult> { unimplemented!() } /// #[server] /// pub async fn blog_posts( /// category: String, -/// ) -> Result, ServerFnError> { +/// ) -> ServerFnResult> { /// let posts = load_posts(&category).await?; /// // maybe do some other work /// Ok(posts) @@ -118,7 +118,7 @@ use syn::{__private::ToTokens, parse_quote}; /// input = Cbor, /// output = Json /// )] -/// pub async fn my_wacky_server_fn(input: Vec) -> Result { +/// pub async fn my_wacky_server_fn(input: Vec) -> ServerFnResult { /// unimplemented!() /// } /// @@ -155,7 +155,7 @@ use syn::{__private::ToTokens, parse_quote}; /// #[server] /// // The TraceLayer will log all requests to the console /// #[middleware(tower_http::timeout::TimeoutLayer::new(std::time::Duration::from_secs(5)))] -/// pub async fn my_wacky_server_fn(input: Vec) -> Result { +/// pub async fn my_wacky_server_fn(input: Vec) -> ServerFnResult { /// unimplemented!() /// } /// ``` @@ -171,7 +171,7 @@ use syn::{__private::ToTokens, parse_quote}; /// ```rust,ignore /// # use dioxus::prelude::*; /// #[server] -/// pub async fn my_wacky_server_fn(input: Vec) -> Result { +/// pub async fn my_wacky_server_fn(input: Vec) -> ServerFnResult { /// let headers: axum::http::header::HeaderMap = extract().await?; /// Ok(format!("The server got a request with headers: {:?}", headers)) /// } @@ -198,7 +198,7 @@ use syn::{__private::ToTokens, parse_quote}; /// } /// /// #[server] -/// pub async fn my_wacky_server_fn(input: Vec) -> Result { +/// pub async fn my_wacky_server_fn(input: Vec) -> ServerFnResult { /// let FromContext(pool): FromContext = extract().await?; /// Ok(format!("The server read {:?} from the shared context", pool)) /// } diff --git a/packages/server/docs/request_origin.md b/packages/server/docs/request_origin.md index 1e9532f62..a56372514 100644 --- a/packages/server/docs/request_origin.md +++ b/packages/server/docs/request_origin.md @@ -23,7 +23,7 @@ fn PrintHtmlRequestInfo() -> Element { ```rust # use dioxus::prelude::*; #[server] -async fn read_headers() -> Result<(), ServerFnError> { +async fn read_headers() -> ServerFnResult { // Since we are calling this from a server function, the server context that is may be from the // initial request or a request from the client let context = server_context(); diff --git a/packages/server/src/config.rs b/packages/server/src/config.rs index 4a6fdfc37..d81e42750 100644 --- a/packages/server/src/config.rs +++ b/packages/server/src/config.rs @@ -157,7 +157,7 @@ impl ServeConfigBuilder { /// } /// /// #[server] - /// async fn read_context() -> Result { + /// async fn read_context() -> ServerFnResult { /// // You can extract values from the server context with the `extract` function /// let FromContext(value) = extract().await?; /// Ok(value) @@ -224,7 +224,7 @@ impl ServeConfigBuilder { /// } /// /// #[server] - /// async fn read_context() -> Result { + /// async fn read_context() -> ServerFnResult { /// // You can extract values from the server context with the `extract` function /// let FromContext(value) = extract().await?; /// Ok(value) @@ -284,7 +284,7 @@ impl ServeConfigBuilder { /// } /// /// #[server] - /// async fn read_context() -> Result { + /// async fn read_context() -> ServerFnResult { /// // You can extract values from the server context with the `extract` function /// let FromContext(value) = extract().await?; /// Ok(value) diff --git a/packages/server/src/context.rs b/packages/server/src/context.rs index 3b51f73b4..4e11fff7a 100644 --- a/packages/server/src/context.rs +++ b/packages/server/src/context.rs @@ -48,7 +48,7 @@ impl AtomicResponsePartsModified { /// ```rust, no_run /// # use dioxus::prelude::*; /// #[server] -/// async fn read_headers() -> Result<(), ServerFnError> { +/// async fn read_headers() -> ServerFnResult { /// let server_context = server_context(); /// let headers: http::HeaderMap = server_context.extract().await?; /// println!("{:?}", headers); @@ -141,7 +141,7 @@ mod server_fn_impl { /// .launch(app); /// /// #[server] - /// async fn read_context() -> Result { + /// async fn read_context() -> ServerFnResult { /// // You can extract values from the server context with the `extract` function /// let FromContext(value) = extract().await?; /// Ok(value) @@ -201,7 +201,7 @@ mod server_fn_impl { /// ```rust, no_run /// # use dioxus::prelude::*; /// #[server] - /// async fn set_headers() -> Result<(), ServerFnError> { + /// async fn set_headers() -> ServerFnResult { /// let server_context = server_context(); /// let response_parts = server_context.response_parts(); /// let cookies = response_parts @@ -225,7 +225,7 @@ mod server_fn_impl { /// ```rust, no_run /// # use dioxus::prelude::*; /// #[server] - /// async fn set_headers() -> Result<(), ServerFnError> { + /// async fn set_headers() -> ServerFnResult { /// let server_context = server_context(); /// server_context.headers_mut() /// .insert("Cookie", http::HeaderValue::from_static("dioxus=fullstack")); @@ -247,7 +247,7 @@ mod server_fn_impl { /// ```rust, no_run /// # use dioxus::prelude::*; /// #[server] - /// async fn set_status() -> Result<(), ServerFnError> { + /// async fn set_status() -> ServerFnResult { /// let server_context = server_context(); /// *server_context.status_mut() = http::StatusCode::INTERNAL_SERVER_ERROR; /// Ok(()) @@ -268,7 +268,7 @@ mod server_fn_impl { /// ```rust, no_run /// # use dioxus::prelude::*; /// #[server] - /// async fn set_version() -> Result<(), ServerFnError> { + /// async fn set_version() -> ServerFnResult { /// let server_context = server_context(); /// *server_context.version_mut() = http::Version::HTTP_2; /// Ok(()) @@ -289,7 +289,7 @@ mod server_fn_impl { /// ```rust, no_run /// # use dioxus::prelude::*; /// #[server] - /// async fn set_version() -> Result<(), ServerFnError> { + /// async fn set_version() -> ServerFnResult { /// let server_context = server_context(); /// *server_context.version_mut() = http::Version::HTTP_2; /// Ok(()) @@ -323,7 +323,7 @@ mod server_fn_impl { /// ```rust, no_run /// # use dioxus::prelude::*; /// #[server] - /// async fn read_headers() -> Result<(), ServerFnError> { + /// async fn read_headers() -> ServerFnResult { /// let server_context = server_context(); /// let request_parts = server_context.request_parts(); /// let id: &i32 = request_parts @@ -347,7 +347,7 @@ mod server_fn_impl { /// ```rust, no_run /// # use dioxus::prelude::*; /// #[server] - /// async fn read_headers() -> Result<(), ServerFnError> { + /// async fn read_headers() -> ServerFnResult { /// let server_context = server_context(); /// let id: i32 = server_context.request_parts_mut() /// .extensions @@ -370,7 +370,7 @@ mod server_fn_impl { /// ```rust, no_run /// # use dioxus::prelude::*; /// #[server] - /// async fn read_headers() -> Result<(), ServerFnError> { + /// async fn read_headers() -> ServerFnResult { /// let server_context = server_context(); /// let headers: http::HeaderMap = server_context.extract().await?; /// println!("{:?}", headers); @@ -526,7 +526,7 @@ impl std::error::Error for NotFoundInServerContext {} /// .launch(app); /// /// #[server] -/// async fn read_context() -> Result { +/// async fn read_context() -> ServerFnResult { /// // You can extract values from the server context with the `extract` function /// let FromContext(value) = extract().await?; /// Ok(value) diff --git a/packages/server/src/lib.rs b/packages/server/src/lib.rs index 723ff4866..0d87d3dc9 100644 --- a/packages/server/src/lib.rs +++ b/packages/server/src/lib.rs @@ -50,7 +50,7 @@ //! } //! //! #[server(GetServerData)] -//! async fn get_server_data() -> Result { +//! async fn get_server_data() -> ServerFnResult { //! Ok("Hello from the server!".to_string()) //! } //! ```