tc_scalar/reference/
id.rs

1//! Reference another `State` in the same transaction context.
2
3use std::collections::HashSet;
4use std::fmt;
5use std::str::FromStr;
6
7use async_hash::{Digest, Hash, Output};
8use async_trait::async_trait;
9use destream::de;
10use destream::en::{EncodeMap, Encoder, IntoStream, ToStream};
11use get_size::GetSize;
12use get_size_derive::*;
13use log::debug;
14use safecast::TryCastFrom;
15
16use tc_error::*;
17use tc_transact::public::{Public, StateInstance, ToState};
18use tcgeneric::{Id, Instance, Label, PathSegment, TCPathBuf};
19
20use crate::{Scope, Value, SELF};
21
22use super::Refer;
23
24const EMPTY_SLICE: &[usize] = &[];
25
26/// A reference to the `State` at a given [`Id`] within the same transaction context.
27#[derive(Clone, Eq, PartialEq, Hash, Ord, PartialOrd, GetSize)]
28pub struct IdRef {
29    to: Id,
30}
31
32impl IdRef {
33    pub fn into_id(self) -> Id {
34        self.to
35    }
36
37    pub fn id(&'_ self) -> &'_ Id {
38        &self.to
39    }
40}
41
42#[async_trait]
43impl<State> Refer<State> for IdRef
44where
45    State: StateInstance + Refer<State>,
46{
47    fn dereference_self(self, path: &TCPathBuf) -> Self {
48        if self.to == SELF {
49            panic!("cannot dereference {} to {}", self, path);
50        } else {
51            self
52        }
53    }
54
55    fn is_conditional(&self) -> bool {
56        false
57    }
58
59    fn is_inter_service_write(&self, _cluster_path: &[PathSegment]) -> bool {
60        false
61    }
62
63    fn is_ref(&self) -> bool {
64        true
65    }
66
67    fn reference_self(self, _path: &TCPathBuf) -> Self {
68        self
69    }
70
71    fn requires(&self, deps: &mut HashSet<Id>) {
72        if self.to != SELF {
73            deps.insert(self.to.clone());
74        }
75    }
76
77    async fn resolve<'a, T: ToState<State> + Public<State> + Instance>(
78        self,
79        context: &'a Scope<'a, State, T>,
80        _txn: &'a State::Txn,
81    ) -> TCResult<State> {
82        debug!("IdRef::resolve {}", self);
83        context.resolve_id(self.id())
84    }
85}
86
87impl PartialEq<Id> for IdRef {
88    fn eq(&self, other: &Id) -> bool {
89        self.id() == other
90    }
91}
92
93impl<'a, D: Digest> Hash<D> for &'a IdRef {
94    fn hash(self) -> Output<D> {
95        Hash::<D>::hash(&self.to)
96    }
97}
98
99impl From<Label> for IdRef {
100    fn from(to: Label) -> Self {
101        Self { to: to.into() }
102    }
103}
104
105impl From<Id> for IdRef {
106    fn from(to: Id) -> Self {
107        Self { to }
108    }
109}
110
111impl FromStr for IdRef {
112    type Err = TCError;
113
114    #[inline]
115    fn from_str(to: &str) -> TCResult<Self> {
116        if !to.starts_with('$') || to.len() < 2 {
117            Err(TCError::unexpected(to, "a reference to an ID"))
118        } else {
119            to[1..]
120                .parse()
121                .map(|to| Self { to })
122                .map_err(|cause| TCError::unexpected(to, "a reference to an ID").consume(cause))
123        }
124    }
125}
126
127impl TryCastFrom<Value> for IdRef {
128    fn can_cast_from(value: &Value) -> bool {
129        match value {
130            Value::String(s) => Self::from_str(s.as_str()).is_ok(),
131            _ => false,
132        }
133    }
134
135    fn opt_cast_from(value: Value) -> Option<Self> {
136        match value {
137            Value::String(s) => Self::from_str(s.as_str()).ok(),
138            _ => None,
139        }
140    }
141}
142
143impl From<IdRef> for Id {
144    fn from(r: IdRef) -> Id {
145        r.to
146    }
147}
148
149impl fmt::Debug for IdRef {
150    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151        write!(f, "reference to {:?}", self.to)
152    }
153}
154
155impl fmt::Display for IdRef {
156    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157        write!(f, "${}", self.to)
158    }
159}
160
161#[async_trait]
162impl de::FromStream for IdRef {
163    type Context = ();
164
165    async fn from_stream<D: de::Decoder>(context: (), d: &mut D) -> Result<Self, D::Error> {
166        let id_ref = String::from_stream(context, d).await?;
167        id_ref.parse().map_err(de::Error::custom)
168    }
169}
170
171impl<'en> ToStream<'en> for IdRef {
172    fn to_stream<E: Encoder<'en>>(&'en self, e: E) -> Result<E::Ok, E::Error> {
173        let mut map = e.encode_map(Some(1))?;
174        map.encode_entry(self.to_string(), EMPTY_SLICE)?;
175        map.end()
176    }
177}
178
179impl<'en> IntoStream<'en> for IdRef {
180    fn into_stream<E: Encoder<'en>>(self, e: E) -> Result<E::Ok, E::Error> {
181        let mut map = e.encode_map(Some(1))?;
182        map.encode_entry(self.to_string(), EMPTY_SLICE)?;
183        map.end()
184    }
185}