use std::collections::{HashMap, HashSet};
use std::{
mem::MaybeUninit,
ops::{Deref, Index},
};
use crate::{ext_methods, MappedSignal, ReadSignal};
use dioxus_core::Subscribers;
use generational_box::{AnyStorage, UnsyncStorage};
#[allow(type_alias_bounds)]
pub type ReadableRef<'a, T: Readable, O = <T as Readable>::Target> =
<T::Storage as AnyStorage>::Ref<'a, O>;
pub trait Readable {
type Target: ?Sized;
type Storage: AnyStorage;
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
Self::Target: 'static;
fn try_peek_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
Self::Target: 'static;
fn subscribers(&self) -> Subscribers
where
Self::Target: 'static;
}
pub trait ReadableExt: Readable {
#[track_caller]
fn read(&self) -> ReadableRef<'_, Self>
where
Self::Target: 'static,
{
self.try_read().unwrap()
}
#[track_caller]
fn try_read(&self) -> Result<ReadableRef<'_, Self>, generational_box::BorrowError>
where
Self::Target: 'static,
{
self.try_read_unchecked()
.map(Self::Storage::downcast_lifetime_ref)
}
#[track_caller]
fn read_unchecked(&self) -> ReadableRef<'static, Self>
where
Self::Target: 'static,
{
self.try_read_unchecked().unwrap()
}
#[track_caller]
fn peek(&self) -> ReadableRef<'_, Self>
where
Self::Target: 'static,
{
Self::Storage::downcast_lifetime_ref(self.peek_unchecked())
}
#[track_caller]
fn try_peek(&self) -> Result<ReadableRef<'_, Self>, generational_box::BorrowError>
where
Self::Target: 'static,
{
self.try_peek_unchecked()
.map(Self::Storage::downcast_lifetime_ref)
}
#[track_caller]
fn peek_unchecked(&self) -> ReadableRef<'static, Self>
where
Self::Target: 'static,
{
self.try_peek_unchecked().unwrap()
}
fn map<F, O>(self, f: F) -> MappedSignal<O, Self, F>
where
Self: Clone + Sized,
F: Fn(&Self::Target) -> &O,
{
MappedSignal::new(self, f)
}
#[track_caller]
fn cloned(&self) -> Self::Target
where
Self::Target: Clone + 'static,
{
self.read().clone()
}
#[track_caller]
fn with<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O
where
Self::Target: 'static,
{
f(&*self.read())
}
#[track_caller]
fn with_peek<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O
where
Self::Target: 'static,
{
f(&*self.peek())
}
#[track_caller]
fn index<I>(
&self,
index: I,
) -> ReadableRef<'_, Self, <Self::Target as std::ops::Index<I>>::Output>
where
Self::Target: std::ops::Index<I> + 'static,
{
<Self::Storage as AnyStorage>::map(self.read(), |v| v.index(index))
}
#[doc(hidden)]
unsafe fn deref_impl<'a>(&self) -> &'a dyn Fn() -> Self::Target
where
Self: Sized + 'a,
Self::Target: Clone + 'static,
{
let uninit_callable = MaybeUninit::<Self>::uninit();
let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
let size_of_closure = std::mem::size_of_val(&uninit_closure);
assert_eq!(size_of_closure, std::mem::size_of::<Self>());
fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
b
}
let reference_to_closure = cast_lifetime(
{
&uninit_closure
},
#[allow(clippy::missing_transmute_annotations)]
unsafe {
std::mem::transmute(self)
},
);
reference_to_closure as &_
}
}
impl<R: Readable + ?Sized> ReadableExt for R {}
pub trait ReadableBoxExt: Readable<Storage = UnsyncStorage> {
fn boxed(self) -> ReadSignal<Self::Target>
where
Self: Sized + 'static,
{
ReadSignal::new(self)
}
}
impl<R: Readable<Storage = UnsyncStorage> + ?Sized> ReadableBoxExt for R {}
pub trait ReadableVecExt<T>: Readable<Target = Vec<T>> {
#[track_caller]
fn len(&self) -> usize
where
T: 'static,
{
self.with(|v| v.len())
}
#[track_caller]
fn is_empty(&self) -> bool
where
T: 'static,
{
self.with(|v| v.is_empty())
}
#[track_caller]
fn first(&self) -> Option<ReadableRef<'_, Self, T>>
where
T: 'static,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.first())
}
#[track_caller]
fn last(&self) -> Option<ReadableRef<'_, Self, T>>
where
T: 'static,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.last())
}
#[track_caller]
fn get(&self, index: usize) -> Option<ReadableRef<'_, Self, T>>
where
T: 'static,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.get(index))
}
#[track_caller]
fn iter(&self) -> ReadableValueIterator<'_, Self>
where
Self: Sized,
{
ReadableValueIterator {
index: 0,
value: self,
}
}
}
pub struct ReadableValueIterator<'a, R> {
index: usize,
value: &'a R,
}
impl<'a, T: 'static, R: Readable<Target = Vec<T>>> Iterator for ReadableValueIterator<'a, R> {
type Item = ReadableRef<'a, R, T>;
fn next(&mut self) -> Option<Self::Item> {
let index = self.index;
self.index += 1;
self.value.get(index)
}
}
impl<T, R> ReadableVecExt<T> for R where R: Readable<Target = Vec<T>> {}
pub trait ReadableOptionExt<T>: Readable<Target = Option<T>> {
#[track_caller]
fn unwrap(&self) -> T
where
T: Clone + 'static,
{
self.as_ref().unwrap().clone()
}
#[track_caller]
fn as_ref(&self) -> Option<ReadableRef<'_, Self, T>>
where
T: 'static,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.as_ref())
}
}
impl<T, R> ReadableOptionExt<T> for R where R: Readable<Target = Option<T>> {}
pub trait ReadableResultExt<T, E>: Readable<Target = Result<T, E>> {
#[track_caller]
fn unwrap(&self) -> T
where
T: Clone + 'static,
E: 'static,
{
self.as_ref()
.unwrap_or_else(|_| panic!("Tried to unwrap a Result that was an error"))
.clone()
}
#[track_caller]
fn as_ref(&self) -> Result<ReadableRef<'_, Self, T>, ReadableRef<'_, Self, E>>
where
T: 'static,
E: 'static,
{
let read = self.read();
match read.deref() {
Ok(_) => Ok(<Self::Storage as AnyStorage>::map(read, |v| {
v.as_ref()
.ok()
.expect("Result variant changed between read and map")
})),
Err(_) => Err(<Self::Storage as AnyStorage>::map(read, |v| {
v.as_ref()
.err()
.expect("Result variant changed between read and map")
})),
}
}
}
impl<T, E, R> ReadableResultExt<T, E> for R where R: Readable<Target = Result<T, E>> {}
pub trait ReadableStringExt: Readable<Target = String> {
ext_methods! {
fn capacity(&self) -> usize = String::capacity;
}
}
impl<W> ReadableStringExt for W where W: Readable<Target = String> {}
pub trait ReadableStrExt: Readable<Target: Deref<Target = str> + 'static> {
ext_methods! {
fn is_empty(&self) -> bool = |s: &Self::Target| s.deref().is_empty();
fn len(&self) -> usize = |s: &Self::Target| s.deref().len();
fn contains(&self, pat: &str) -> bool = |s: &Self::Target, pat| s.deref().contains(pat);
}
}
impl<W> ReadableStrExt for W where W: Readable<Target: Deref<Target = str> + 'static> {}
pub trait ReadableHashMapExt<K: 'static, V: 'static, H: 'static>:
Readable<Target = HashMap<K, V, H>>
{
ext_methods! {
fn is_empty(&self) -> bool = HashMap::is_empty;
fn len(&self) -> usize = HashMap::len;
fn capacity(&self) -> usize = HashMap::capacity;
}
#[track_caller]
fn get(&self, key: &K) -> Option<ReadableRef<'_, Self, V>>
where
K: std::hash::Hash + Eq,
H: std::hash::BuildHasher,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.get(key))
}
#[track_caller]
fn contains_key(&self, key: &K) -> bool
where
K: std::hash::Hash + Eq,
H: std::hash::BuildHasher,
{
self.with(|v| v.contains_key(key))
}
}
impl<K: 'static, V: 'static, H: 'static, R> ReadableHashMapExt<K, V, H> for R where
R: Readable<Target = HashMap<K, V, H>>
{
}
pub trait ReadableHashSetExt<V: 'static, H: 'static>: Readable<Target = HashSet<V, H>> {
ext_methods! {
fn is_empty(&self) -> bool = HashSet::is_empty;
fn len(&self) -> usize = HashSet::len;
fn capacity(&self) -> usize = HashSet::capacity;
}
#[track_caller]
fn contains(&self, value: &V) -> bool
where
V: std::hash::Hash + Eq,
H: std::hash::BuildHasher,
{
self.with(|v| v.contains(value))
}
}
impl<V: 'static, H: 'static, R> ReadableHashSetExt<V, H> for R where
R: Readable<Target = HashSet<V, H>>
{
}