tc_value/
class.rs

1use std::cmp::Ordering;
2use std::convert::TryFrom;
3use std::fmt;
4use std::str::FromStr;
5use std::sync::Arc;
6
7use async_trait::async_trait;
8use bytes::Bytes;
9use destream::de::Error as DestreamError;
10use destream::de::Visitor as DestreamVisitor;
11use destream::{de, en};
12use email_address_parser::EmailAddress;
13use log::debug;
14use safecast::{TryCastFrom, TryCastInto};
15
16use tc_error::*;
17use tcgeneric::*;
18
19use super::number::{ComplexType, FloatType, IntType, NumberClass, NumberType, UIntType};
20use super::value::Value;
21use super::version::Version;
22
23/// The path prefix of a [`ValueType`].
24pub const PREFIX: PathLabel = path_label(&["state", "scalar", "value"]);
25
26/// The class of a [`Value`].
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
28pub enum ValueType {
29    Bytes,
30    Email,
31    Id,
32    Link,
33    None,
34    Number(NumberType),
35    String,
36    Tuple,
37    Value,
38    Version,
39}
40
41impl ValueType {
42    /// Parse a [`ValueType`] from a path suffix.
43    pub fn from_suffix(suffix: &[PathSegment]) -> Option<Self> {
44        debug!("ValueType::from_suffix {}", TCPath::from(suffix));
45
46        use ComplexType as CT;
47        use FloatType as FT;
48        use IntType as IT;
49        use NumberType as NT;
50        use UIntType as UT;
51
52        if suffix.is_empty() {
53            Some(Self::default())
54        } else if suffix.len() == 1 {
55            match suffix[0].as_str() {
56                "bytes" => Some(Self::Bytes),
57                "email" => Some(Self::Email),
58                "id" => Some(Self::Id),
59                "link" => Some(Self::Link),
60                "number" => Some(Self::Number(NT::Number)),
61                "none" => Some(Self::None),
62                "string" => Some(Self::String),
63                "tuple" => Some(Self::Tuple),
64                "version" => Some(Self::Version),
65                _ => None,
66            }
67        } else if suffix.len() == 2 && &suffix[0] == "number" {
68            match suffix[1].as_str() {
69                "bool" => Some(Self::Number(NT::Bool)),
70                "complex" => Some(Self::Number(NT::Complex(CT::Complex))),
71                "float" => Some(Self::Number(NT::Float(FT::Float))),
72                "int" => Some(Self::Number(NT::Int(IT::Int))),
73                "uint" => Some(Self::Number(NT::UInt(UT::UInt))),
74                _ => None,
75            }
76        } else if suffix.len() == 3 && &suffix[0] == "number" {
77            match suffix[1].as_str() {
78                "complex" => match suffix[2].as_str() {
79                    "32" => Some(Self::Number(NT::Complex(CT::C32))),
80                    "64" => Some(Self::Number(NT::Complex(CT::C64))),
81                    _ => None,
82                },
83                "float" => match suffix[2].as_str() {
84                    "32" => Some(Self::Number(NT::Float(FT::F32))),
85                    "64" => Some(Self::Number(NT::Float(FT::F64))),
86                    _ => None,
87                },
88                "int" => match suffix[2].as_str() {
89                    "16" => Some(Self::Number(NT::Int(IT::I16))),
90                    "32" => Some(Self::Number(NT::Int(IT::I32))),
91                    "64" => Some(Self::Number(NT::Int(IT::I64))),
92                    _ => None,
93                },
94                "uint" => match suffix[2].as_str() {
95                    "8" => Some(Self::Number(NT::UInt(UT::U8))),
96                    "16" => Some(Self::Number(NT::UInt(UT::U16))),
97                    "32" => Some(Self::Number(NT::UInt(UT::U32))),
98                    "64" => Some(Self::Number(NT::UInt(UT::U64))),
99                    _ => None,
100                },
101                _ => None,
102            }
103        } else {
104            None
105        }
106    }
107
108    /// Return the fixed size of this [`ValueType`] in memory, if it has a fixed size.
109    pub fn size(&self) -> Option<usize> {
110        match self {
111            Self::Number(nt) => Some(nt.size()),
112            _ => None,
113        }
114    }
115
116    /// Try to cast the given `value` into this [`ValueType`].
117    pub fn try_cast<V>(&self, value: V) -> TCResult<Value>
118    where
119        Value: From<V>,
120    {
121        let on_err = |v: &Value| bad_request!("cannot cast into {self:?} from {v:?}");
122        let value = Value::from(value);
123
124        match self {
125            Self::Bytes => match value {
126                Value::Bytes(bytes) => Ok(Value::Bytes(bytes)),
127                Value::Number(_) => Err(not_implemented!("cast into Bytes from Number")),
128                Value::String(s) => {
129                    Bytes::try_cast_from(s, |s| bad_request!("cannot cast into Bytes from {s}"))
130                        .map(Vec::from)
131                        .map(Arc::from)
132                        .map(Value::Bytes)
133                }
134                other => Err(bad_request!("cannot cast into Bytes from {other:?}")),
135            },
136            Self::Email => match value {
137                Value::Email(email) => Ok(Value::Email(email)),
138
139                Value::Id(id) => parse_email(id.as_str())
140                    .map(Arc::new)
141                    .map(Value::Email)
142                    .ok_or_else(|| bad_request!("cannot cast into an email address from {}", id)),
143
144                Value::String(s) => parse_email(s.as_str())
145                    .map(Arc::new)
146                    .map(Value::Email)
147                    .ok_or_else(|| bad_request!("cannot cast into an email address from {}", s)),
148
149                other => Err(bad_request!(
150                    "cannot cast into an email address from {other:?}"
151                )),
152            },
153            Self::Id => value.try_cast_into(on_err).map(Value::Id),
154            Self::Link => value.try_cast_into(on_err).map(Value::String),
155            Self::None => Ok(Value::None),
156            Self::Number(nt) => value
157                .try_cast_into(|v| bad_request!("cannot cast into Number from {v:?}"))
158                .map(|n| nt.cast(n))
159                .map(Value::Number),
160
161            Self::String => value.try_cast_into(on_err).map(Value::String),
162            Self::Tuple => value.try_cast_into(on_err).map(Value::Tuple),
163            Self::Value => Ok(value),
164            Self::Version => match value {
165                Value::Id(id) => id.as_str().parse().map(Value::Version),
166                Value::String(s) => s.as_str().parse().map(Value::Version),
167                Value::Tuple(t) => {
168                    let (maj, min, rev) =
169                        t.try_cast_into(|t| bad_request!("invalid semantic version {t:?}"))?;
170
171                    Ok(Value::Version(Version::from((maj, min, rev))))
172                }
173                Value::Version(v) => Ok(Value::Version(v)),
174                other => Err(bad_request!("cannot cast into Version from {other:?}")),
175            },
176        }
177    }
178}
179
180impl Default for ValueType {
181    fn default() -> Self {
182        Self::Value
183    }
184}
185
186impl Class for ValueType {}
187
188impl NativeClass for ValueType {
189    fn from_path(path: &[PathSegment]) -> Option<Self> {
190        debug!("ValueType::from_path {}", TCPath::from(path));
191
192        if path.len() >= 3 && &path[..3] == &PREFIX[..] {
193            Self::from_suffix(&path[3..])
194        } else {
195            None
196        }
197    }
198
199    fn path(&self) -> TCPathBuf {
200        let prefix = TCPathBuf::from(PREFIX);
201
202        match self {
203            Self::Bytes => prefix.append(label("bytes")),
204            Self::Email => prefix.append(label("email")),
205            Self::Id => prefix.append(label("id")),
206            Self::Link => prefix.append(label("link")),
207            Self::None => prefix.append(label("none")),
208            Self::Number(nt) => {
209                const N8: Label = label("8");
210                const N16: Label = label("16");
211                const N32: Label = label("32");
212                const N64: Label = label("64");
213
214                let prefix = prefix.append(label("number"));
215                use NumberType as NT;
216                match nt {
217                    NT::Bool => prefix.append(label("bool")),
218                    NT::Complex(ct) => {
219                        let prefix = prefix.append(label("complex"));
220                        use ComplexType as CT;
221                        match ct {
222                            CT::C32 => prefix.append(N32),
223                            CT::C64 => prefix.append(N64),
224                            CT::Complex => prefix,
225                        }
226                    }
227                    NT::Float(ft) => {
228                        let prefix = prefix.append(label("float"));
229                        use FloatType as FT;
230                        match ft {
231                            FT::F32 => prefix.append(N32),
232                            FT::F64 => prefix.append(N64),
233                            FT::Float => prefix,
234                        }
235                    }
236                    NT::Int(it) => {
237                        let prefix = prefix.append(label("int"));
238                        use IntType as IT;
239                        match it {
240                            IT::I8 => prefix.append(N16),
241                            IT::I16 => prefix.append(N16),
242                            IT::I32 => prefix.append(N32),
243                            IT::I64 => prefix.append(N64),
244                            IT::Int => prefix,
245                        }
246                    }
247                    NT::UInt(ut) => {
248                        let prefix = prefix.append(label("uint"));
249                        use UIntType as UT;
250                        match ut {
251                            UT::U8 => prefix.append(N8),
252                            UT::U16 => prefix.append(N16),
253                            UT::U32 => prefix.append(N32),
254                            UT::U64 => prefix.append(N64),
255                            UT::UInt => prefix,
256                        }
257                    }
258                    NT::Number => prefix,
259                }
260            }
261            Self::String => prefix.append(label("string")),
262            Self::Tuple => prefix.append(label("tuple")),
263            Self::Value => prefix,
264            Self::Version => prefix.append(label("version")),
265        }
266    }
267}
268
269impl Ord for ValueType {
270    fn cmp(&self, other: &Self) -> Ordering {
271        use Ordering::*;
272
273        match (self, other) {
274            (Self::Number(l), Self::Number(r)) => l.cmp(r),
275            (l, r) if l == r => Equal,
276
277            (Self::Value, _) => Greater,
278            (_, Self::Value) => Less,
279
280            (Self::Tuple, _) => Greater,
281            (_, Self::Tuple) => Less,
282
283            (Self::Link, _) => Greater,
284            (_, Self::Link) => Less,
285
286            (Self::String, _) => Greater,
287            (_, Self::String) => Less,
288
289            (Self::Email, _) => Greater,
290            (_, Self::Email) => Less,
291
292            (Self::Id, _) => Greater,
293            (_, Self::Id) => Less,
294
295            (Self::Bytes, _) => Greater,
296            (_, Self::Bytes) => Less,
297
298            (Self::Number(_), _) => Greater,
299            (_, Self::Number(_)) => Less,
300
301            (Self::Version, _) => Greater,
302            (_, Self::Version) => Less,
303
304            (Self::None, Self::None) => Equal,
305        }
306    }
307}
308
309impl PartialOrd for ValueType {
310    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
311        Some(self.cmp(other))
312    }
313}
314
315impl From<NumberType> for ValueType {
316    fn from(nt: NumberType) -> ValueType {
317        ValueType::Number(nt)
318    }
319}
320
321impl TryCastFrom<Value> for ValueType {
322    fn can_cast_from(value: &Value) -> bool {
323        match value {
324            Value::Link(l) if l.host().is_none() => Self::from_path(l.path()).is_some(),
325            Value::String(s) => {
326                if let Ok(path) = TCPathBuf::from_str(s.as_str()) {
327                    Self::from_path(&path).is_some()
328                } else {
329                    false
330                }
331            }
332            _ => false,
333        }
334    }
335
336    fn opt_cast_from(value: Value) -> Option<Self> {
337        if let Some(path) = TCPathBuf::opt_cast_from(value) {
338            Self::from_path(&path)
339        } else {
340            None
341        }
342    }
343}
344
345impl TryFrom<ValueType> for NumberType {
346    type Error = TCError;
347
348    fn try_from(vt: ValueType) -> TCResult<NumberType> {
349        match vt {
350            ValueType::Number(nt) => Ok(nt),
351            other => Err(TCError::unexpected(other, "a Number class")),
352        }
353    }
354}
355
356#[async_trait]
357impl de::FromStream for ValueType {
358    type Context = ();
359
360    async fn from_stream<D: de::Decoder>(_: (), decoder: &mut D) -> Result<Self, D::Error> {
361        debug!("ValueType::from_stream");
362        decoder.decode_any(ValueTypeVisitor).await
363    }
364}
365
366impl<'en> en::IntoStream<'en> for ValueType {
367    fn into_stream<E: en::Encoder<'en>>(self, encoder: E) -> Result<E::Ok, E::Error> {
368        use en::EncodeMap;
369        let mut map = encoder.encode_map(Some(1))?;
370        map.encode_entry(self.path(), Map::<Value>::default())?;
371        map.end()
372    }
373}
374
375impl<'en> en::ToStream<'en> for ValueType {
376    fn to_stream<E: en::Encoder<'en>>(&'en self, encoder: E) -> Result<E::Ok, E::Error> {
377        use en::EncodeMap;
378        let mut map = encoder.encode_map(Some(1))?;
379        map.encode_entry(self.path(), Map::<Value>::default())?;
380        map.end()
381    }
382}
383
384impl fmt::Display for ValueType {
385    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
386        match self {
387            Self::Bytes => f.write_str("type Bytes"),
388            Self::Email => f.write_str("type Email"),
389            Self::Id => f.write_str("type Id"),
390            Self::Link => f.write_str("type Link"),
391            Self::None => f.write_str("type None"),
392            Self::Number(nt) => write!(f, "type {}", nt),
393            Self::String => f.write_str("type String"),
394            Self::Tuple => f.write_str("type Tuple<Value>"),
395            Self::Value => f.write_str("type Value"),
396            Self::Version => f.write_str("type Version"),
397        }
398    }
399}
400
401struct ValueTypeVisitor;
402
403impl ValueTypeVisitor {
404    fn visit_path<E: DestreamError>(self, path: TCPathBuf) -> Result<ValueType, E> {
405        ValueType::from_path(&path)
406            .ok_or_else(|| DestreamError::invalid_value(path, Self::expecting()))
407    }
408}
409
410#[async_trait]
411impl DestreamVisitor for ValueTypeVisitor {
412    type Value = ValueType;
413
414    fn expecting() -> &'static str {
415        "a Value type"
416    }
417
418    fn visit_string<E: DestreamError>(self, v: String) -> Result<Self::Value, E> {
419        let path: TCPathBuf = v.parse().map_err(DestreamError::custom)?;
420        self.visit_path(path)
421    }
422
423    async fn visit_map<A: de::MapAccess>(self, mut access: A) -> Result<Self::Value, A::Error> {
424        if let Some(path) = access.next_key::<TCPathBuf>(()).await? {
425            if let Ok(map) = access.next_value::<Map<Value>>(()).await {
426                if map.is_empty() {
427                    self.visit_path(path)
428                } else {
429                    Err(de::Error::custom(format!(
430                        "invalid specification {:?} for Value class {}",
431                        map, path
432                    )))
433                }
434            } else if let Ok(list) = access.next_value::<Tuple<Value>>(()).await {
435                if list.is_empty() {
436                    self.visit_path(path)
437                } else {
438                    Err(de::Error::custom(format!(
439                        "invalid specification {:?} for Value class {}",
440                        list, path
441                    )))
442                }
443            } else {
444                Err(de::Error::custom(format!(
445                    "invalid specification for Value class {}",
446                    path
447                )))
448            }
449        } else {
450            Err(DestreamError::invalid_length(0, Self::expecting()))
451        }
452    }
453}
454
455fn parse_email(s: &str) -> Option<EmailAddress> {
456    EmailAddress::parse(s.as_ref(), None)
457}