spacetimedb_sats/
algebraic_type.rs

1pub mod fmt;
2pub mod map_notation;
3
4use crate::algebraic_value::de::{ValueDeserializeError, ValueDeserializer};
5use crate::algebraic_value::ser::value_serialize;
6use crate::de::Deserialize;
7use crate::meta_type::MetaType;
8use crate::product_type::{CONNECTION_ID_TAG, IDENTITY_TAG, TIMESTAMP_TAG, TIME_DURATION_TAG, UUID_TAG};
9use crate::sum_type::{OPTION_NONE_TAG, OPTION_SOME_TAG, RESULT_ERR_TAG, RESULT_OK_TAG};
10use crate::typespace::Typespace;
11use crate::{i256, u256};
12use crate::{AlgebraicTypeRef, AlgebraicValue, ArrayType, ProductType, SpacetimeType, SumType, SumTypeVariant};
13use derive_more::From;
14use enum_as_inner::EnumAsInner;
15
16/// The SpacetimeDB Algebraic Type System (SATS) is a structural type system in
17/// which a nominal type system can be constructed.
18///
19/// The type system unifies the concepts sum types, product types, scalar value types,
20/// and convenience types strings, arrays, and maps,
21/// into a single type system.
22#[derive(EnumAsInner, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, SpacetimeType, From)]
23#[sats(crate = crate)]
24pub enum AlgebraicType {
25    /// A type where the definition is given by the typing context (`Typespace`).
26    /// In other words, this is defined by a pointer to another `AlgebraicType`.
27    ///
28    /// This should not be conflated with reference and pointer types in languages like Rust,
29    /// In other words, this is not `&T` or `*const T`.
30    Ref(AlgebraicTypeRef),
31    /// A structural sum type.
32    ///
33    /// Unlike most languages, sums in SATs are *[structural]* and not nominal.
34    /// When checking whether two nominal types are the same,
35    /// their names and/or declaration sites (e.g., module / namespace) are considered.
36    /// Meanwhile, a structural type system would only check the structure of the type itself,
37    /// e.g., the names of its variants and their inner data types in the case of a sum.
38    ///
39    /// This is also known as a discriminated union (implementation) or disjoint union.
40    /// Another name is [coproduct (category theory)](https://ncatlab.org/nlab/show/coproduct).
41    ///
42    /// These structures are known as sum types because the number of possible values a sum
43    /// ```text
44    /// { N_0(T_0), N_1(T_1), ..., N_n(T_n) }
45    /// ```
46    /// is:
47    /// ```text
48    /// Σ (i ∈ 0..n). values(T_i)
49    /// ```
50    /// so for example, `values({ A(U64), B(Bool) }) = values(U64) + values(Bool)`.
51    ///
52    /// See also:
53    /// - <https://en.wikipedia.org/wiki/Tagged_union>
54    /// - <https://ncatlab.org/nlab/show/sum+type>
55    ///
56    /// [structural]: https://en.wikipedia.org/wiki/Structural_type_system
57    Sum(SumType),
58    /// A structural product type.
59    ///
60    /// This is also known as `struct` and `tuple` in many languages,
61    /// but note that unlike most languages, sums in SATs are *[structural]* and not nominal.
62    /// When checking whether two nominal types are the same,
63    /// their names and/or declaration sites (e.g., module / namespace) are considered.
64    /// Meanwhile, a structural type system would only check the structure of the type itself,
65    /// e.g., the names of its fields and their types in the case of a record.
66    /// The name "product" comes from category theory.
67    ///
68    /// See also:
69    /// - <https://en.wikipedia.org/wiki/Record_(computer_science)>
70    /// - <https://ncatlab.org/nlab/show/product+type>
71    ///
72    /// These structures are known as product types because the number of possible values in product
73    /// ```text
74    /// { N_0: T_0, N_1: T_1, ..., N_n: T_n }
75    /// ```
76    /// is:
77    /// ```text
78    /// Π (i ∈ 0..n). values(T_i)
79    /// ```
80    /// so for example, `values({ A: U64, B: Bool }) = values(U64) * values(Bool)`.
81    ///
82    /// [structural]: https://en.wikipedia.org/wiki/Structural_type_system
83    Product(ProductType),
84    /// The type of array values where elements are of a base type `elem_ty`.
85    /// Values [`AlgebraicValue::Array(array)`](crate::AlgebraicValue::Array) will have this type.
86    Array(ArrayType),
87    /// The UTF-8 encoded `String` type.
88    /// Values [`AlgebraicValue::String(s)`](crate::AlgebraicValue::String) will have this type.
89    ///
90    /// This type exists for convenience and because it is easy to just use Rust's `String` (UTF-8)
91    /// as opposed to rolling your own equivalent byte-array based UTF-8 encoding.
92    String,
93    /// The bool type. Values [`AlgebraicValue::Bool(b)`](crate::AlgebraicValue::Bool) will have this type.
94    Bool,
95    /// The `I8` type. Values [`AlgebraicValue::I8(v)`](crate::AlgebraicValue::I8) will have this type.
96    I8,
97    /// The `U8` type. Values [`AlgebraicValue::U8(v)`](crate::AlgebraicValue::U8) will have this type.
98    U8,
99    /// The `I16` type. Values [`AlgebraicValue::I16(v)`](crate::AlgebraicValue::I16) will have this type.
100    I16,
101    /// The `U16` type. Values [`AlgebraicValue::U16(v)`](crate::AlgebraicValue::U16) will have this type.
102    U16,
103    /// The `I32` type. Values [`AlgebraicValue::I32(v)`](crate::AlgebraicValue::I32) will have this type.
104    I32,
105    /// The `U32` type. Values [`AlgebraicValue::U32(v)`](crate::AlgebraicValue::U32) will have this type.
106    U32,
107    /// The `I64` type. Values [`AlgebraicValue::I64(v)`](crate::AlgebraicValue::I64) will have this type.
108    I64,
109    /// The `U64` type. Values [`AlgebraicValue::U64(v)`](crate::AlgebraicValue::U64) will have this type.
110    U64,
111    /// The `I128` type. Values [`AlgebraicValue::I128(v)`](crate::AlgebraicValue::I128) will have this type.
112    I128,
113    /// The `U128` type. Values [`AlgebraicValue::U128(v)`](crate::AlgebraicValue::U128) will have this type.
114    U128,
115    /// The `I256` type. Values [`AlgebraicValue::I256(v)`](crate::AlgebraicValue::I256) will have this type.
116    I256,
117    /// The `U256` type. Values [`AlgebraicValue::U256(v)`](crate::AlgebraicValue::U256) will have this type.
118    U256,
119    /// The `F32` type. Values [`AlgebraicValue::F32(v)`](crate::AlgebraicValue::F32) will have this type.
120    F32,
121    /// The `F64` type. Values [`AlgebraicValue::F64(v)`](crate::AlgebraicValue::F64) will have this type.
122    F64,
123}
124
125impl MetaType for AlgebraicType {
126    /// This is a static function that constructs the type of `AlgebraicType`
127    /// and returns it as an `AlgebraicType`.
128    ///
129    /// This could alternatively be implemented
130    /// as a regular AlgebraicValue or as a static variable.
131    fn meta_type() -> Self {
132        AlgebraicType::sum([
133            ("ref", AlgebraicTypeRef::meta_type()),
134            ("sum", SumType::meta_type()),
135            ("product", ProductType::meta_type()),
136            ("array", ArrayType::meta_type()),
137            ("string", AlgebraicType::unit()),
138            ("bool", AlgebraicType::unit()),
139            ("i8", AlgebraicType::unit()),
140            ("u8", AlgebraicType::unit()),
141            ("i16", AlgebraicType::unit()),
142            ("u16", AlgebraicType::unit()),
143            ("i32", AlgebraicType::unit()),
144            ("u32", AlgebraicType::unit()),
145            ("i64", AlgebraicType::unit()),
146            ("u64", AlgebraicType::unit()),
147            ("i128", AlgebraicType::unit()),
148            ("u128", AlgebraicType::unit()),
149            ("i256", AlgebraicType::unit()),
150            ("u256", AlgebraicType::unit()),
151            ("f32", AlgebraicType::unit()),
152            ("f64", AlgebraicType::unit()),
153        ])
154    }
155}
156
157/// Provided to enable `mem::take`.
158impl Default for AlgebraicType {
159    fn default() -> Self {
160        Self::ZERO_REF
161    }
162}
163
164impl AlgebraicType {
165    /// The first type in the typespace.
166    pub const ZERO_REF: Self = Self::Ref(AlgebraicTypeRef(0));
167
168    /// Returns whether this type is the `ConnectionId` type.
169    ///
170    /// Construct an instance of this type with [`Self::connection_id`]
171    pub fn is_connection_id(&self) -> bool {
172        matches!(self, Self::Product(p) if p.is_connection_id())
173    }
174
175    /// Returns whether this type is the conventional identity type.
176    pub fn is_identity(&self) -> bool {
177        matches!(self, Self::Product(p) if p.is_identity())
178    }
179
180    /// Returns whether this type is the conventional point-in-time `Timestamp` type.
181    pub fn is_timestamp(&self) -> bool {
182        matches!(self, Self::Product(p) if p.is_timestamp())
183    }
184
185    /// Returns whether this type is the conventional time-delta `TimeDuration` type.
186    pub fn is_time_duration(&self) -> bool {
187        matches!(self, Self::Product(p) if p.is_time_duration())
188    }
189
190    /// Returns whether this type is the conventional `UUID` type.
191    pub fn is_uuid(&self) -> bool {
192        matches!(self, Self::Product(p) if p.is_uuid())
193    }
194
195    /// Returns whether this type is the conventional `ScheduleAt` type.
196    pub fn is_schedule_at(&self) -> bool {
197        matches!(self, Self::Sum(p) if p.is_schedule_at())
198    }
199
200    /// Returns whether this type is a unit type.
201    pub fn is_unit(&self) -> bool {
202        matches!(self, Self::Product(p) if p.is_unit())
203    }
204
205    /// Returns whether this type is a never type.
206    pub fn is_never(&self) -> bool {
207        matches!(self, Self::Sum(p) if p.is_empty())
208    }
209
210    /// Returns whether this type is an option type.
211    pub fn is_option(&self) -> bool {
212        matches!(self, Self::Sum(p) if p.is_option())
213    }
214
215    /// If this type is the standard option type, returns the type of the `some` variant.
216    /// Otherwise, returns `None`.
217    pub fn as_option(&self) -> Option<&AlgebraicType> {
218        self.as_sum()?.as_option()
219    }
220
221    /// Returns whether this type is a result type.
222    pub fn is_result(&self) -> bool {
223        matches!(self, Self::Sum(p) if p.is_result())
224    }
225
226    /// If this type is the standard result type, returns the types of the `ok` and `err` variants.
227    /// Otherwise, returns `None`.
228    pub fn as_result(&self) -> Option<(&AlgebraicType, &AlgebraicType)> {
229        self.as_sum()?.as_result()
230    }
231
232    /// Returns whether this type is scalar or a string type.
233    pub fn is_scalar_or_string(&self) -> bool {
234        self.is_scalar() || self.is_string()
235    }
236
237    /// Returns whether this type is one which holds a scalar value.
238    ///
239    /// A scalar value is one not made up of other values, i.e., not composite.
240    /// These are all integer and float values,
241    /// i.e., integer and float types are scalar.
242    /// References to other types, i.e., [`AlgebraicType::Ref`]s are not scalar.
243    pub fn is_scalar(&self) -> bool {
244        self.is_bool() || self.is_integer() || self.is_float()
245    }
246
247    /// Returns whether the type is a signed integer type.
248    pub fn is_signed(&self) -> bool {
249        matches!(
250            self,
251            Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 | Self::I256
252        )
253    }
254
255    /// Returns whether the type is an unsigned integer type.
256    pub fn is_unsigned(&self) -> bool {
257        matches!(
258            self,
259            Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::U128 | Self::U256
260        )
261    }
262
263    /// Returns whether this type is one of the integer types, e.g., `U64` and `I32`.
264    pub fn is_integer(&self) -> bool {
265        self.is_signed() || self.is_unsigned()
266    }
267
268    /// Returns whether the type is a float type.
269    pub fn is_float(&self) -> bool {
270        matches!(self, Self::F32 | Self::F64)
271    }
272
273    /// The canonical 0-element unit type.
274    pub fn unit() -> Self {
275        let fs: [AlgebraicType; 0] = [];
276        Self::product(fs)
277    }
278
279    /// The canonical 0-variant "never" / "absurd" / "void" type.
280    pub fn never() -> Self {
281        let vs: [SumTypeVariant; 0] = [];
282        Self::sum(vs)
283    }
284
285    /// A type representing an array of `U8`s.
286    pub fn bytes() -> Self {
287        Self::array(Self::U8)
288    }
289
290    /// Returns whether this type is `AlgebraicType::bytes()`.
291    pub fn is_bytes(&self) -> bool {
292        self.as_array().is_some_and(|ty| ty.elem_ty.is_u8())
293    }
294
295    /// Whether this type, or the types it references, contain any `AlgebraicTypeRef`s.
296    pub fn contains_refs(&self) -> bool {
297        match self {
298            AlgebraicType::Ref(_) => true,
299            AlgebraicType::Product(ProductType { elements }) => {
300                elements.iter().any(|elem| elem.algebraic_type.contains_refs())
301            }
302            AlgebraicType::Sum(SumType { variants }) => {
303                variants.iter().any(|variant| variant.algebraic_type.contains_refs())
304            }
305            AlgebraicType::Array(array) => array.elem_ty.contains_refs(),
306            _ => false,
307        }
308    }
309
310    /// Returns a sum type with the given `sum`.
311    pub fn sum<S: Into<SumType>>(sum: S) -> Self {
312        AlgebraicType::Sum(sum.into())
313    }
314
315    /// Returns a product type with the given `prod`.
316    pub fn product<P: Into<ProductType>>(prod: P) -> Self {
317        AlgebraicType::Product(prod.into())
318    }
319
320    /// Returns a structural option type where `some_type` is the type for the `some` variant.
321    pub fn option(some_type: Self) -> Self {
322        Self::sum([(OPTION_SOME_TAG, some_type), (OPTION_NONE_TAG, AlgebraicType::unit())])
323    }
324
325    /// Returns a structural result type where `ok_type` is the type for the `ok` variant
326    /// and `err_type` is the type for the `err` variant.
327    pub fn result(ok_type: Self, err_type: Self) -> Self {
328        Self::sum([(RESULT_OK_TAG, ok_type), (RESULT_ERR_TAG, err_type)])
329    }
330
331    /// Returns an unsized array type where the element type is `ty`.
332    pub fn array(ty: Self) -> Self {
333        ArrayType { elem_ty: Box::new(ty) }.into()
334    }
335
336    /// Construct a copy of the `Identity` type.
337    pub fn identity() -> Self {
338        AlgebraicType::product([(IDENTITY_TAG, AlgebraicType::U256)])
339    }
340
341    /// Construct a copy of the `ConnectionId` type.
342    pub fn connection_id() -> Self {
343        AlgebraicType::product([(CONNECTION_ID_TAG, AlgebraicType::U128)])
344    }
345
346    /// Construct a copy of the point-in-time `Timestamp` type.
347    pub fn timestamp() -> Self {
348        AlgebraicType::product([(TIMESTAMP_TAG, AlgebraicType::I64)])
349    }
350
351    /// Construct a copy of the time-delta `TimeDuration` type.
352    pub fn time_duration() -> Self {
353        AlgebraicType::product([(TIME_DURATION_TAG, AlgebraicType::I64)])
354    }
355
356    /// Construct a copy of the `UUID` type.
357    pub fn uuid() -> Self {
358        AlgebraicType::product([(UUID_TAG, AlgebraicType::U128)])
359    }
360
361    /// Returns a sum type of unit variants with names taken from `var_names`.
362    pub fn simple_enum<'a>(var_names: impl Iterator<Item = &'a str>) -> Self {
363        Self::sum(var_names.into_iter().map(SumTypeVariant::unit).collect::<Box<[_]>>())
364    }
365
366    pub fn as_value(&self) -> AlgebraicValue {
367        value_serialize(self)
368    }
369
370    pub fn from_value(value: &AlgebraicValue) -> Result<Self, ValueDeserializeError> {
371        Self::deserialize(ValueDeserializer::from_ref(value))
372    }
373
374    #[inline]
375    /// Given an AlgebraicType, returns the min value for that type.
376    pub fn min_value(&self) -> Option<AlgebraicValue> {
377        match *self {
378            Self::I8 => Some(i8::MIN.into()),
379            Self::U8 => Some(u8::MIN.into()),
380            Self::I16 => Some(i16::MIN.into()),
381            Self::U16 => Some(u16::MIN.into()),
382            Self::I32 => Some(i32::MIN.into()),
383            Self::U32 => Some(u32::MIN.into()),
384            Self::I64 => Some(i64::MIN.into()),
385            Self::U64 => Some(u64::MIN.into()),
386            Self::I128 => Some(i128::MIN.into()),
387            Self::U128 => Some(u128::MIN.into()),
388            Self::I256 => Some(i256::MIN.into()),
389            Self::U256 => Some(u256::MIN.into()),
390            Self::F32 => Some(f32::MIN.into()),
391            Self::F64 => Some(f64::MIN.into()),
392            _ => None,
393        }
394    }
395
396    #[inline]
397    /// Given an AlgebraicType, returns the max value for that type.
398    pub fn max_value(&self) -> Option<AlgebraicValue> {
399        match *self {
400            Self::I8 => Some(i8::MAX.into()),
401            Self::U8 => Some(u8::MAX.into()),
402            Self::I16 => Some(i16::MAX.into()),
403            Self::U16 => Some(u16::MAX.into()),
404            Self::I32 => Some(i32::MAX.into()),
405            Self::U32 => Some(u32::MAX.into()),
406            Self::I64 => Some(i64::MAX.into()),
407            Self::U64 => Some(u64::MAX.into()),
408            Self::I128 => Some(i128::MAX.into()),
409            Self::U128 => Some(u128::MAX.into()),
410            Self::I256 => Some(i256::MAX.into()),
411            Self::U256 => Some(u256::MAX.into()),
412            Self::F32 => Some(f32::MAX.into()),
413            Self::F64 => Some(f64::MAX.into()),
414            _ => None,
415        }
416    }
417
418    /// Check if the type is one of a small number of special, known types
419    /// with specific layouts.
420    /// See also [`ProductType::is_special`] and [`SumType::is_special`].
421    pub fn is_special(&self) -> bool {
422        match self {
423            AlgebraicType::Product(product) => product.is_special(),
424            AlgebraicType::Sum(sum) => sum.is_special(),
425            _ => false,
426        }
427    }
428
429    /// Validates that the type can be used to generate a type definition
430    /// in a `SpacetimeDB` client module.
431    ///
432    /// Such a type must be a non-special sum or product type.
433    /// All of the elements of the type must satisfy [`AlgebraicType::is_valid_for_client_type_use`].
434    ///
435    /// This method does not actually follow `Ref`s to check the types they point to,
436    /// it only checks the structure of this type.
437    pub fn is_valid_for_client_type_definition(&self) -> bool {
438        // Special types should not be used to generate type definitions.
439        if self.is_special() {
440            return false;
441        }
442        match self {
443            AlgebraicType::Sum(sum) => sum
444                .variants
445                .iter()
446                .all(|variant| variant.algebraic_type.is_valid_for_client_type_use()),
447            AlgebraicType::Product(product) => product
448                .elements
449                .iter()
450                .all(|elem| elem.algebraic_type.is_valid_for_client_type_use()),
451            _ => false,
452        }
453    }
454
455    /// Validates that the type can be used to generate a *use* of a type in a `SpacetimeDB` client module.
456    /// (As opposed to a *definition* of a type.)
457    ///
458    /// This means that the type is either:
459    /// - a reference
460    /// - a special, known type
461    /// - a non-compound type like `U8`, `I32`, `F64`, etc.
462    /// - or a map, array, option, or result built from types that satisfy [`AlgebraicType::is_valid_for_client_type_use`]
463    ///
464    /// This method does not actually follow `Ref`s to check the types they point to,
465    /// it only checks the structure of the type.
466    pub fn is_valid_for_client_type_use(&self) -> bool {
467        match self {
468            AlgebraicType::Sum(sum) => {
469                if let Some(wrapped) = sum.as_option() {
470                    wrapped.is_valid_for_client_type_use()
471                } else if let Some((ok_ty, err_ty)) = sum.as_result() {
472                    ok_ty.is_valid_for_client_type_use() && err_ty.is_valid_for_client_type_use()
473                } else {
474                    sum.is_special() || sum.is_empty()
475                }
476            }
477            AlgebraicType::Product(product) => product.is_special() || product.is_unit(),
478            AlgebraicType::Array(array) => array.elem_ty.is_valid_for_client_type_use(),
479            AlgebraicType::Ref(_) => true,
480            _ => true,
481        }
482    }
483
484    pub fn type_check(&self, value: &AlgebraicValue, typespace: &Typespace) -> bool {
485        match (self, value) {
486            (_, AlgebraicValue::Min | AlgebraicValue::Max) => true,
487            (AlgebraicType::Ref(r), _) => {
488                if let Some(resolved_ty) = typespace.get(*r) {
489                    resolved_ty.type_check(value, typespace)
490                } else {
491                    false
492                }
493            }
494            (AlgebraicType::Sum(sum_ty), AlgebraicValue::Sum(sv)) => sum_ty.type_check(sv, typespace),
495            (AlgebraicType::Product(product_ty), AlgebraicValue::Product(pv)) => product_ty.type_check(pv, typespace),
496            (AlgebraicType::Array(array_ty), AlgebraicValue::Array(arr)) => array_ty.type_check(arr, typespace),
497
498            (AlgebraicType::String, AlgebraicValue::String(_))
499            | (AlgebraicType::Bool, AlgebraicValue::Bool(_))
500            | (AlgebraicType::I8, AlgebraicValue::I8(_))
501            | (AlgebraicType::U8, AlgebraicValue::U8(_))
502            | (AlgebraicType::I16, AlgebraicValue::I16(_))
503            | (AlgebraicType::U16, AlgebraicValue::U16(_))
504            | (AlgebraicType::I32, AlgebraicValue::I32(_))
505            | (AlgebraicType::U32, AlgebraicValue::U32(_))
506            | (AlgebraicType::I64, AlgebraicValue::I64(_))
507            | (AlgebraicType::U64, AlgebraicValue::U64(_))
508            | (AlgebraicType::I128, AlgebraicValue::I128(_))
509            | (AlgebraicType::U128, AlgebraicValue::U128(_))
510            | (AlgebraicType::I256, AlgebraicValue::I256(_))
511            | (AlgebraicType::U256, AlgebraicValue::U256(_))
512            | (AlgebraicType::F32, AlgebraicValue::F32(_))
513            | (AlgebraicType::F64, AlgebraicValue::F64(_)) => true,
514            _ => false,
515        }
516    }
517}
518#[cfg(test)]
519mod tests {
520    use super::AlgebraicType;
521    use crate::meta_type::MetaType;
522    use crate::satn::Satn;
523    use crate::{
524        algebraic_type::fmt::fmt_algebraic_type, algebraic_type::map_notation::fmt_algebraic_type as fmt_map,
525        algebraic_type_ref::AlgebraicTypeRef, typespace::Typespace,
526    };
527    use crate::{product, AlgebraicValue, ValueWithType, WithTypespace};
528
529    #[test]
530    fn never() {
531        assert_eq!("(|)", fmt_algebraic_type(&AlgebraicType::never()).to_string());
532    }
533
534    #[test]
535    fn never_map() {
536        assert_eq!("{ ty_: Sum }", fmt_map(&AlgebraicType::never()).to_string());
537    }
538
539    #[test]
540    fn unit() {
541        assert_eq!("()", fmt_algebraic_type(&AlgebraicType::unit()).to_string());
542    }
543
544    #[test]
545    fn unit_map() {
546        assert_eq!("{ ty_: Product }", fmt_map(&AlgebraicType::unit()).to_string());
547    }
548
549    #[test]
550    fn primitive() {
551        assert_eq!("U8", fmt_algebraic_type(&AlgebraicType::U8).to_string());
552    }
553
554    #[test]
555    fn primitive_map() {
556        assert_eq!("{ ty_: U8 }", fmt_map(&AlgebraicType::U8).to_string());
557    }
558
559    #[test]
560    fn option() {
561        let option = AlgebraicType::option(AlgebraicType::never());
562        assert_eq!("(some: (|) | none: ())", fmt_algebraic_type(&option).to_string());
563    }
564
565    #[test]
566    fn option_map() {
567        let option = AlgebraicType::option(AlgebraicType::never());
568        assert_eq!(
569            "{ ty_: Sum, some: { ty_: Sum }, none: { ty_: Product } }",
570            fmt_map(&option).to_string()
571        );
572    }
573
574    #[test]
575    fn result() {
576        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);
577        assert_eq!("(ok: U8 | err: String)", fmt_algebraic_type(&result).to_string());
578    }
579
580    #[test]
581    fn result_map() {
582        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);
583        assert_eq!(
584            "{ ty_: Sum, ok: { ty_: U8 }, err: { ty_: String } }",
585            fmt_map(&result).to_string()
586        );
587    }
588
589    #[test]
590    fn algebraic_type() {
591        let algebraic_type = AlgebraicType::meta_type();
592        assert_eq!(
593            "(\
594                ref: U32 \
595                | sum: (variants: Array<(\
596                    name: (some: String | none: ()), \
597                    algebraic_type: &0\
598                )>) \
599                | product: (elements: Array<(\
600                    name: (some: String | none: ()), \
601                    algebraic_type: &0\
602                )>) \
603                | array: &0 \
604                | string: () \
605                | bool: () \
606                | i8: () | u8: () \
607                | i16: () | u16: () \
608                | i32: () | u32: () \
609                | i64: () | u64: () \
610                | i128: () | u128: () \
611                | i256: () | u256: () \
612                | f32: () | f64: ()\
613            )",
614            fmt_algebraic_type(&algebraic_type).to_string()
615        );
616    }
617
618    #[test]
619    fn algebraic_type_map() {
620        let algebraic_type = AlgebraicType::meta_type();
621        assert_eq!(
622            "{ \
623                ty_: Sum, \
624                ref: { ty_: U32 }, \
625                sum: { \
626                    ty_: Product, \
627                    variants: { \
628                        ty_: Array, \
629                        0: { \
630                            ty_: Product, \
631                            name: { ty_: Sum, some: { ty_: String }, none: { ty_: Product } }, \
632                            algebraic_type: { ty_: Ref, 0: 0 } \
633                        } \
634                    } \
635                }, \
636                product: { \
637                    ty_: Product, \
638                    elements: { \
639                        ty_: Array, \
640                        0: { \
641                            ty_: Product, \
642                            name: { ty_: Sum, some: { ty_: String }, none: { ty_: Product } }, \
643                            algebraic_type: { ty_: Ref, 0: 0 } \
644                        } \
645                    } \
646                }, \
647                array: { ty_: Ref, 0: 0 }, \
648                string: { ty_: Product }, \
649                bool: { ty_: Product }, \
650                i8: { ty_: Product }, u8: { ty_: Product }, \
651                i16: { ty_: Product }, u16: { ty_: Product }, \
652                i32: { ty_: Product }, u32: { ty_: Product }, \
653                i64: { ty_: Product }, u64: { ty_: Product }, \
654                i128: { ty_: Product }, u128: { ty_: Product }, \
655                i256: { ty_: Product }, u256: { ty_: Product }, \
656                f32: { ty_: Product }, f64: { ty_: Product } \
657            }",
658            fmt_map(&algebraic_type).to_string()
659        );
660    }
661
662    #[test]
663    fn nested_products_and_sums() {
664        let builtin = AlgebraicType::U8;
665        let product = AlgebraicType::product([("thing", AlgebraicType::U8)]);
666        let sum = AlgebraicType::sum([builtin.clone(), builtin.clone(), product]);
667        let next = AlgebraicType::product([
668            (Some("test"), builtin.clone()),
669            (None, sum),
670            (None, builtin),
671            (Some("never"), AlgebraicType::never()),
672        ]);
673        assert_eq!(
674            "(test: U8, 1: (U8 | U8 | (thing: U8)), 2: U8, never: (|))",
675            fmt_algebraic_type(&next).to_string()
676        );
677    }
678
679    fn in_space<'a, T: crate::Value>(ts: &'a Typespace, ty: &'a T::Type, val: &'a T) -> ValueWithType<'a, T> {
680        WithTypespace::new(ts, ty).with_value(val)
681    }
682
683    #[test]
684    fn option_as_value() {
685        let option = AlgebraicType::option(AlgebraicType::never());
686        let algebraic_type = AlgebraicType::meta_type();
687        let typespace = Typespace::new(vec![algebraic_type]);
688        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));
689        assert_eq!(
690            r#"(sum = (variants = [(name = (some = "some"), algebraic_type = (sum = (variants = []))), (name = (some = "none"), algebraic_type = (product = (elements = [])))]))"#,
691            in_space(&typespace, &at_ref, &option.as_value()).to_satn()
692        );
693    }
694
695    #[test]
696    fn result_as_value() {
697        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);
698        let algebraic_type = AlgebraicType::meta_type();
699        let typespace = Typespace::new(vec![algebraic_type]);
700        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));
701        assert_eq!(
702            r#"(sum = (variants = [(name = (some = "ok"), algebraic_type = (u8 = ())), (name = (some = "err"), algebraic_type = (string = ()))]))"#,
703            in_space(&typespace, &at_ref, &result.as_value()).to_satn()
704        );
705    }
706
707    #[test]
708    fn algebraic_type_as_value() {
709        let algebraic_type = AlgebraicType::meta_type();
710        let typespace = Typespace::new(vec![algebraic_type.clone()]);
711        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));
712
713        let ref0 = "algebraic_type = (ref = 0)";
714        let unit = "algebraic_type = (product = (elements = []))";
715        let aggr_elems_ty = format!(
716            "algebraic_type = (array = (product = (elements = [\
717                (\
718                    name = (some = \"name\"), \
719                    algebraic_type = (sum = (variants = [\
720                        (name = (some = \"some\"), algebraic_type = (string = ())), \
721                        (name = (some = \"none\"), {unit})\
722                    ]))\
723                ), \
724                (name = (some = \"algebraic_type\"), {ref0})\
725            ])))"
726        );
727
728        assert_eq!(
729            format!(
730                "(\
731                sum = (\
732                    variants = [\
733                        (name = (some = \"ref\"), algebraic_type = (u32 = ())), \
734                        (\
735                            name = (some = \"sum\"), \
736                            algebraic_type = (product = (elements = [\
737                                (name = (some = \"variants\"), {aggr_elems_ty})\
738                            ]))\
739                        ), \
740                        (\
741                            name = (some = \"product\"), \
742                            algebraic_type = (product = (elements = [\
743                                (name = (some = \"elements\"), {aggr_elems_ty})\
744                            ]))\
745                        ), \
746                        (name = (some = \"array\"), {ref0}), \
747                        (name = (some = \"string\"), {unit}), \
748                        (name = (some = \"bool\"), {unit}), \
749                        (name = (some = \"i8\"), {unit}), \
750                        (name = (some = \"u8\"), {unit}), \
751                        (name = (some = \"i16\"), {unit}), \
752                        (name = (some = \"u16\"), {unit}), \
753                        (name = (some = \"i32\"), {unit}), \
754                        (name = (some = \"u32\"), {unit}), \
755                        (name = (some = \"i64\"), {unit}), \
756                        (name = (some = \"u64\"), {unit}), \
757                        (name = (some = \"i128\"), {unit}), \
758                        (name = (some = \"u128\"), {unit}), \
759                        (name = (some = \"i256\"), {unit}), \
760                        (name = (some = \"u256\"), {unit}), \
761                        (name = (some = \"f32\"), {unit}), \
762                        (name = (some = \"f64\"), {unit})\
763                    ]\
764                )\
765            )"
766            ),
767            in_space(&typespace, &at_ref, &algebraic_type.as_value()).to_satn()
768        );
769    }
770
771    #[test]
772    fn option_from_value() {
773        let option = AlgebraicType::option(AlgebraicType::never());
774        AlgebraicType::from_value(&option.as_value()).expect("No errors.");
775    }
776
777    #[test]
778    fn result_from_value() {
779        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);
780        AlgebraicType::from_value(&result.as_value()).expect("No errors.");
781    }
782
783    #[test]
784    fn builtin_from_value() {
785        let u8 = AlgebraicType::U8;
786        AlgebraicType::from_value(&u8.as_value()).expect("No errors.");
787    }
788
789    #[test]
790    fn algebraic_type_from_value() {
791        let algebraic_type = AlgebraicType::meta_type();
792        AlgebraicType::from_value(&algebraic_type.as_value()).expect("No errors.");
793    }
794
795    #[test]
796    fn special_types_are_special() {
797        assert!(AlgebraicType::identity().is_identity());
798        assert!(AlgebraicType::identity().is_special());
799        assert!(AlgebraicType::connection_id().is_connection_id());
800        assert!(AlgebraicType::connection_id().is_special());
801        assert!(AlgebraicType::timestamp().is_timestamp());
802        assert!(AlgebraicType::timestamp().is_special());
803        assert!(AlgebraicType::time_duration().is_special());
804        assert!(AlgebraicType::time_duration().is_time_duration());
805        assert!(AlgebraicType::uuid().is_uuid());
806        assert!(AlgebraicType::uuid().is_special());
807    }
808
809    #[test]
810    fn type_check() {
811        let av = AlgebraicValue::sum(1, AlgebraicValue::from(product![0u16, 1u32]));
812        let at = AlgebraicType::sum([
813            ("a", AlgebraicType::U8),
814            ("b", AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U32])),
815        ]);
816
817        at.type_check(&av, Typespace::EMPTY);
818    }
819}