tc_transact/public/
value.rs

1use safecast::TryCastFrom;
2use std::sync::Arc;
3
4use tc_error::*;
5use tc_value::uuid::Uuid;
6use tc_value::{Number, TCString, Value, ValueType};
7use tcgeneric::{label, Id, Label, Map, PathSegment, Tuple};
8
9use super::helpers::SelfHandler;
10use super::{ClosureInstance, GetHandler, Handler, Route, StateInstance};
11
12pub const PREFIX: Label = label("value");
13
14struct EqHandler<F> {
15    call: F,
16}
17
18impl<'a, State, F> Handler<'a, State> for EqHandler<F>
19where
20    State: StateInstance,
21    F: FnOnce(Value) -> bool + Send + 'a,
22{
23    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
24    where
25        'b: 'a,
26    {
27        Some(Box::new(|_txn, key| {
28            Box::pin(async move { Ok(Value::from((self.call)(key)).into()) })
29        }))
30    }
31}
32
33impl<F> From<F> for EqHandler<F> {
34    fn from(call: F) -> Self {
35        Self { call }
36    }
37}
38
39impl<State> Route<State> for Value
40where
41    State: StateInstance + From<Value> + From<Tuple<Value>>,
42    Box<dyn ClosureInstance<State>>: TryCastFrom<State>,
43    Id: TryCastFrom<State>,
44    Map<State>: TryFrom<State, Error = TCError>,
45    Number: TryCastFrom<State>,
46    TCString: TryCastFrom<State>,
47    Tuple<State>: TryCastFrom<State>,
48    Value: TryCastFrom<State>,
49{
50    fn route<'a>(&'a self, path: &'a [PathSegment]) -> Option<Box<dyn Handler<'a, State> + 'a>> {
51        let child_handler = match self {
52            Self::Number(number) => number.route(path),
53            Self::String(s) => s.route(path),
54            Self::Tuple(tuple) => tuple.route(path),
55            _ => None,
56        };
57
58        if child_handler.is_some() {
59            child_handler
60        } else if path.len() == 1 {
61            match path[0].as_str() {
62                "eq" => Some(Box::new(EqHandler::from(move |other| self == &other))),
63                "ne" => Some(Box::new(EqHandler::from(move |other| self != &other))),
64                _ => None,
65            }
66        } else if path.is_empty() {
67            Some(Box::new(SelfHandler::from(self)))
68        } else {
69            None
70        }
71    }
72}
73
74struct UuidHandler<'a> {
75    dtype: &'a str,
76}
77
78impl<'a, State: StateInstance> Handler<'a, State> for UuidHandler<'a> {
79    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
80    where
81        'b: 'a,
82    {
83        Some(Box::new(|_txn, key| {
84            Box::pin(async move {
85                let uuid = if key.is_some() {
86                    Uuid::try_cast_from(key, |v| TCError::unexpected(v, "a UUID"))?
87                } else {
88                    return Err(bad_request!("missing UUID to cast into {}", self.dtype));
89                };
90
91                let value = match self.dtype {
92                    "bytes" => Value::Bytes(Arc::new(uuid.into_bytes())),
93                    "id" => Value::Id(uuid.into()),
94                    "string" => Value::String(uuid.to_string().into()),
95                    other => {
96                        return Err(TCError::not_found(format!("{} in {}", other, self.dtype)))
97                    }
98                };
99
100                Ok(State::from(value))
101            })
102        }))
103    }
104}
105
106struct StaticHandler {
107    class: ValueType,
108}
109
110impl<'a, State: StateInstance> Handler<'a, State> for StaticHandler {
111    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
112    where
113        'b: 'a,
114    {
115        Some(Box::new(|_txn, key| {
116            Box::pin(async move { self.class.try_cast(key).map(State::from) })
117        }))
118    }
119}
120
121impl<'a> From<ValueType> for StaticHandler {
122    fn from(class: ValueType) -> Self {
123        Self { class }
124    }
125}
126
127pub struct Static;
128
129impl<State: StateInstance> Route<State> for Static {
130    fn route<'a>(&'a self, path: &'a [PathSegment]) -> Option<Box<dyn Handler<'a, State> + 'a>> {
131        if let Some(class) = ValueType::from_suffix(path) {
132            return Some(Box::new(StaticHandler::from(class)));
133        }
134
135        assert!(
136            !path.is_empty(),
137            "failed to parse ValueType::Value from an empty path"
138        );
139
140        match path[0].as_str() {
141            "bytes" | "id" | "string" if path.len() == 2 => match path[1].as_str() {
142                "uuid" => Some(Box::new(UuidHandler {
143                    dtype: path[0].as_str(),
144                })),
145                _ => None,
146            },
147            _ => None,
148        }
149    }
150}