tc_scalar/reference/
id.rs1use 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#[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}