1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
use crate::{HydrationCtx, IntoView};
use cfg_if::cfg_if;
use leptos_reactive::{signal_prelude::*, use_context, RwSignal};
use std::{collections::HashMap, error::Error, sync::Arc};
/// A struct to hold all the possible errors that could be provided by child Views
#[derive(Debug, Clone, Default)]
pub struct Errors(HashMap<ErrorKey, Arc<dyn Error + Send + Sync>>);
/// A unique key for an error that occurs at a particular location in the user interface.
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
pub struct ErrorKey(String);
impl<T> From<T> for ErrorKey
where
T: Into<String>,
{
fn from(key: T) -> ErrorKey {
ErrorKey(key.into())
}
}
impl IntoIterator for Errors {
type Item = (ErrorKey, Arc<dyn Error + Send + Sync>);
type IntoIter = IntoIter;
fn into_iter(self) -> Self::IntoIter {
IntoIter(self.0.into_iter())
}
}
/// An owning iterator over all the errors contained in the [Errors] struct.
pub struct IntoIter(
std::collections::hash_map::IntoIter<
ErrorKey,
Arc<dyn Error + Send + Sync>,
>,
);
impl Iterator for IntoIter {
type Item = (ErrorKey, Arc<dyn Error + Send + Sync>);
fn next(
&mut self,
) -> std::option::Option<<Self as std::iter::Iterator>::Item> {
self.0.next()
}
}
/// An iterator over all the errors contained in the [Errors] struct.
pub struct Iter<'a>(
std::collections::hash_map::Iter<
'a,
ErrorKey,
Arc<dyn Error + Send + Sync>,
>,
);
impl<'a> Iterator for Iter<'a> {
type Item = (&'a ErrorKey, &'a Arc<dyn Error + Send + Sync>);
fn next(
&mut self,
) -> std::option::Option<<Self as std::iter::Iterator>::Item> {
self.0.next()
}
}
impl<T, E> IntoView for Result<T, E>
where
T: IntoView + 'static,
E: Error + Send + Sync + 'static,
{
fn into_view(self, cx: leptos_reactive::Scope) -> crate::View {
let id = ErrorKey(HydrationCtx::peek().previous);
let errors = use_context::<RwSignal<Errors>>(cx);
match self {
Ok(stuff) => {
if let Some(errors) = errors {
errors.update(|errors| {
errors.0.remove(&id);
});
}
stuff.into_view(cx)
}
Err(error) => {
match errors {
Some(errors) => {
errors.update({
#[cfg(all(
target_arch = "wasm32",
feature = "web"
))]
let id = id.clone();
move |errors: &mut Errors| errors.insert(id, error)
});
// remove the error from the list if this drops,
// i.e., if it's in a DynChild that switches from Err to Ok
// Only can run on the client, will panic on the server
cfg_if! {
if #[cfg(all(target_arch = "wasm32", feature = "web"))] {
use leptos_reactive::{on_cleanup, queue_microtask};
on_cleanup(cx, move || {
queue_microtask(move || {
errors.update(|errors: &mut Errors| {
errors.remove(&id);
});
});
});
}
}
}
None => {
#[cfg(debug_assertions)]
warn!(
"No ErrorBoundary components found! Returning \
errors will not be handled and will silently \
disappear"
);
}
}
().into_view(cx)
}
}
}
}
impl Errors {
/// Returns `true` if there are no errors.
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// 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,
{
self.0.insert(key, Arc::new(error));
}
/// 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,
{
self.0.insert(Default::default(), Arc::new(error));
}
/// Remove an error to Errors that will be processed by `<ErrorBoundary/>`
pub fn remove(
&mut self,
key: &ErrorKey,
) -> Option<Arc<dyn Error + Send + Sync>> {
self.0.remove(key)
}
/// An iterator over all the errors, in arbitrary order.
pub fn iter(&self) -> Iter<'_> {
Iter(self.0.iter())
}
}