tc_state/object/
class.rs

1//! A user-defined class.
2
3use std::fmt;
4use std::ops::Deref;
5
6use async_trait::async_trait;
7use destream::{de, en};
8use get_size::GetSize;
9use get_size_derive::*;
10use safecast::*;
11
12use tc_scalar::*;
13use tc_transact::hash::{Digest, Hash, Output};
14use tc_value::{Link, Value};
15use tcgeneric::{path_label, Map, NativeClass, PathLabel};
16
17use crate::{State, StateType};
18
19use super::ObjectType;
20
21const PATH: PathLabel = path_label(&["state", "class"]);
22
23/// A user-defined class.
24#[derive(Clone, Default, Eq, PartialEq, GetSize)]
25pub struct InstanceClass {
26    extends: Link,
27    proto: Map<Scalar>,
28}
29
30impl InstanceClass {
31    /// Construct a new base class.
32    pub fn new(proto: Map<Scalar>) -> Self {
33        Self {
34            extends: PATH.into(),
35            proto,
36        }
37    }
38
39    /// Construct a new class which extends the class at `extends`.
40    pub fn extend<L: Into<Link>>(extends: L, proto: Map<Scalar>) -> Self {
41        Self {
42            extends: extends.into(),
43            proto,
44        }
45    }
46
47    /// Borrow the link to this class' parent.
48    pub fn extends(&self) -> &Link {
49        &self.extends
50    }
51
52    /// Consume this class and return its data.
53    pub fn into_inner(self) -> (Link, Map<Scalar>) {
54        (self.extends, self.proto)
55    }
56
57    /// Borrow the prototype of this class.
58    pub fn proto(&'_ self) -> &'_ Map<Scalar> {
59        &self.proto
60    }
61}
62
63impl<D: Digest> Hash<D> for InstanceClass {
64    fn hash(self) -> Output<D> {
65        Hash::<D>::hash(&self)
66    }
67}
68
69impl<'a, D: Digest> Hash<D> for &'a InstanceClass {
70    fn hash(self) -> Output<D> {
71        if self.extends == Link::from(PATH) {
72            Hash::<D>::hash(self.proto.deref())
73        } else {
74            Hash::<D>::hash((&self.extends, self.proto.deref()))
75        }
76    }
77}
78
79impl tcgeneric::Class for InstanceClass {}
80
81impl tcgeneric::Instance for InstanceClass {
82    type Class = ObjectType;
83
84    fn class(&self) -> ObjectType {
85        ObjectType::Class
86    }
87}
88
89impl From<StateType> for InstanceClass {
90    fn from(st: StateType) -> Self {
91        Self::extend(st.path(), Map::default())
92    }
93}
94
95impl From<(Link, Map<Scalar>)> for InstanceClass {
96    fn from(class: (Link, Map<Scalar>)) -> Self {
97        let (extends, proto) = class;
98        Self { extends, proto }
99    }
100}
101
102impl CastFrom<Link> for InstanceClass {
103    fn cast_from(extends: Link) -> Self {
104        Self {
105            extends,
106            proto: Map::new(),
107        }
108    }
109}
110
111impl TryCastFrom<PostRef> for InstanceClass {
112    fn can_cast_from(value: &PostRef) -> bool {
113        match value {
114            (Subject::Link(_), _) => true,
115            _ => false,
116        }
117    }
118
119    fn opt_cast_from(value: PostRef) -> Option<Self> {
120        match value {
121            (Subject::Link(extends), proto) => Some(Self { extends, proto }),
122            _ => None,
123        }
124    }
125}
126
127impl TryCastFrom<OpRef> for InstanceClass {
128    fn can_cast_from(op_ref: &OpRef) -> bool {
129        match op_ref {
130            OpRef::Post(op_ref) => Self::can_cast_from(op_ref),
131            _ => false,
132        }
133    }
134
135    fn opt_cast_from(op_ref: OpRef) -> Option<Self> {
136        match op_ref {
137            OpRef::Post(op_ref) => Self::opt_cast_from(op_ref),
138            _ => None,
139        }
140    }
141}
142
143impl TryCastFrom<TCRef> for InstanceClass {
144    fn can_cast_from(tc_ref: &TCRef) -> bool {
145        match tc_ref {
146            TCRef::Op(op_ref) => Self::can_cast_from(op_ref),
147            _ => false,
148        }
149    }
150
151    fn opt_cast_from(tc_ref: TCRef) -> Option<Self> {
152        match tc_ref {
153            TCRef::Op(op_ref) => Self::opt_cast_from(op_ref),
154            _ => None,
155        }
156    }
157}
158
159impl TryCastFrom<Scalar> for InstanceClass {
160    fn can_cast_from(scalar: &Scalar) -> bool {
161        match scalar {
162            Scalar::Ref(tc_ref) => Self::can_cast_from(&**tc_ref),
163            Scalar::Tuple(tuple) => tuple.matches::<(Link, Map<Scalar>)>(),
164            Scalar::Value(value) => Self::can_cast_from(value),
165            _ => false,
166        }
167    }
168
169    fn opt_cast_from(scalar: Scalar) -> Option<Self> {
170        match scalar {
171            Scalar::Ref(tc_ref) => Self::opt_cast_from(*tc_ref),
172            Scalar::Tuple(tuple) => {
173                let (extends, proto) = tuple.opt_cast_into()?;
174                Some(Self::cast_from((extends, proto)))
175            }
176            Scalar::Value(value) => Self::opt_cast_from(value),
177            _ => None,
178        }
179    }
180}
181
182impl CastFrom<InstanceClass> for Scalar {
183    fn cast_from(class: InstanceClass) -> Self {
184        if class.proto.is_empty() {
185            Self::Value(class.extends.into())
186        } else {
187            Self::Ref(Box::new(TCRef::Op(OpRef::Post((
188                class.extends.into(),
189                class.proto,
190            )))))
191        }
192    }
193}
194
195impl<Txn> CastFrom<InstanceClass> for (Link, Map<State<Txn>>) {
196    fn cast_from(class: InstanceClass) -> Self {
197        let proto = class
198            .proto
199            .into_iter()
200            .map(|(key, scalar)| (key, State::Scalar(scalar)))
201            .collect();
202
203        (class.extends, proto)
204    }
205}
206
207impl TryCastFrom<Value> for InstanceClass {
208    fn can_cast_from(value: &Value) -> bool {
209        match value {
210            Value::Link(link) => Self::can_cast_from(link),
211            _ => false,
212        }
213    }
214
215    fn opt_cast_from(value: Value) -> Option<Self> {
216        match value {
217            Value::Link(link) => Self::opt_cast_from(link),
218            _ => None,
219        }
220    }
221}
222
223impl TryCastFrom<InstanceClass> for StateType {
224    fn can_cast_from(class: &InstanceClass) -> bool {
225        if class.proto.is_empty() {
226            if class.extends.host().is_none() {
227                return StateType::from_path(class.extends.path()).is_some();
228            }
229        }
230
231        false
232    }
233
234    fn opt_cast_from(class: InstanceClass) -> Option<Self> {
235        if class.proto.is_empty() {
236            if class.extends.host().is_none() {
237                return StateType::from_path(class.extends.path());
238            }
239        }
240
241        None
242    }
243}
244
245impl TryCastFrom<InstanceClass> for Link {
246    fn can_cast_from(class: &InstanceClass) -> bool {
247        class.proto.is_empty()
248    }
249
250    fn opt_cast_from(class: InstanceClass) -> Option<Self> {
251        if class.proto.is_empty() {
252            Some(class.extends.into())
253        } else {
254            None
255        }
256    }
257}
258
259impl TryCastFrom<InstanceClass> for Value {
260    fn can_cast_from(class: &InstanceClass) -> bool {
261        Link::can_cast_from(class)
262    }
263
264    fn opt_cast_from(class: InstanceClass) -> Option<Self> {
265        Link::opt_cast_from(class).map(Self::Link)
266    }
267}
268
269#[async_trait]
270impl de::FromStream for InstanceClass {
271    type Context = ();
272
273    async fn from_stream<D: de::Decoder>(_: (), decoder: &mut D) -> Result<Self, D::Error> {
274        decoder.decode_map(InstanceClassVisitor).await
275    }
276}
277
278impl<'en> en::ToStream<'en> for InstanceClass {
279    fn to_stream<E: en::Encoder<'en>>(&'en self, encoder: E) -> Result<E::Ok, E::Error> {
280        use en::EncodeMap;
281
282        let mut map = encoder.encode_map(Some(1))?;
283        map.encode_entry(&self.extends, &self.proto)?;
284        map.end()
285    }
286}
287
288impl<'en> en::IntoStream<'en> for InstanceClass {
289    fn into_stream<E: en::Encoder<'en>>(self, encoder: E) -> Result<E::Ok, E::Error> {
290        use en::EncodeMap;
291
292        let mut map = encoder.encode_map(Some(1))?;
293        map.encode_entry(self.extends, self.proto)?;
294        map.end()
295    }
296}
297
298impl fmt::Debug for InstanceClass {
299    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
300        write!(
301            f,
302            "Class which extends {} with prototype {:?}",
303            self.extends, self.proto
304        )
305    }
306}
307
308struct InstanceClassVisitor;
309
310#[async_trait]
311impl de::Visitor for InstanceClassVisitor {
312    type Value = InstanceClass;
313
314    fn expecting() -> &'static str {
315        "a user-defined Class"
316    }
317
318    async fn visit_map<A: de::MapAccess>(self, mut access: A) -> Result<InstanceClass, A::Error> {
319        if let Some(extends) = access.next_key::<Link>(()).await? {
320            log::debug!("Class extends {}", extends);
321            let proto = access.next_value(()).await?;
322            log::debug!("prototype is {:?}", proto);
323            return Ok(InstanceClass::extend(extends, proto));
324        } else {
325            Err(de::Error::invalid_length(0, 1))
326        }
327    }
328}