zng_var/
var_value.rs

1use std::{
2    any::{Any, TypeId},
3    fmt, ops,
4    sync::Arc,
5};
6
7use smallbox::*;
8
9/// Small box for [`AnyVarValue`] values.
10pub struct BoxAnyVarValue(SmallBox<dyn AnyVarValue, space::S4>);
11impl ops::Deref for BoxAnyVarValue {
12    type Target = dyn AnyVarValue;
13
14    fn deref(&self) -> &Self::Target {
15        &*self.0
16    }
17}
18impl ops::DerefMut for BoxAnyVarValue {
19    fn deref_mut(&mut self) -> &mut Self::Target {
20        &mut *self.0
21    }
22}
23impl BoxAnyVarValue {
24    /// Box `value`.
25    pub fn new(value: impl AnyVarValue) -> Self {
26        BoxAnyVarValue(smallbox!(value))
27    }
28
29    /// Downcast to value.
30    pub fn downcast<T: VarValue>(self) -> Result<T, Self> {
31        // Can't cast to `SmallBox<dyn Any>` in stable, so need to clone here for now.
32        match self.downcast_ref::<T>() {
33            Some(v) => Ok(v.clone()),
34            None => Err(self),
35        }
36    }
37
38    /// Gets the [`TypeId`] of the boxed value.
39    ///
40    /// Note that if you call [`Any::type_id`] directly on this type you will get the `BoxAnyVarValue` type id, not
41    /// the id of the actual value.
42    pub fn type_id(&self) -> TypeId {
43        (*self.0).type_id()
44    }
45
46    /// Alternate formatter that writes detailed debug info about the box and value type name.
47    ///
48    /// The `std::fmt::Debug` implementation simply redirects to the value debug,
49    /// this wrapper provides more info.
50    pub fn detailed_debug(&self) -> impl fmt::Debug {
51        struct DetailedDebug<'a>(&'a BoxAnyVarValue);
52        impl<'a> fmt::Debug for DetailedDebug<'a> {
53            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54                let mut b = f.debug_struct("BoxAnyVarValue");
55                b.field("value", &*self.0.0);
56                #[cfg(feature = "type_names")]
57                b.field("type_name()", &self.0.0.type_name());
58                #[cfg(not(feature = "type_names"))]
59                b.field("type_id()", &self.0.type_id());
60                b.field("is_heap()", &SmallBox::is_heap(&self.0.0));
61                b.finish()
62            }
63        }
64        DetailedDebug(self)
65    }
66}
67impl Clone for BoxAnyVarValue {
68    fn clone(&self) -> Self {
69        self.0.clone_boxed()
70    }
71}
72impl fmt::Debug for BoxAnyVarValue {
73    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74        fmt::Debug::fmt(&*self.0, f)
75    }
76}
77// don't implement PartialEq for BoxAnyVarValue,
78// that would cause it to become a AnyVarValue itself and cause all sort of issues
79
80/// Represents any variable value.
81///
82/// # Trait Alias
83///
84/// This trait is used like a type alias for traits and is
85/// already implemented for all types it can apply to.
86///
87/// See [`VarValue<T>`] for more details.
88pub trait AnyVarValue: fmt::Debug + Any + Send + Sync {
89    /// Clone the value.
90    fn clone_boxed(&self) -> BoxAnyVarValue;
91    /// Gets if `self` and `other` are equal.
92    fn eq_any(&self, other: &dyn AnyVarValue) -> bool;
93    /// Value type name.
94    ///
95    /// Note that this string is not stable and should be used for debug only.
96    #[cfg(feature = "type_names")]
97    fn type_name(&self) -> &'static str;
98
99    /// Swap value with `other` if both are of the same type.
100    fn try_swap(&mut self, other: &mut dyn AnyVarValue) -> bool;
101}
102impl dyn AnyVarValue {
103    /// Returns some reference to the inner value if it is of type `T`, or
104    /// `None` if it isn't.
105    pub fn downcast_ref<T: VarValue>(&self) -> Option<&T> {
106        let any: &dyn Any = self;
107        any.downcast_ref()
108    }
109
110    /// Returns some mutable reference to the inner value if it is of type `T`, or
111    /// `None` if it isn't.
112    pub fn downcast_mut<T: VarValue>(&mut self) -> Option<&mut T> {
113        let any: &mut dyn Any = self;
114        any.downcast_mut()
115    }
116
117    /// Returns `true` if the inner type is the same as `T`.
118    pub fn is<T: VarValue>(&self) -> bool {
119        let any: &dyn Any = self;
120        any.is::<T>()
121    }
122}
123impl PartialEq for dyn AnyVarValue {
124    fn eq(&self, other: &Self) -> bool {
125        self.eq_any(other)
126    }
127}
128impl<T> AnyVarValue for T
129where
130    T: fmt::Debug + PartialEq + Clone + Any + Send + Sync,
131{
132    fn clone_boxed(&self) -> BoxAnyVarValue {
133        BoxAnyVarValue::new(self.clone())
134    }
135
136    fn eq_any(&self, other: &dyn AnyVarValue) -> bool {
137        match other.downcast_ref::<T>() {
138            Some(o) => self == o,
139            None => false,
140        }
141    }
142
143    #[cfg(feature = "type_names")]
144    fn type_name(&self) -> &'static str {
145        std::any::type_name::<T>()
146    }
147
148    fn try_swap(&mut self, other: &mut dyn AnyVarValue) -> bool {
149        if let Some(other) = other.downcast_mut::<T>() {
150            std::mem::swap(self, other);
151            return true;
152        }
153        false
154    }
155}
156
157/// Represents a type that can be a [`Var<T>`] value.
158///
159/// # Trait Alias
160///
161/// This trait is used like a type alias for traits and is
162/// already implemented for all types it can apply to.
163///
164/// # Implementing
165///
166/// Types need to be `Debug + Clone + PartialEq + Send + Sync + Any` to auto-implement this trait,
167/// if you want to place an external type in a variable and it does not implement all the traits
168/// you may need to declare a *newtype* wrapper.
169///
170/// If the external type is at least `Debug + Send + Sync + Any` you can use the [`ArcEq<T>`] wrapper
171/// to quickly implement `Clone + PartialEq`, this is particularly useful for error types in [`ResponseVar<Result<_, E>>`].
172///
173/// If you want to use another variable as value use the [`VarEq<T>`] wrapper to use [`Var::var_eq`] as `PartialEq`.
174/// Vars are not allowed to be values directly as that causes type inference issues.
175///
176/// [`Var<T>`]: crate::Var
177/// [`ResponseVar<Result<_, E>>`]: crate::ResponseVar
178/// [`Var::var_eq`]: crate::Var::var_eq
179pub trait VarValue: AnyVarValue + Clone + PartialEq {}
180impl<T: AnyVarValue + Clone + PartialEq> VarValue for T {}
181
182/// Arc value that implements equality by pointer comparison.
183///
184/// This type allows external types that are only `Debug + Send + Sync` to become
185/// a full [`VarValue`] to be allowed as a variable value.
186pub struct ArcEq<T: fmt::Debug + Send + Sync>(pub Arc<T>);
187impl<T: fmt::Debug + Send + Sync> ops::Deref for ArcEq<T> {
188    type Target = Arc<T>;
189
190    fn deref(&self) -> &Self::Target {
191        &self.0
192    }
193}
194impl<T: fmt::Debug + Send + Sync> ArcEq<T> {
195    /// Constructs a new `ArcEq<T>`.
196    pub fn new(value: T) -> Self {
197        Self(Arc::new(value))
198    }
199}
200impl<T: fmt::Debug + Send + Sync> PartialEq for ArcEq<T> {
201    fn eq(&self, other: &Self) -> bool {
202        Arc::ptr_eq(&self.0, &other.0)
203    }
204}
205impl<T: fmt::Debug + Send + Sync> Eq for ArcEq<T> {}
206impl<T: fmt::Debug + Send + Sync> Clone for ArcEq<T> {
207    fn clone(&self) -> Self {
208        Self(Arc::clone(&self.0))
209    }
210}
211impl<T: fmt::Debug + Send + Sync> fmt::Debug for ArcEq<T> {
212    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213        fmt::Debug::fmt(&*self.0, f)
214    }
215}
216
217/// Represents a [`Var<T>`] as a value inside another variable.
218///
219/// Variable values must implement `PartialEq + Debug + Clone + Send + Sync + Any`. Variable types
220/// implement all of those except `PartialEq`, this type wraps a variable and adds equality using [`Var::var_eq`].
221///
222/// Variables cannot be be values directly because that breaks the [`IntoVar<T>`] blanket implementation for value types,
223/// as variables also implement `IntoVar<T>`. This could be solved with the *default impl* Rust feature, but it is not yet stable.
224/// This type is a workaround that limitation, it derefs to the wrapped var so it should require minimal refactoring as a drop-in replacement
225/// for `Var<T>` in struct fields.
226///
227/// [`Var<T>`]: crate::Var
228/// [`Var::var_eq`]: crate::Var::var_eq
229/// [`IntoVar<T>`]: crate::IntoVar
230#[derive(Clone)]
231pub struct VarEq<T: VarValue>(pub crate::Var<T>);
232impl<T: VarValue> ops::Deref for VarEq<T> {
233    type Target = crate::Var<T>;
234
235    fn deref(&self) -> &Self::Target {
236        &self.0
237    }
238}
239impl<T: VarValue> fmt::Debug for VarEq<T> {
240    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
241        fmt::Debug::fmt(&self.0, f)
242    }
243}
244impl<T: VarValue> PartialEq for VarEq<T> {
245    fn eq(&self, other: &Self) -> bool {
246        self.0.var_eq(&other.0)
247    }
248}
249
250/// A property value that is not a variable but can be inspected.
251///
252/// # Implementing
253///
254/// The trait is only auto-implemented for `T: Into<T> + VarValue`, unfortunately actual type conversions
255/// must be manually implemented, note that the [`impl_from_and_into_var!`] macro auto-implements this conversion.
256///
257/// [`impl_from_and_into_var!`]: crate::impl_from_and_into_var
258#[diagnostic::on_unimplemented(
259    note = "`IntoValue<T>` is implemented for all `T: VarValue`",
260    note = "you can use `impl_from_and_into_var!` to implement conversions"
261)]
262pub trait IntoValue<T: VarValue>: Into<T> {}
263impl<T: VarValue> IntoValue<T> for T {}