server_fn/
error.rs

1use serde::{Deserialize, Serialize};
2use std::{
3    fmt,
4    fmt::{Display, Write},
5    str::FromStr,
6};
7use thiserror::Error;
8use throw_error::Error;
9use url::Url;
10
11/// A custom header that can be used to indicate a server function returned an error.
12pub const SERVER_FN_ERROR_HEADER: &str = "serverfnerror";
13
14impl From<ServerFnError> for Error {
15    fn from(e: ServerFnError) -> Self {
16        Error::from(ServerFnErrorErr::from(e))
17    }
18}
19
20/// An empty value indicating that there is no custom error type associated
21/// with this server function.
22#[derive(
23    Debug,
24    Deserialize,
25    Serialize,
26    PartialEq,
27    Eq,
28    Hash,
29    PartialOrd,
30    Ord,
31    Clone,
32    Copy,
33)]
34#[cfg_attr(
35    feature = "rkyv",
36    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
37)]
38pub struct NoCustomError;
39
40// Implement `Display` for `NoCustomError`
41impl fmt::Display for NoCustomError {
42    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43        write!(f, "Unit Type Displayed")
44    }
45}
46
47impl FromStr for NoCustomError {
48    type Err = ();
49
50    fn from_str(_s: &str) -> Result<Self, Self::Err> {
51        Ok(NoCustomError)
52    }
53}
54
55/// Wraps some error type, which may implement any of [`Error`](trait@std::error::Error), [`Clone`], or
56/// [`Display`].
57#[derive(Debug)]
58pub struct WrapError<T>(pub T);
59
60/// A helper macro to convert a variety of different types into `ServerFnError`.
61/// This should mostly be used if you are implementing `From<ServerFnError>` for `YourError`.
62#[macro_export]
63macro_rules! server_fn_error {
64    () => {{
65        use $crate::{ViaError, WrapError};
66        (&&&&&WrapError(())).to_server_error()
67    }};
68    ($err:expr) => {{
69        use $crate::error::{ViaError, WrapError};
70        match $err {
71            error => (&&&&&WrapError(error)).to_server_error(),
72        }
73    }};
74}
75
76/// This trait serves as the conversion method between a variety of types
77/// and [`ServerFnError`].
78pub trait ViaError<E> {
79    /// Converts something into an error.
80    fn to_server_error(&self) -> ServerFnError<E>;
81}
82
83// This impl should catch if you fed it a [`ServerFnError`] already.
84impl<E: ServerFnErrorKind + std::error::Error + Clone> ViaError<E>
85    for &&&&WrapError<ServerFnError<E>>
86{
87    fn to_server_error(&self) -> ServerFnError<E> {
88        self.0.clone()
89    }
90}
91
92// A type tag for ServerFnError so we can special case it
93pub(crate) trait ServerFnErrorKind {}
94
95impl ServerFnErrorKind for ServerFnError {}
96
97// This impl should catch passing () or nothing to server_fn_error
98impl ViaError<NoCustomError> for &&&WrapError<()> {
99    fn to_server_error(&self) -> ServerFnError {
100        ServerFnError::WrappedServerError(NoCustomError)
101    }
102}
103
104// This impl will catch any type that implements any type that impls
105// Error and Clone, so that it can be wrapped into ServerFnError
106impl<E: std::error::Error + Clone> ViaError<E> for &&WrapError<E> {
107    fn to_server_error(&self) -> ServerFnError<E> {
108        ServerFnError::WrappedServerError(self.0.clone())
109    }
110}
111
112// If it doesn't impl Error, but does impl Display and Clone,
113// we can still wrap it in String form
114impl<E: Display + Clone> ViaError<E> for &WrapError<E> {
115    fn to_server_error(&self) -> ServerFnError<E> {
116        ServerFnError::ServerError(self.0.to_string())
117    }
118}
119
120// This is what happens if someone tries to pass in something that does
121// not meet the above criteria
122impl<E> ViaError<E> for WrapError<E> {
123    #[track_caller]
124    fn to_server_error(&self) -> ServerFnError<E> {
125        panic!(
126            "At {}, you call `to_server_error()` or use  `server_fn_error!` \
127             with a value that does not implement `Clone` and either `Error` \
128             or `Display`.",
129            std::panic::Location::caller()
130        );
131    }
132}
133
134/// Type for errors that can occur when using server functions.
135///
136/// Unlike [`ServerFnErrorErr`], this does not implement [`Error`](trait@std::error::Error).
137/// This means that other error types can easily be converted into it using the
138/// `?` operator.
139#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
140#[cfg_attr(
141    feature = "rkyv",
142    derive(rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)
143)]
144pub enum ServerFnError<E = NoCustomError> {
145    /// A user-defined custom error type, which defaults to [`NoCustomError`].
146    WrappedServerError(E),
147    /// Error while trying to register the server function (only occurs in case of poisoned RwLock).
148    Registration(String),
149    /// Occurs on the client if there is a network error while trying to run function on server.
150    Request(String),
151    /// Occurs on the server if there is an error creating an HTTP response.
152    Response(String),
153    /// Occurs when there is an error while actually running the function on the server.
154    ServerError(String),
155    /// Occurs on the client if there is an error deserializing the server's response.
156    Deserialization(String),
157    /// Occurs on the client if there is an error serializing the server function arguments.
158    Serialization(String),
159    /// Occurs on the server if there is an error deserializing one of the arguments that's been sent.
160    Args(String),
161    /// Occurs on the server if there's a missing argument.
162    MissingArg(String),
163}
164
165impl ServerFnError<NoCustomError> {
166    /// Constructs a new [`ServerFnError::ServerError`] from some other type.
167    pub fn new(msg: impl ToString) -> Self {
168        Self::ServerError(msg.to_string())
169    }
170}
171
172impl<CustErr> From<CustErr> for ServerFnError<CustErr> {
173    fn from(value: CustErr) -> Self {
174        ServerFnError::WrappedServerError(value)
175    }
176}
177
178impl<E: std::error::Error> From<E> for ServerFnError {
179    fn from(value: E) -> Self {
180        ServerFnError::ServerError(value.to_string())
181    }
182}
183
184impl<CustErr> Display for ServerFnError<CustErr>
185where
186    CustErr: Display,
187{
188    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
189        write!(
190            f,
191            "{}",
192            match self {
193                ServerFnError::Registration(s) => format!(
194                    "error while trying to register the server function: {s}"
195                ),
196                ServerFnError::Request(s) => format!(
197                    "error reaching server to call server function: {s}"
198                ),
199                ServerFnError::ServerError(s) =>
200                    format!("error running server function: {s}"),
201                ServerFnError::Deserialization(s) =>
202                    format!("error deserializing server function results: {s}"),
203                ServerFnError::Serialization(s) =>
204                    format!("error serializing server function arguments: {s}"),
205                ServerFnError::Args(s) => format!(
206                    "error deserializing server function arguments: {s}"
207                ),
208                ServerFnError::MissingArg(s) => format!("missing argument {s}"),
209                ServerFnError::Response(s) =>
210                    format!("error generating HTTP response: {s}"),
211                ServerFnError::WrappedServerError(e) => format!("{e}"),
212            }
213        )
214    }
215}
216
217/// A serializable custom server function error type.
218///
219/// This is implemented for all types that implement [`FromStr`] + [`Display`].
220///
221/// This means you do not necessarily need the overhead of `serde` for a custom error type.
222/// Instead, you can use something like `strum` to derive `FromStr` and `Display` for your
223/// custom error type.
224///
225/// This is implemented for the default [`ServerFnError`], which uses [`NoCustomError`].
226pub trait ServerFnErrorSerde: Sized {
227    /// Converts the custom error type to a [`String`].
228    fn ser(&self) -> Result<String, std::fmt::Error>;
229
230    /// Deserializes the custom error type from a [`String`].
231    fn de(data: &str) -> Self;
232}
233
234impl<CustErr> ServerFnErrorSerde for ServerFnError<CustErr>
235where
236    CustErr: FromStr + Display,
237{
238    fn ser(&self) -> Result<String, std::fmt::Error> {
239        let mut buf = String::new();
240        match self {
241            ServerFnError::WrappedServerError(e) => {
242                write!(&mut buf, "WrappedServerFn|{e}")
243            }
244            ServerFnError::Registration(e) => {
245                write!(&mut buf, "Registration|{e}")
246            }
247            ServerFnError::Request(e) => write!(&mut buf, "Request|{e}"),
248            ServerFnError::Response(e) => write!(&mut buf, "Response|{e}"),
249            ServerFnError::ServerError(e) => {
250                write!(&mut buf, "ServerError|{e}")
251            }
252            ServerFnError::Deserialization(e) => {
253                write!(&mut buf, "Deserialization|{e}")
254            }
255            ServerFnError::Serialization(e) => {
256                write!(&mut buf, "Serialization|{e}")
257            }
258            ServerFnError::Args(e) => write!(&mut buf, "Args|{e}"),
259            ServerFnError::MissingArg(e) => {
260                write!(&mut buf, "MissingArg|{e}")
261            }
262        }?;
263        Ok(buf)
264    }
265
266    fn de(data: &str) -> Self {
267        data.split_once('|')
268            .and_then(|(ty, data)| match ty {
269                "WrappedServerFn" => match CustErr::from_str(data) {
270                    Ok(d) => Some(ServerFnError::WrappedServerError(d)),
271                    Err(_) => None,
272                },
273                "Registration" => {
274                    Some(ServerFnError::Registration(data.to_string()))
275                }
276                "Request" => Some(ServerFnError::Request(data.to_string())),
277                "Response" => Some(ServerFnError::Response(data.to_string())),
278                "ServerError" => {
279                    Some(ServerFnError::ServerError(data.to_string()))
280                }
281                "Deserialization" => {
282                    Some(ServerFnError::Deserialization(data.to_string()))
283                }
284                "Serialization" => {
285                    Some(ServerFnError::Serialization(data.to_string()))
286                }
287                "Args" => Some(ServerFnError::Args(data.to_string())),
288                "MissingArg" => {
289                    Some(ServerFnError::MissingArg(data.to_string()))
290                }
291                _ => None,
292            })
293            .unwrap_or_else(|| {
294                ServerFnError::Deserialization(format!(
295                    "Could not deserialize error {data:?}"
296                ))
297            })
298    }
299}
300
301impl<E> std::error::Error for ServerFnError<E>
302where
303    E: std::error::Error + 'static,
304    ServerFnError<E>: std::fmt::Display,
305{
306    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
307        match self {
308            ServerFnError::WrappedServerError(e) => Some(e),
309            _ => None,
310        }
311    }
312}
313
314/// Type for errors that can occur when using server functions.
315///
316/// Unlike [`ServerFnError`], this implements [`std::error::Error`]. This means
317/// it can be used in situations in which the `Error` trait is required, but it’s
318/// not possible to create a blanket implementation that converts other errors into
319/// this type.
320///
321/// [`ServerFnError`] and [`ServerFnErrorErr`] mutually implement [`From`], so
322/// it is easy to convert between the two types.
323#[derive(Error, Debug, Clone, PartialEq, Eq)]
324pub enum ServerFnErrorErr<E = NoCustomError> {
325    /// A user-defined custom error type, which defaults to [`NoCustomError`].
326    #[error("internal error: {0}")]
327    WrappedServerError(E),
328    /// Error while trying to register the server function (only occurs in case of poisoned RwLock).
329    #[error("error while trying to register the server function: {0}")]
330    Registration(String),
331    /// Occurs on the client if there is a network error while trying to run function on server.
332    #[error("error reaching server to call server function: {0}")]
333    Request(String),
334    /// Occurs when there is an error while actually running the function on the server.
335    #[error("error running server function: {0}")]
336    ServerError(String),
337    /// Occurs on the client if there is an error deserializing the server's response.
338    #[error("error deserializing server function results: {0}")]
339    Deserialization(String),
340    /// Occurs on the client if there is an error serializing the server function arguments.
341    #[error("error serializing server function arguments: {0}")]
342    Serialization(String),
343    /// Occurs on the server if there is an error deserializing one of the arguments that's been sent.
344    #[error("error deserializing server function arguments: {0}")]
345    Args(String),
346    /// Occurs on the server if there's a missing argument.
347    #[error("missing argument {0}")]
348    MissingArg(String),
349    /// Occurs on the server if there is an error creating an HTTP response.
350    #[error("error creating response {0}")]
351    Response(String),
352}
353
354impl<CustErr> From<ServerFnError<CustErr>> for ServerFnErrorErr<CustErr> {
355    fn from(value: ServerFnError<CustErr>) -> Self {
356        match value {
357            ServerFnError::Registration(value) => {
358                ServerFnErrorErr::Registration(value)
359            }
360            ServerFnError::Request(value) => ServerFnErrorErr::Request(value),
361            ServerFnError::ServerError(value) => {
362                ServerFnErrorErr::ServerError(value)
363            }
364            ServerFnError::Deserialization(value) => {
365                ServerFnErrorErr::Deserialization(value)
366            }
367            ServerFnError::Serialization(value) => {
368                ServerFnErrorErr::Serialization(value)
369            }
370            ServerFnError::Args(value) => ServerFnErrorErr::Args(value),
371            ServerFnError::MissingArg(value) => {
372                ServerFnErrorErr::MissingArg(value)
373            }
374            ServerFnError::WrappedServerError(value) => {
375                ServerFnErrorErr::WrappedServerError(value)
376            }
377            ServerFnError::Response(value) => ServerFnErrorErr::Response(value),
378        }
379    }
380}
381
382/// Associates a particular server function error with the server function
383/// found at a particular path.
384///
385/// This can be used to pass an error from the server back to the client
386/// without JavaScript/WASM supported, by encoding it in the URL as a query string.
387/// This is useful for progressive enhancement.
388#[derive(Debug)]
389pub struct ServerFnUrlError<CustErr> {
390    path: String,
391    error: ServerFnError<CustErr>,
392}
393
394impl<CustErr> ServerFnUrlError<CustErr> {
395    /// Creates a new structure associating the server function at some path
396    /// with a particular error.
397    pub fn new(path: impl Display, error: ServerFnError<CustErr>) -> Self {
398        Self {
399            path: path.to_string(),
400            error,
401        }
402    }
403
404    /// The error itself.
405    pub fn error(&self) -> &ServerFnError<CustErr> {
406        &self.error
407    }
408
409    /// The path of the server function that generated this error.
410    pub fn path(&self) -> &str {
411        &self.path
412    }
413
414    /// Adds an encoded form of this server function error to the given base URL.
415    pub fn to_url(&self, base: &str) -> Result<Url, url::ParseError>
416    where
417        CustErr: FromStr + Display,
418    {
419        let mut url = Url::parse(base)?;
420        url.query_pairs_mut()
421            .append_pair("__path", &self.path)
422            .append_pair(
423                "__err",
424                &ServerFnErrorSerde::ser(&self.error).unwrap_or_default(),
425            );
426        Ok(url)
427    }
428
429    /// Replaces any ServerFnUrlError info from the URL in the given string
430    /// with the serialized success value given.
431    pub fn strip_error_info(path: &mut String) {
432        if let Ok(mut url) = Url::parse(&*path) {
433            // NOTE: This is gross, but the Serializer you get from
434            // .query_pairs_mut() isn't an Iterator so you can't just .retain().
435            let pairs_previously = url
436                .query_pairs()
437                .map(|(k, v)| (k.to_string(), v.to_string()))
438                .collect::<Vec<_>>();
439            let mut pairs = url.query_pairs_mut();
440            pairs.clear();
441            for (key, value) in pairs_previously
442                .into_iter()
443                .filter(|(key, _)| key != "__path" && key != "__err")
444            {
445                pairs.append_pair(&key, &value);
446            }
447            drop(pairs);
448            *path = url.to_string();
449        }
450    }
451}
452
453impl<CustErr> From<ServerFnUrlError<CustErr>> for ServerFnError<CustErr> {
454    fn from(error: ServerFnUrlError<CustErr>) -> Self {
455        error.error
456    }
457}
458
459impl<CustErr> From<ServerFnUrlError<CustErr>> for ServerFnErrorErr<CustErr> {
460    fn from(error: ServerFnUrlError<CustErr>) -> Self {
461        error.error.into()
462    }
463}