use crate::CopyValue; use crate::Signal; use crate::SignalData; use dioxus_core::ScopeId; use generational_box::Mappable; use generational_box::Storage; use std::fmt::Debug; use std::fmt::Display; /// A read only signal that has been mapped to a new type. pub struct MappedSignal { origin_scope: ScopeId, mapping: CopyValue U>>, } impl MappedSignal<()> { /// Create a new mapped signal. pub fn new>, U: ?Sized>( signal: Signal, mapping: impl Fn(&T) -> &U + 'static, ) -> MappedSignal< <<>>::Ref as Mappable>>::Mapped< T, > as generational_box::Mappable>::Mapped, > { MappedSignal { origin_scope: signal.origin_scope(), mapping: CopyValue::new(Box::new(move || { <<>>::Ref as Mappable>>::Mapped>::map( signal.read(), &mapping, ) })), } } } impl MappedSignal { /// Get the scope that the signal was created in. pub fn origin_scope(&self) -> ScopeId { self.origin_scope } /// Get the current value of the signal. This will subscribe the current scope to the signal. pub fn read(&self) -> U { (self.mapping.read())() } /// Run a closure with a reference to the signal's value. pub fn with(&self, f: impl FnOnce(U) -> O) -> O { f(self.read()) } } impl MappedSignal { /// Get the current value of the signal. This will subscribe the current scope to the signal. pub fn value(&self) -> U { self.read().clone() } } impl PartialEq for MappedSignal { fn eq(&self, other: &Self) -> bool { self.mapping == other.mapping } } impl std::clone::Clone for MappedSignal { fn clone(&self) -> Self { *self } } impl Copy for MappedSignal {} impl Display for MappedSignal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.with(|v| Display::fmt(&v, f)) } } impl Debug for MappedSignal { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.with(|v| Debug::fmt(&v, f)) } } impl std::ops::Deref for MappedSignal { type Target = dyn Fn() -> T; fn deref(&self) -> &Self::Target { // https://github.com/dtolnay/case-studies/tree/master/callable-types // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit). let uninit_callable = std::mem::MaybeUninit::::uninit(); // Then move that value into the closure. We assume that the closure now has a in memory layout of Self. let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }); // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure. let size_of_closure = std::mem::size_of_val(&uninit_closure); assert_eq!(size_of_closure, std::mem::size_of::()); // Then cast the lifetime of the closure to the lifetime of &self. fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T { b } let reference_to_closure = cast_lifetime( { // The real closure that we will never use. &uninit_closure }, // We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self. unsafe { std::mem::transmute(self) }, ); // Cast the closure to a trait object. reference_to_closure as &Self::Target } }