1use std::marker::PhantomData;
2
3use futures::TryFutureExt;
4use log::debug;
5use safecast::TryCastInto;
6
7use tc_error::*;
8#[cfg(any(feature = "btree", feature = "table"))]
9use tc_transact::fs;
10use tc_transact::hash::AsyncHash;
11use tc_transact::public::helpers::{AttributeHandler, EchoHandler, SelfHandler};
12use tc_transact::public::{GetHandler, Handler, PostHandler, Route};
13use tc_transact::{Gateway, Transaction};
14use tc_value::{Link, Number, Value};
15use tcgeneric::{label, Id, Instance, Label, Map, NativeClass, PathSegment, TCPath};
16
17#[cfg(any(feature = "btree", feature = "table"))]
18use crate::collection::{BTreeFile, BTreeSchema};
19#[cfg(feature = "table")]
20use crate::collection::{TableFile, TableSchema};
21use crate::object::{InstanceClass, Object};
22#[cfg(any(feature = "btree", feature = "table"))]
23use crate::Collection;
24use crate::{CacheBlock, State, StateType};
25
26pub const PREFIX: Label = label("state");
27
28struct ClassHandler {
29 class: StateType,
30}
31
32impl<'a, Txn> Handler<'a, State<Txn>> for ClassHandler
33where
34 Txn: Transaction<CacheBlock> + Gateway<State<Txn>>,
35{
36 fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, Txn, State<Txn>>>
37 where
38 'b: 'a,
39 {
40 Some(Box::new(|_txn, _key| {
41 Box::pin(async move { Ok(Link::from(self.class.path()).into()) })
42 }))
43 }
44
45 fn post<'b>(self: Box<Self>) -> Option<PostHandler<'a, 'b, Txn, State<Txn>>>
46 where
47 'b: 'a,
48 {
49 Some(Box::new(|_txn, params| {
50 Box::pin(async move {
51 let mut proto = Map::new();
52 for (id, member) in params.into_iter() {
53 let member = member.try_cast_into(|s| {
54 TCError::unexpected(s, "an attribute in an public prototype")
55 })?;
56
57 proto.insert(id, member);
58 }
59
60 let class = InstanceClass::extend(self.class.path().clone(), proto);
61 Ok(Object::Class(class).into())
62 })
63 }))
64 }
65}
66
67struct HashHandler<Txn> {
68 state: State<Txn>,
69}
70
71impl<'a, Txn> Handler<'a, State<Txn>> for HashHandler<Txn>
72where
73 Txn: Transaction<CacheBlock> + Gateway<State<Txn>>,
74{
75 fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, Txn, State<Txn>>>
76 where
77 'b: 'a,
78 {
79 Some(Box::new(|txn, key| {
80 Box::pin(async move {
81 key.expect_none()?;
82
83 self.state
84 .hash(*txn.id())
85 .map_ok(Id::from)
86 .map_ok(Value::from)
87 .map_ok(State::from)
88 .await
89 })
90 }))
91 }
92}
93
94impl<Txn> From<State<Txn>> for HashHandler<Txn> {
95 fn from(state: State<Txn>) -> HashHandler<Txn> {
96 Self { state }
97 }
98}
99
100impl From<StateType> for ClassHandler {
101 fn from(class: StateType) -> Self {
102 Self { class }
103 }
104}
105
106impl<Txn> Route<State<Txn>> for StateType
107where
108 Txn: Transaction<CacheBlock> + Gateway<State<Txn>>,
109{
110 fn route<'a>(
111 &'a self,
112 path: &'a [PathSegment],
113 ) -> Option<Box<dyn Handler<'a, State<Txn>> + 'a>> {
114 let child_handler = match self {
115 #[cfg(feature = "chain")]
116 Self::Chain(ct) => ct.route(path),
117 #[cfg(feature = "collection")]
118 Self::Collection(ct) => ct.route(path),
119 Self::Object(ot) => ot.route(path),
120 Self::Scalar(st) => st.route(path),
121 _ => None,
122 };
123
124 if child_handler.is_some() {
125 return child_handler;
126 }
127
128 if path.is_empty() {
129 Some(Box::new(ClassHandler::from(*self)))
130 } else {
131 None
132 }
133 }
134}
135
136impl<Txn> Route<State<Txn>> for State<Txn>
137where
138 Txn: Transaction<CacheBlock> + Gateway<Self>,
139{
140 fn route<'a>(
141 &'a self,
142 path: &'a [PathSegment],
143 ) -> Option<Box<dyn Handler<'a, State<Txn>> + 'a>> {
144 debug!(
145 "instance of {:?} route {}",
146 self.class(),
147 TCPath::from(path)
148 );
149
150 if let Some(handler) = match self {
151 #[cfg(feature = "chain")]
152 Self::Chain(chain) => chain.route(path),
153 Self::Closure(closure) if path.is_empty() => {
154 let handler: Box<dyn Handler<'a, State<Txn>> + 'a> = Box::new(closure.clone());
155 Some(handler)
156 }
157 #[cfg(feature = "collection")]
158 Self::Collection(collection) => collection.route(path),
159 Self::Map(map) => map.route(path),
160 Self::Object(object) => object.route(path),
161 Self::Scalar(scalar) => scalar.route(path),
162 Self::Tuple(tuple) => tuple.route(path),
163 _ => None,
164 } {
165 return Some(handler);
166 }
167
168 if path.is_empty() {
169 Some(Box::new(SelfHandler::from(self)))
170 } else if path.len() == 1 {
171 match path[0].as_str() {
172 "class" => Some(Box::new(ClassHandler::from(self.class()))),
173 "hash" => Some(Box::new(HashHandler::from(self.clone()))),
174 "is_none" => Some(Box::new(AttributeHandler::from(Number::Bool(
175 self.is_none().into(),
176 )))),
177 _ => None,
178 }
179 } else {
180 None
181 }
182 }
183}
184
185#[derive(Copy, Clone)]
186pub struct Static<Txn> {
187 phantom: PhantomData<Txn>,
188}
189
190impl<Txn> Default for Static<Txn> {
191 fn default() -> Self {
192 Self {
193 phantom: PhantomData,
194 }
195 }
196}
197
198#[cfg(all(feature = "collection", not(feature = "btree"), not(feature = "table")))]
199impl<Txn> Route<State<Txn>> for Static<Txn>
200where
201 Txn: Transaction<CacheBlock> + Gateway<State<Txn>>,
202{
203 fn route<'a>(
204 &'a self,
205 path: &'a [PathSegment],
206 ) -> Option<Box<dyn Handler<'a, State<Txn>> + 'a>> {
207 if path.is_empty() {
208 return Some(Box::new(EchoHandler));
209 }
210
211 match path[0].as_str() {
212 "collection" => tc_collection::public::Static.route(&path[1..]),
213 "scalar" => tc_scalar::public::Static.route(&path[1..]),
214 "map" => tc_transact::public::generic::MapStatic.route(&path[1..]),
215 "tuple" => tc_transact::public::generic::TupleStatic.route(&path[1..]),
216 _ => None,
217 }
218 }
219}
220
221#[cfg(all(feature = "btree", not(feature = "table")))]
222impl<Txn> Route<State<Txn>> for Static<Txn>
223where
224 Txn: Transaction<CacheBlock> + Gateway<State<Txn>>,
225 BTreeFile<Txn>: fs::Persist<CacheBlock, Schema = BTreeSchema, Txn = Txn>,
226 Collection<Txn>: From<BTreeFile<Txn>>,
227{
228 fn route<'a>(
229 &'a self,
230 path: &'a [PathSegment],
231 ) -> Option<Box<dyn Handler<'a, State<Txn>> + 'a>> {
232 if path.is_empty() {
233 return Some(Box::new(EchoHandler));
234 }
235
236 match path[0].as_str() {
237 "collection" => tc_collection::public::Static.route(&path[1..]),
238 "scalar" => tc_scalar::public::Static.route(&path[1..]),
239 "map" => tc_transact::public::generic::MapStatic.route(&path[1..]),
240 "tuple" => tc_transact::public::generic::TupleStatic.route(&path[1..]),
241 _ => None,
242 }
243 }
244}
245
246#[cfg(feature = "table")]
247impl<Txn> Route<State<Txn>> for Static<Txn>
248where
249 Txn: Transaction<CacheBlock> + Gateway<State<Txn>>,
250 BTreeFile<Txn>: fs::Persist<CacheBlock, Schema = BTreeSchema, Txn = Txn>,
251 TableFile<Txn>: fs::Persist<CacheBlock, Schema = TableSchema, Txn = Txn>,
252 Collection<Txn>: From<BTreeFile<Txn>>,
253{
254 fn route<'a>(
255 &'a self,
256 path: &'a [PathSegment],
257 ) -> Option<Box<dyn Handler<'a, State<Txn>> + 'a>> {
258 if path.is_empty() {
259 return Some(Box::new(EchoHandler));
260 }
261
262 match path[0].as_str() {
263 "collection" => tc_collection::public::Static.route(&path[1..]),
264 "scalar" => tc_scalar::public::Static.route(&path[1..]),
265 "map" => tc_transact::public::generic::MapStatic.route(&path[1..]),
266 "tuple" => tc_transact::public::generic::TupleStatic.route(&path[1..]),
267 _ => None,
268 }
269 }
270}
271
272#[cfg(not(feature = "collection"))]
273impl<Txn> Route<State<Txn>> for Static<Txn>
274where
275 Txn: Transaction<CacheBlock> + Gateway<State<Txn>>,
276{
277 fn route<'a>(
278 &'a self,
279 path: &'a [PathSegment],
280 ) -> Option<Box<dyn Handler<'a, State<Txn>> + 'a>> {
281 if path.is_empty() {
282 return Some(Box::new(EchoHandler));
283 }
284
285 match path[0].as_str() {
286 "scalar" => tc_scalar::public::Static.route(&path[1..]),
287 "map" => tc_transact::public::generic::MapStatic.route(&path[1..]),
288 "tuple" => tc_transact::public::generic::TupleStatic.route(&path[1..]),
289 _ => None,
290 }
291 }
292}