Compare commits
8 Commits
Author | SHA1 | Date |
---|---|---|
Greg Johnston | 1a7344ca50 | |
Greg Johnston | 921bb616b4 | |
Greg Johnston | 5bb7122b93 | |
Greg Johnston | ec2aa3e7a4 | |
Greg Johnston | 43959a96c7 | |
Greg Johnston | 7925fc4245 | |
Greg Johnston | 2bd1ad0f11 | |
Greg Johnston | dd730aa4ac |
|
@ -1,4 +1,4 @@
|
|||
use leptos::*;
|
||||
use leptos::{error::Result, *};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -8,30 +8,24 @@ pub struct Cat {
|
|||
}
|
||||
|
||||
#[derive(Error, Clone, Debug)]
|
||||
pub enum FetchError {
|
||||
pub enum CatError {
|
||||
#[error("Please request more than zero cats.")]
|
||||
NonZeroCats,
|
||||
#[error("Error loading data from serving.")]
|
||||
Request,
|
||||
#[error("Error deserializaing cat data from request.")]
|
||||
Json,
|
||||
}
|
||||
|
||||
type CatCount = usize;
|
||||
|
||||
async fn fetch_cats(count: CatCount) -> Result<Vec<String>, FetchError> {
|
||||
async fn fetch_cats(count: CatCount) -> Result<Vec<String>> {
|
||||
if count > 0 {
|
||||
// make the request
|
||||
let res = reqwasm::http::Request::get(&format!(
|
||||
"https://api.thecatapi.com/v1/images/search?limit={count}",
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
.map_err(|_| FetchError::Request)?
|
||||
.await?
|
||||
// convert it to JSON
|
||||
.json::<Vec<Cat>>()
|
||||
.await
|
||||
.map_err(|_| FetchError::Json)?
|
||||
.await?
|
||||
// extract the URL field for each cat
|
||||
.into_iter()
|
||||
.take(count)
|
||||
|
@ -39,7 +33,7 @@ async fn fetch_cats(count: CatCount) -> Result<Vec<String>, FetchError> {
|
|||
.collect::<Vec<_>>();
|
||||
Ok(res)
|
||||
} else {
|
||||
Err(FetchError::NonZeroCats)
|
||||
Err(CatError::NonZeroCats.into())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -163,12 +163,11 @@ pub async fn login(
|
|||
|
||||
let user: User = User::get_from_username(username, &pool)
|
||||
.await
|
||||
.ok_or("User does not exist.")
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
|
||||
.ok_or_else(|| {
|
||||
ServerFnError::ServerError("User does not exist.".into())
|
||||
})?;
|
||||
|
||||
match verify(password, &user.password)
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?
|
||||
{
|
||||
match verify(password, &user.password)? {
|
||||
true => {
|
||||
auth.login_user(user.id);
|
||||
auth.remember_user(remember.is_some());
|
||||
|
@ -204,13 +203,16 @@ pub async fn signup(
|
|||
.bind(username.clone())
|
||||
.bind(password_hashed)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
|
||||
.await?;
|
||||
|
||||
let user = User::get_from_username(username, &pool)
|
||||
.await
|
||||
.ok_or("Signup failed: User does not exist.")
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
|
||||
let user =
|
||||
User::get_from_username(username, &pool)
|
||||
.await
|
||||
.ok_or_else(|| {
|
||||
ServerFnError::ServerError(
|
||||
"Signup failed: User does not exist.".into(),
|
||||
)
|
||||
})?;
|
||||
|
||||
auth.login_user(user.id);
|
||||
auth.remember_user(remember.is_some());
|
||||
|
|
|
@ -21,14 +21,12 @@ if #[cfg(feature = "ssr")] {
|
|||
|
||||
pub fn pool(cx: Scope) -> Result<SqlitePool, ServerFnError> {
|
||||
use_context::<SqlitePool>(cx)
|
||||
.ok_or("Pool missing.")
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
||||
.ok_or_else(|| ServerFnError::ServerError("Pool missing.".into()))
|
||||
}
|
||||
|
||||
pub fn auth(cx: Scope) -> Result<AuthSession, ServerFnError> {
|
||||
use_context::<AuthSession>(cx)
|
||||
.ok_or("Auth session missing.")
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
||||
.ok_or_else(|| ServerFnError::ServerError("Auth session missing.".into()))
|
||||
}
|
||||
|
||||
#[derive(sqlx::FromRow, Clone)]
|
||||
|
@ -64,11 +62,7 @@ pub async fn get_todos(cx: Scope) -> Result<Vec<Todo>, ServerFnError> {
|
|||
let mut rows =
|
||||
sqlx::query_as::<_, SqlTodo>("SELECT * FROM todos").fetch(&pool);
|
||||
|
||||
while let Some(row) = rows
|
||||
.try_next()
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?
|
||||
{
|
||||
while let Some(row) = rows.try_next().await? {
|
||||
todos.push(row);
|
||||
}
|
||||
|
||||
|
@ -117,12 +111,11 @@ pub async fn add_todo(cx: Scope, title: String) -> Result<(), ServerFnError> {
|
|||
pub async fn delete_todo(cx: Scope, id: u16) -> Result<(), ServerFnError> {
|
||||
let pool = pool(cx)?;
|
||||
|
||||
sqlx::query("DELETE FROM todos WHERE id = $1")
|
||||
Ok(sqlx::query("DELETE FROM todos WHERE id = $1")
|
||||
.bind(id)
|
||||
.execute(&pool)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
||||
.map(|_| ())?)
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
|
|
@ -9,7 +9,7 @@ cfg_if! {
|
|||
use sqlx::{Connection, SqliteConnection};
|
||||
|
||||
pub async fn db() -> Result<SqliteConnection, ServerFnError> {
|
||||
SqliteConnection::connect("sqlite:Todos.db").await.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
||||
Ok(SqliteConnection::connect("sqlite:Todos.db").await?)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
||||
|
@ -43,11 +43,7 @@ pub async fn get_todos(cx: Scope) -> Result<Vec<Todo>, ServerFnError> {
|
|||
let mut todos = Vec::new();
|
||||
let mut rows =
|
||||
sqlx::query_as::<_, Todo>("SELECT * FROM todos").fetch(&mut conn);
|
||||
while let Some(row) = rows
|
||||
.try_next()
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?
|
||||
{
|
||||
while let Some(row) = rows.try_next().await? {
|
||||
todos.push(row);
|
||||
}
|
||||
|
||||
|
@ -76,12 +72,11 @@ pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
|
|||
pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
|
||||
let mut conn = db().await?;
|
||||
|
||||
sqlx::query("DELETE FROM todos WHERE id = $1")
|
||||
Ok(sqlx::query("DELETE FROM todos WHERE id = $1")
|
||||
.bind(id)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
||||
.map(|_| ())?)
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
|
|
@ -11,7 +11,7 @@ cfg_if! {
|
|||
// use http::{header::SET_COOKIE, HeaderMap, HeaderValue, StatusCode};
|
||||
|
||||
pub async fn db() -> Result<SqliteConnection, ServerFnError> {
|
||||
SqliteConnection::connect("sqlite:Todos.db").await.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
||||
Ok(SqliteConnection::connect("sqlite:Todos.db").await?)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
||||
|
@ -47,11 +47,7 @@ pub async fn get_todos(cx: Scope) -> Result<Vec<Todo>, ServerFnError> {
|
|||
let mut todos = Vec::new();
|
||||
let mut rows =
|
||||
sqlx::query_as::<_, Todo>("SELECT * FROM todos").fetch(&mut conn);
|
||||
while let Some(row) = rows
|
||||
.try_next()
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?
|
||||
{
|
||||
while let Some(row) = rows.try_next().await? {
|
||||
todos.push(row);
|
||||
}
|
||||
|
||||
|
@ -93,12 +89,11 @@ pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
|
|||
pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
|
||||
let mut conn = db().await?;
|
||||
|
||||
sqlx::query("DELETE FROM todos WHERE id = $1")
|
||||
Ok(sqlx::query("DELETE FROM todos WHERE id = $1")
|
||||
.bind(id)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
||||
.map(|_| ())?)
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
|
|
@ -11,7 +11,7 @@ cfg_if! {
|
|||
// use http::{header::SET_COOKIE, HeaderMap, HeaderValue, StatusCode};
|
||||
|
||||
pub async fn db() -> Result<SqliteConnection, ServerFnError> {
|
||||
SqliteConnection::connect("sqlite:Todos.db").await.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
||||
Ok(SqliteConnection::connect("sqlite:Todos.db").await?)
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
||||
|
@ -47,11 +47,7 @@ pub async fn get_todos(cx: Scope) -> Result<Vec<Todo>, ServerFnError> {
|
|||
let mut todos = Vec::new();
|
||||
let mut rows =
|
||||
sqlx::query_as::<_, Todo>("SELECT * FROM todos").fetch(&mut conn);
|
||||
while let Some(row) = rows
|
||||
.try_next()
|
||||
.await
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?
|
||||
{
|
||||
while let Some(row) = rows.try_next().await? {
|
||||
todos.push(row);
|
||||
}
|
||||
|
||||
|
@ -93,12 +89,11 @@ pub async fn add_todo(title: String) -> Result<(), ServerFnError> {
|
|||
pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
|
||||
let mut conn = db().await?;
|
||||
|
||||
sqlx::query("DELETE FROM todos WHERE id = $1")
|
||||
Ok(sqlx::query("DELETE FROM todos WHERE id = $1")
|
||||
.bind(id)
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))
|
||||
.map(|_| ())?)
|
||||
}
|
||||
|
||||
#[component]
|
||||
|
|
|
@ -171,6 +171,11 @@ pub use leptos_dom::{
|
|||
Class, CollectView, Errors, Fragment, HtmlElement, IntoAttribute,
|
||||
IntoClass, IntoProperty, IntoStyle, IntoView, NodeRef, Property, View,
|
||||
};
|
||||
|
||||
/// Types to make it easier to handle errors in your application.
|
||||
pub mod error {
|
||||
pub use server_fn::error::{Error, Result};
|
||||
}
|
||||
#[cfg(not(any(target_arch = "wasm32", feature = "template_macro")))]
|
||||
pub use leptos_macro::view as template;
|
||||
pub use leptos_macro::{component, server, slot, view, Params};
|
||||
|
@ -178,6 +183,7 @@ pub use leptos_reactive::*;
|
|||
pub use leptos_server::{
|
||||
self, create_action, create_multi_action, create_server_action,
|
||||
create_server_multi_action, Action, MultiAction, ServerFn, ServerFnError,
|
||||
ServerFnErrorErr,
|
||||
};
|
||||
pub use server_fn::{self, ServerFn as _};
|
||||
pub use typed_builder;
|
||||
|
|
|
@ -18,6 +18,7 @@ indexmap = "1.9"
|
|||
itertools = "0.10"
|
||||
js-sys = "0.3"
|
||||
leptos_reactive = { workspace = true }
|
||||
server_fn = { workspace = true }
|
||||
once_cell = "1"
|
||||
pad-adapter = "0.1"
|
||||
paste = "1"
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
use crate::{HydrationCtx, IntoView};
|
||||
use cfg_if::cfg_if;
|
||||
use leptos_reactive::{signal_prelude::*, use_context, RwSignal};
|
||||
use std::{borrow::Cow, collections::HashMap, error::Error, sync::Arc};
|
||||
use server_fn::error::Error;
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
/// A struct to hold all the possible errors that could be provided by child Views
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[repr(transparent)]
|
||||
pub struct Errors(HashMap<ErrorKey, Arc<dyn Error + Send + Sync>>);
|
||||
pub struct Errors(HashMap<ErrorKey, Error>);
|
||||
|
||||
/// A unique key for an error that occurs at a particular location in the user interface.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -24,7 +25,7 @@ where
|
|||
}
|
||||
|
||||
impl IntoIterator for Errors {
|
||||
type Item = (ErrorKey, Arc<dyn Error + Send + Sync>);
|
||||
type Item = (ErrorKey, Error);
|
||||
type IntoIter = IntoIter;
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -35,15 +36,10 @@ impl IntoIterator for Errors {
|
|||
|
||||
/// An owning iterator over all the errors contained in the [Errors] struct.
|
||||
#[repr(transparent)]
|
||||
pub struct IntoIter(
|
||||
std::collections::hash_map::IntoIter<
|
||||
ErrorKey,
|
||||
Arc<dyn Error + Send + Sync>,
|
||||
>,
|
||||
);
|
||||
pub struct IntoIter(std::collections::hash_map::IntoIter<ErrorKey, Error>);
|
||||
|
||||
impl Iterator for IntoIter {
|
||||
type Item = (ErrorKey, Arc<dyn Error + Send + Sync>);
|
||||
type Item = (ErrorKey, Error);
|
||||
|
||||
#[inline(always)]
|
||||
fn next(
|
||||
|
@ -55,16 +51,10 @@ impl Iterator for IntoIter {
|
|||
|
||||
/// An iterator over all the errors contained in the [Errors] struct.
|
||||
#[repr(transparent)]
|
||||
pub struct Iter<'a>(
|
||||
std::collections::hash_map::Iter<
|
||||
'a,
|
||||
ErrorKey,
|
||||
Arc<dyn Error + Send + Sync>,
|
||||
>,
|
||||
);
|
||||
pub struct Iter<'a>(std::collections::hash_map::Iter<'a, ErrorKey, Error>);
|
||||
|
||||
impl<'a> Iterator for Iter<'a> {
|
||||
type Item = (&'a ErrorKey, &'a Arc<dyn Error + Send + Sync>);
|
||||
type Item = (&'a ErrorKey, &'a Error);
|
||||
|
||||
#[inline(always)]
|
||||
fn next(
|
||||
|
@ -77,7 +67,7 @@ impl<'a> Iterator for Iter<'a> {
|
|||
impl<T, E> IntoView for Result<T, E>
|
||||
where
|
||||
T: IntoView + 'static,
|
||||
E: Error + Send + Sync + 'static,
|
||||
E: Into<Error>,
|
||||
{
|
||||
fn into_view(self, cx: leptos_reactive::Scope) -> crate::View {
|
||||
let id = ErrorKey(HydrationCtx::peek().fragment.to_string().into());
|
||||
|
@ -92,6 +82,7 @@ where
|
|||
stuff.into_view(cx)
|
||||
}
|
||||
Err(error) => {
|
||||
let error = error.into();
|
||||
match errors {
|
||||
Some(errors) => {
|
||||
errors.update({
|
||||
|
@ -133,6 +124,7 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Errors {
|
||||
/// Returns `true` if there are no errors.
|
||||
#[inline(always)]
|
||||
|
@ -143,24 +135,21 @@ impl Errors {
|
|||
/// Add an error to Errors that will be processed by `<ErrorBoundary/>`
|
||||
pub fn insert<E>(&mut self, key: ErrorKey, error: E)
|
||||
where
|
||||
E: Error + Send + Sync + 'static,
|
||||
E: Into<Error>,
|
||||
{
|
||||
self.0.insert(key, Arc::new(error));
|
||||
self.0.insert(key, error.into());
|
||||
}
|
||||
|
||||
/// Add an error with the default key for errors outside the reactive system
|
||||
pub fn insert_with_default_key<E>(&mut self, error: E)
|
||||
where
|
||||
E: Error + Send + Sync + 'static,
|
||||
E: Into<Error>,
|
||||
{
|
||||
self.0.insert(Default::default(), Arc::new(error));
|
||||
self.0.insert(Default::default(), error.into());
|
||||
}
|
||||
|
||||
/// Remove an error to Errors that will be processed by `<ErrorBoundary/>`
|
||||
pub fn remove(
|
||||
&mut self,
|
||||
key: &ErrorKey,
|
||||
) -> Option<Arc<dyn Error + Send + Sync>> {
|
||||
pub fn remove(&mut self, key: &ErrorKey) -> Option<Error> {
|
||||
self.0.remove(key)
|
||||
}
|
||||
|
||||
|
|
|
@ -116,7 +116,9 @@
|
|||
//! your app is not available.
|
||||
|
||||
use leptos_reactive::*;
|
||||
pub use server_fn::{Encoding, Payload, ServerFnError};
|
||||
pub use server_fn::{
|
||||
error::ServerFnErrorErr, Encoding, Payload, ServerFnError,
|
||||
};
|
||||
|
||||
mod action;
|
||||
mod multi_action;
|
||||
|
|
|
@ -386,7 +386,7 @@ where
|
|||
let e = ServerFnError::Request(e.to_string());
|
||||
value.try_set(Some(Err(e.clone())));
|
||||
if let Some(error) = error {
|
||||
error.try_set(Some(Box::new(e)));
|
||||
error.try_set(Some(Box::new(ServerFnErrorErr::from(e))));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -406,7 +406,8 @@ where
|
|||
cx.batch(move || {
|
||||
value.try_set(Some(Err(e.clone())));
|
||||
if let Some(error) = error {
|
||||
error.try_set(Some(Box::new(e)));
|
||||
error
|
||||
.try_set(Some(Box::new(ServerFnErrorErr::from(e))));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -472,7 +473,7 @@ where
|
|||
error!("{e:?}");
|
||||
if let Some(error) = error {
|
||||
error.try_set(Some(Box::new(
|
||||
ServerFnError::Request(
|
||||
ServerFnErrorErr::Request(
|
||||
e.as_string().unwrap_or_default(),
|
||||
),
|
||||
)));
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::{error, fmt, ops, sync::Arc};
|
||||
use thiserror::Error;
|
||||
|
||||
/// This is a result type into which any error can be converted,
|
||||
/// and which can be used directly in your `view`.
|
||||
///
|
||||
/// All errors will be stored as [`Error`].
|
||||
pub type Result<T, E = Error> = core::result::Result<T, E>;
|
||||
|
||||
/// A generic wrapper for any error.
|
||||
#[derive(Debug, Clone)]
|
||||
#[repr(transparent)]
|
||||
pub struct Error(Arc<dyn error::Error + Send + Sync>);
|
||||
|
||||
impl Error {
|
||||
/// Converts the wrapper into the inner reference-counted error.
|
||||
pub fn into_inner(self) -> Arc<dyn error::Error + Send + Sync> {
|
||||
Arc::clone(&self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Deref for Error {
|
||||
type Target = Arc<dyn error::Error + Send + Sync>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Error
|
||||
where
|
||||
T: std::error::Error + Send + Sync + 'static,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
Error(Arc::new(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ServerFnError> for Error {
|
||||
fn from(e: ServerFnError) -> Self {
|
||||
Error(Arc::new(ServerFnErrorErr::from(e)))
|
||||
}
|
||||
}
|
||||
|
||||
/// Type for errors that can occur when using server functions.
|
||||
///
|
||||
/// Unlike [`ServerFnErrorErr`], this does not implement [`std::error::Error`].
|
||||
/// This means that other error types can easily be converted into it using the
|
||||
/// `?` operator.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerFnError {
|
||||
/// Error while trying to register the server function (only occurs in case of poisoned RwLock).
|
||||
Registration(String),
|
||||
/// Occurs on the client if there is a network error while trying to run function on server.
|
||||
Request(String),
|
||||
/// Occurs when there is an error while actually running the function on the server.
|
||||
ServerError(String),
|
||||
/// Occurs on the client if there is an error deserializing the server's response.
|
||||
Deserialization(String),
|
||||
/// Occurs on the client if there is an error serializing the server function arguments.
|
||||
Serialization(String),
|
||||
/// Occurs on the server if there is an error deserializing one of the arguments that's been sent.
|
||||
Args(String),
|
||||
/// Occurs on the server if there's a missing argument.
|
||||
MissingArg(String),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ServerFnError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
ServerFnError::Registration(s) => format!(
|
||||
"error while trying to register the server function: {s}"
|
||||
),
|
||||
ServerFnError::Request(s) => format!(
|
||||
"error reaching server to call server function: {s}"
|
||||
),
|
||||
ServerFnError::ServerError(s) =>
|
||||
format!("error running server function: {s}"),
|
||||
ServerFnError::Deserialization(s) =>
|
||||
format!("error deserializing server function results: {s}"),
|
||||
ServerFnError::Serialization(s) =>
|
||||
format!("error serializing server function arguments: {s}"),
|
||||
ServerFnError::Args(s) => format!(
|
||||
"error deserializing server function arguments: {s}"
|
||||
),
|
||||
ServerFnError::MissingArg(s) => format!("missing argument {s}"),
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<E> for ServerFnError
|
||||
where
|
||||
E: std::error::Error,
|
||||
{
|
||||
fn from(e: E) -> Self {
|
||||
ServerFnError::ServerError(e.to_string())
|
||||
}
|
||||
}
|
||||
|
||||
/// Type for errors that can occur when using server functions.
|
||||
///
|
||||
/// Unlike [`ServerFnErrorErr`], this implements [`std::error::Error`]. This means
|
||||
/// it can be used in situations in which the `Error` trait is required, but it’s
|
||||
/// not possible to create a blanket implementation that converts other errors into
|
||||
/// this type.
|
||||
///
|
||||
/// [`ServerFnError`] and [`ServerFnErrorErr`] mutually implement [`From`], so
|
||||
/// it is easy to convert between the two types.
|
||||
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerFnErrorErr {
|
||||
/// Error while trying to register the server function (only occurs in case of poisoned RwLock).
|
||||
#[error("error while trying to register the server function: {0}")]
|
||||
Registration(String),
|
||||
/// Occurs on the client if there is a network error while trying to run function on server.
|
||||
#[error("error reaching server to call server function: {0}")]
|
||||
Request(String),
|
||||
/// Occurs when there is an error while actually running the function on the server.
|
||||
#[error("error running server function: {0}")]
|
||||
ServerError(String),
|
||||
/// Occurs on the client if there is an error deserializing the server's response.
|
||||
#[error("error deserializing server function results: {0}")]
|
||||
Deserialization(String),
|
||||
/// Occurs on the client if there is an error serializing the server function arguments.
|
||||
#[error("error serializing server function arguments: {0}")]
|
||||
Serialization(String),
|
||||
/// Occurs on the server if there is an error deserializing one of the arguments that's been sent.
|
||||
#[error("error deserializing server function arguments: {0}")]
|
||||
Args(String),
|
||||
/// Occurs on the server if there's a missing argument.
|
||||
#[error("missing argument {0}")]
|
||||
MissingArg(String),
|
||||
}
|
||||
|
||||
impl From<ServerFnError> for ServerFnErrorErr {
|
||||
fn from(value: ServerFnError) -> Self {
|
||||
match value {
|
||||
ServerFnError::Registration(value) => {
|
||||
ServerFnErrorErr::Registration(value)
|
||||
}
|
||||
ServerFnError::Request(value) => ServerFnErrorErr::Request(value),
|
||||
ServerFnError::ServerError(value) => {
|
||||
ServerFnErrorErr::ServerError(value)
|
||||
}
|
||||
ServerFnError::Deserialization(value) => {
|
||||
ServerFnErrorErr::Deserialization(value)
|
||||
}
|
||||
ServerFnError::Serialization(value) => {
|
||||
ServerFnErrorErr::Serialization(value)
|
||||
}
|
||||
ServerFnError::Args(value) => ServerFnErrorErr::Args(value),
|
||||
ServerFnError::MissingArg(value) => {
|
||||
ServerFnErrorErr::MissingArg(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -90,15 +90,17 @@ use quote::TokenStreamExt;
|
|||
// used by the macro
|
||||
#[doc(hidden)]
|
||||
pub use serde;
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
pub use server_fn_macro_default::server;
|
||||
use std::{future::Future, pin::Pin, str::FromStr};
|
||||
#[cfg(any(feature = "ssr", doc))]
|
||||
use syn::parse_quote;
|
||||
use thiserror::Error;
|
||||
// used by the macro
|
||||
#[doc(hidden)]
|
||||
pub use xxhash_rust;
|
||||
/// Error types used in server functions.
|
||||
pub mod error;
|
||||
pub use error::ServerFnError;
|
||||
|
||||
/// Default server function registry
|
||||
pub mod default;
|
||||
|
@ -451,32 +453,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Type for errors that can occur when using server functions.
|
||||
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerFnError {
|
||||
/// Error while trying to register the server function (only occurs in case of poisoned RwLock).
|
||||
#[error("error while trying to register the server function: {0}")]
|
||||
Registration(String),
|
||||
/// Occurs on the client if there is a network error while trying to run function on server.
|
||||
#[error("error reaching server to call server function: {0}")]
|
||||
Request(String),
|
||||
/// Occurs when there is an error while actually running the function on the server.
|
||||
#[error("error running server function: {0}")]
|
||||
ServerError(String),
|
||||
/// Occurs on the client if there is an error deserializing the server's response.
|
||||
#[error("error deserializing server function results {0}")]
|
||||
Deserialization(String),
|
||||
/// Occurs on the client if there is an error serializing the server function arguments.
|
||||
#[error("error serializing server function arguments {0}")]
|
||||
Serialization(String),
|
||||
/// Occurs on the server if there is an error deserializing one of the arguments that's been sent.
|
||||
#[error("error deserializing server function arguments {0}")]
|
||||
Args(String),
|
||||
/// Occurs on the server if there's a missing argument.
|
||||
#[error("missing argument {0}")]
|
||||
MissingArg(String),
|
||||
}
|
||||
|
||||
/// Executes the HTTP call to call a server function from the client, given its URL and argument type.
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub async fn call_server_fn<T, C: 'static>(
|
||||
|
@ -649,7 +625,7 @@ where
|
|||
// Lazily initialize the client to be reused for all server function calls.
|
||||
#[cfg(any(all(not(feature = "ssr"), not(target_arch = "wasm32")), doc))]
|
||||
static CLIENT: once_cell::sync::Lazy<reqwest::Client> =
|
||||
once_cell::sync::Lazy::new(|| reqwest::Client::new());
|
||||
once_cell::sync::Lazy::new(reqwest::Client::new);
|
||||
|
||||
#[cfg(any(all(not(feature = "ssr"), not(target_arch = "wasm32")), doc))]
|
||||
static ROOT_URL: once_cell::sync::OnceCell<&'static str> =
|
||||
|
|
Loading…
Reference in New Issue