Skip to main content

cordis/
value.rs

1//! Cloneable, dynamically typed values used for services, events, and config.
2
3use crate::{CordisError, ErrorCode, Result};
4use std::any::{Any, type_name};
5use std::fmt::{self, Debug, Formatter};
6use std::sync::Arc;
7
8/// A cloneable type-erased `Send + Sync` value.
9///
10/// Cordis TypeScript stores arbitrary JavaScript values.  This wrapper is the
11/// Rust equivalent: callers recover the concrete type through [`Value::downcast`].
12#[derive(Clone)]
13pub struct Value {
14    inner: Arc<dyn Any + Send + Sync>,
15    type_name: &'static str,
16}
17
18impl Value {
19    /// Erase a concrete value.
20    pub fn new<T>(value: T) -> Self
21    where
22        T: Any + Send + Sync,
23    {
24        Self {
25            inner: Arc::new(value),
26            type_name: type_name::<T>(),
27        }
28    }
29
30    /// Erase an existing `Arc` without adding a second `Arc` layer.
31    pub fn from_arc<T>(value: Arc<T>) -> Self
32    where
33        T: Any + Send + Sync,
34    {
35        Self {
36            inner: value,
37            type_name: type_name::<T>(),
38        }
39    }
40
41    /// Return the stored concrete type's diagnostic name.
42    pub const fn type_name(&self) -> &'static str {
43        self.type_name
44    }
45
46    /// Whether this value stores `T`.
47    pub fn is<T: Any>(&self) -> bool {
48        self.inner.is::<T>()
49    }
50
51    /// Recover an `Arc<T>` from the erased value.
52    pub fn downcast<T>(&self) -> Result<Arc<T>>
53    where
54        T: Any + Send + Sync,
55    {
56        self.inner.clone().downcast::<T>().map_err(|_| {
57            CordisError::with_message(
58                ErrorCode::TypeMismatch,
59                format!(
60                    "expected value of type `{}`, found `{}`",
61                    type_name::<T>(),
62                    self.type_name
63                ),
64            )
65        })
66    }
67
68    /// Borrow the underlying `Any` trait object.
69    pub fn as_any(&self) -> &(dyn Any + Send + Sync) {
70        self.inner.as_ref()
71    }
72
73    /// Whether two values share the same allocation.
74    pub(crate) fn ptr_eq(&self, other: &Value) -> bool {
75        Arc::ptr_eq(&self.inner, &other.inner)
76    }
77}
78
79impl Default for Value {
80    fn default() -> Self {
81        Self::new(())
82    }
83}
84
85impl Debug for Value {
86    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
87        f.debug_struct("Value")
88            .field("type_name", &self.type_name)
89            .finish_non_exhaustive()
90    }
91}
92
93/// Type-erased plugin configuration.
94pub type Config = Value;