tc_state/object/
mod.rs

1//! User-defined public-orientation features.
2
3use std::fmt;
4use std::marker::PhantomData;
5
6use async_trait::async_trait;
7use destream::{de, en};
8use safecast::{TryCastFrom, TryCastInto};
9
10use tc_error::*;
11use tc_scalar::Scalar;
12use tc_transact::hash::{AsyncHash, Hash, Output, Sha256};
13use tc_transact::{Gateway, IntoView, Transaction, TxnId};
14use tc_value::Value;
15use tcgeneric::{label, path_label, NativeClass, PathLabel, PathSegment, TCPathBuf};
16
17use super::{CacheBlock, State};
18
19pub use class::*;
20pub use instance::*;
21
22mod class;
23mod instance;
24pub mod public;
25
26const PREFIX: PathLabel = path_label(&["state", "object"]);
27
28/// The type of a user-defined [`Object`].
29#[derive(Copy, Clone, Eq, PartialEq)]
30pub enum ObjectType {
31    Class,
32    Instance,
33}
34
35impl tcgeneric::Class for ObjectType {}
36
37impl NativeClass for ObjectType {
38    fn from_path(path: &[PathSegment]) -> Option<Self> {
39        if path.len() == 3 && &path[..2] == &PREFIX[..] {
40            match path[2].as_str() {
41                "class" => Some(Self::Class),
42                "instance" => Some(Self::Instance),
43                _ => None,
44            }
45        } else {
46            None
47        }
48    }
49
50    fn path(&self) -> TCPathBuf {
51        let suffix = match self {
52            Self::Class => "class",
53            Self::Instance => "instance",
54        };
55
56        TCPathBuf::from(PREFIX).append(label(suffix))
57    }
58}
59
60impl fmt::Debug for ObjectType {
61    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62        match self {
63            Self::Class => f.write_str("user-defined Class"),
64            Self::Instance => f.write_str("user-defined Instance"),
65        }
66    }
67}
68
69/// A user-defined [`InstanceClass`] or [`InstanceExt`].
70pub enum Object<Txn> {
71    Class(InstanceClass),
72    Instance(InstanceExt<Txn, State<Txn>>),
73}
74
75impl<Txn> Clone for Object<Txn> {
76    fn clone(&self) -> Self {
77        match self {
78            Self::Class(class) => Self::Class(class.clone()),
79            Self::Instance(instance) => Self::Instance(instance.clone()),
80        }
81    }
82}
83
84impl<Txn> tcgeneric::Instance for Object<Txn>
85where
86    Txn: Send + Sync,
87{
88    type Class = ObjectType;
89
90    fn class(&self) -> ObjectType {
91        match self {
92            Self::Class(_) => ObjectType::Class,
93            Self::Instance(_) => ObjectType::Instance,
94        }
95    }
96}
97
98#[async_trait]
99impl<Txn> AsyncHash for Object<Txn>
100where
101    Txn: Transaction<CacheBlock> + Gateway<State<Txn>>,
102{
103    async fn hash(&self, _txn_id: TxnId) -> TCResult<Output<Sha256>> {
104        match self {
105            Self::Class(class) => Ok(Hash::<Sha256>::hash(class)),
106            Self::Instance(instance) => Err(bad_request!("cannot hash {:?}", instance)),
107        }
108    }
109}
110
111impl<Txn> From<InstanceClass> for Object<Txn> {
112    fn from(class: InstanceClass) -> Self {
113        Object::Class(class)
114    }
115}
116
117impl<Txn> From<InstanceExt<Txn, State<Txn>>> for Object<Txn> {
118    fn from(instance: InstanceExt<Txn, State<Txn>>) -> Self {
119        Object::Instance(instance)
120    }
121}
122
123impl<Txn> TryCastFrom<Object<Txn>> for Scalar {
124    fn can_cast_from(object: &Object<Txn>) -> bool {
125        match object {
126            Object::Class(class) => Self::can_cast_from(class),
127            Object::Instance(instance) => Self::can_cast_from(instance),
128        }
129    }
130
131    fn opt_cast_from(object: Object<Txn>) -> Option<Self> {
132        match object {
133            Object::Class(class) => Self::opt_cast_from(class),
134            Object::Instance(instance) => Self::opt_cast_from(instance),
135        }
136    }
137}
138
139impl<Txn> TryCastFrom<Object<Txn>> for Value {
140    fn can_cast_from(object: &Object<Txn>) -> bool {
141        match object {
142            Object::Class(class) => Self::can_cast_from(class),
143            Object::Instance(instance) => Self::can_cast_from(instance),
144        }
145    }
146
147    fn opt_cast_from(object: Object<Txn>) -> Option<Self> {
148        match object {
149            Object::Class(class) => Self::opt_cast_from(class),
150            Object::Instance(instance) => Self::opt_cast_from(instance),
151        }
152    }
153}
154
155#[async_trait]
156impl<'en, Txn> IntoView<'en, CacheBlock> for Object<Txn>
157where
158    Txn: Transaction<CacheBlock> + Gateway<State<Txn>>,
159{
160    type Txn = Txn;
161    type View = ObjectView<'en>;
162
163    async fn into_view(self, _txn: Txn) -> TCResult<ObjectView<'en>> {
164        match self {
165            Self::Class(class) => Ok(ObjectView::Class(class, PhantomData)),
166            Self::Instance(_instance) => Err(not_implemented!("view of user-defined instance")),
167        }
168    }
169}
170
171#[async_trait]
172impl<Txn> fmt::Debug for Object<Txn> {
173    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
174        match self {
175            Self::Class(ict) => fmt::Debug::fmt(ict, f),
176            Self::Instance(ic) => fmt::Debug::fmt(ic, f),
177        }
178    }
179}
180
181/// A view of an [`Object`] at a specific transaction, used for serialization.
182pub enum ObjectView<'en> {
183    // the 'en lifetime is needed to compile when the collection feature flag is off
184    Class(InstanceClass, PhantomData<&'en ()>),
185}
186
187impl<'en> en::IntoStream<'en> for ObjectView<'en> {
188    fn into_stream<E: en::Encoder<'en>>(self, encoder: E) -> Result<E::Ok, E::Error> {
189        use destream::en::EncodeMap;
190
191        let mut map = encoder.encode_map(Some(1))?;
192
193        match self {
194            Self::Class(class, _) => map.encode_entry(ObjectType::Class.path().to_string(), class),
195        }?;
196
197        map.end()
198    }
199}
200
201/// A helper struct for Object deserialization
202pub struct ObjectVisitor<Txn> {
203    phantom: PhantomData<Txn>,
204}
205
206impl<Txn> ObjectVisitor<Txn>
207where
208    Txn: Send + Sync,
209{
210    pub fn new() -> Self {
211        Self {
212            phantom: PhantomData,
213        }
214    }
215
216    pub async fn visit_map_value<Err: de::Error>(
217        self,
218        class: ObjectType,
219        state: State<Txn>,
220    ) -> Result<Object<Txn>, Err> {
221        match class {
222            ObjectType::Class => {
223                let class = state.try_cast_into(|s| {
224                    de::Error::invalid_value(format!("{s:?}"), "a Class definition")
225                })?;
226
227                Ok(Object::Class(class))
228            }
229            ObjectType::Instance => Ok(Object::Instance(InstanceExt::new(
230                state,
231                InstanceClass::default(),
232            ))),
233        }
234    }
235}