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};
9use crate::sum_type::{OPTION_NONE_TAG, OPTION_SOME_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 `ScheduleAt` type.
191    pub fn is_schedule_at(&self) -> bool {
192        matches!(self, Self::Sum(p) if p.is_schedule_at())
193    }
194
195    /// Returns whether this type is a unit type.
196    pub fn is_unit(&self) -> bool {
197        matches!(self, Self::Product(p) if p.is_unit())
198    }
199
200    /// Returns whether this type is a never type.
201    pub fn is_never(&self) -> bool {
202        matches!(self, Self::Sum(p) if p.is_empty())
203    }
204
205    /// Returns whether this type is an option type.
206    pub fn is_option(&self) -> bool {
207        matches!(self, Self::Sum(p) if p.is_option())
208    }
209
210    /// If this type is the standard option type, returns the type of the `some` variant.
211    /// Otherwise, returns `None`.
212    pub fn as_option(&self) -> Option<&AlgebraicType> {
213        self.as_sum()?.as_option()
214    }
215
216    /// Returns whether this type is scalar or a string type.
217    pub fn is_scalar_or_string(&self) -> bool {
218        self.is_scalar() || self.is_string()
219    }
220
221    /// Returns whether this type is one which holds a scalar value.
222    ///
223    /// A scalar value is one not made up of other values, i.e., not composite.
224    /// These are all integer and float values,
225    /// i.e., integer and float types are scalar.
226    /// References to other types, i.e., [`AlgebraicType::Ref`]s are not scalar.
227    pub fn is_scalar(&self) -> bool {
228        self.is_bool() || self.is_integer() || self.is_float()
229    }
230
231    /// Returns whether the type is a signed integer type.
232    pub fn is_signed(&self) -> bool {
233        matches!(
234            self,
235            Self::I8 | Self::I16 | Self::I32 | Self::I64 | Self::I128 | Self::I256
236        )
237    }
238
239    /// Returns whether the type is an unsigned integer type.
240    pub fn is_unsigned(&self) -> bool {
241        matches!(
242            self,
243            Self::U8 | Self::U16 | Self::U32 | Self::U64 | Self::U128 | Self::U256
244        )
245    }
246
247    /// Returns whether this type is one of the integer types, e.g., `U64` and `I32`.
248    pub fn is_integer(&self) -> bool {
249        self.is_signed() || self.is_unsigned()
250    }
251
252    /// Returns whether the type is a float type.
253    pub fn is_float(&self) -> bool {
254        matches!(self, Self::F32 | Self::F64)
255    }
256
257    /// The canonical 0-element unit type.
258    pub fn unit() -> Self {
259        let fs: [AlgebraicType; 0] = [];
260        Self::product(fs)
261    }
262
263    /// The canonical 0-variant "never" / "absurd" / "void" type.
264    pub fn never() -> Self {
265        let vs: [SumTypeVariant; 0] = [];
266        Self::sum(vs)
267    }
268
269    /// A type representing an array of `U8`s.
270    pub fn bytes() -> Self {
271        Self::array(Self::U8)
272    }
273
274    /// Returns whether this type is `AlgebraicType::bytes()`.
275    pub fn is_bytes(&self) -> bool {
276        self.as_array().is_some_and(|ty| ty.elem_ty.is_u8())
277    }
278
279    /// Whether this type, or the types it references, contain any `AlgebraicTypeRef`s.
280    pub fn contains_refs(&self) -> bool {
281        match self {
282            AlgebraicType::Ref(_) => true,
283            AlgebraicType::Product(ProductType { elements }) => {
284                elements.iter().any(|elem| elem.algebraic_type.contains_refs())
285            }
286            AlgebraicType::Sum(SumType { variants }) => {
287                variants.iter().any(|variant| variant.algebraic_type.contains_refs())
288            }
289            AlgebraicType::Array(array) => array.elem_ty.contains_refs(),
290            _ => false,
291        }
292    }
293
294    /// Returns a sum type with the given `sum`.
295    pub fn sum<S: Into<SumType>>(sum: S) -> Self {
296        AlgebraicType::Sum(sum.into())
297    }
298
299    /// Returns a product type with the given `prod`.
300    pub fn product<P: Into<ProductType>>(prod: P) -> Self {
301        AlgebraicType::Product(prod.into())
302    }
303
304    /// Returns a structural option type where `some_type` is the type for the `some` variant.
305    pub fn option(some_type: Self) -> Self {
306        Self::sum([(OPTION_SOME_TAG, some_type), (OPTION_NONE_TAG, AlgebraicType::unit())])
307    }
308
309    /// Returns an unsized array type where the element type is `ty`.
310    pub fn array(ty: Self) -> Self {
311        ArrayType { elem_ty: Box::new(ty) }.into()
312    }
313
314    /// Construct a copy of the `Identity` type.
315    pub fn identity() -> Self {
316        AlgebraicType::product([(IDENTITY_TAG, AlgebraicType::U256)])
317    }
318
319    /// Construct a copy of the `ConnectionId` type.
320    pub fn connection_id() -> Self {
321        AlgebraicType::product([(CONNECTION_ID_TAG, AlgebraicType::U128)])
322    }
323
324    /// Construct a copy of the point-in-time `Timestamp` type.
325    pub fn timestamp() -> Self {
326        AlgebraicType::product([(TIMESTAMP_TAG, AlgebraicType::I64)])
327    }
328
329    /// Construct a copy of the time-delta `TimeDuration` type.
330    pub fn time_duration() -> Self {
331        AlgebraicType::product([(TIME_DURATION_TAG, AlgebraicType::I64)])
332    }
333
334    /// Returns a sum type of unit variants with names taken from `var_names`.
335    pub fn simple_enum<'a>(var_names: impl Iterator<Item = &'a str>) -> Self {
336        Self::sum(var_names.into_iter().map(SumTypeVariant::unit).collect::<Box<[_]>>())
337    }
338
339    pub fn as_value(&self) -> AlgebraicValue {
340        value_serialize(self)
341    }
342
343    pub fn from_value(value: &AlgebraicValue) -> Result<Self, ValueDeserializeError> {
344        Self::deserialize(ValueDeserializer::from_ref(value))
345    }
346
347    #[inline]
348    /// Given an AlgebraicType, returns the min value for that type.
349    pub fn min_value(&self) -> Option<AlgebraicValue> {
350        match *self {
351            Self::I8 => Some(i8::MIN.into()),
352            Self::U8 => Some(u8::MIN.into()),
353            Self::I16 => Some(i16::MIN.into()),
354            Self::U16 => Some(u16::MIN.into()),
355            Self::I32 => Some(i32::MIN.into()),
356            Self::U32 => Some(u32::MIN.into()),
357            Self::I64 => Some(i64::MIN.into()),
358            Self::U64 => Some(u64::MIN.into()),
359            Self::I128 => Some(i128::MIN.into()),
360            Self::U128 => Some(u128::MIN.into()),
361            Self::I256 => Some(i256::MIN.into()),
362            Self::U256 => Some(u256::MIN.into()),
363            Self::F32 => Some(f32::MIN.into()),
364            Self::F64 => Some(f64::MIN.into()),
365            _ => None,
366        }
367    }
368
369    #[inline]
370    /// Given an AlgebraicType, returns the max value for that type.
371    pub fn max_value(&self) -> Option<AlgebraicValue> {
372        match *self {
373            Self::I8 => Some(i8::MAX.into()),
374            Self::U8 => Some(u8::MAX.into()),
375            Self::I16 => Some(i16::MAX.into()),
376            Self::U16 => Some(u16::MAX.into()),
377            Self::I32 => Some(i32::MAX.into()),
378            Self::U32 => Some(u32::MAX.into()),
379            Self::I64 => Some(i64::MAX.into()),
380            Self::U64 => Some(u64::MAX.into()),
381            Self::I128 => Some(i128::MAX.into()),
382            Self::U128 => Some(u128::MAX.into()),
383            Self::I256 => Some(i256::MAX.into()),
384            Self::U256 => Some(u256::MAX.into()),
385            Self::F32 => Some(f32::MAX.into()),
386            Self::F64 => Some(f64::MAX.into()),
387            _ => None,
388        }
389    }
390
391    /// Check if the type is one of a small number of special, known types
392    /// with specific layouts.
393    /// See also [`ProductType::is_special`] and [`SumType::is_special`].
394    pub fn is_special(&self) -> bool {
395        match self {
396            AlgebraicType::Product(product) => product.is_special(),
397            AlgebraicType::Sum(sum) => sum.is_special(),
398            _ => false,
399        }
400    }
401
402    /// Validates that the type can be used to generate a type definition
403    /// in a `SpacetimeDB` client module.
404    ///
405    /// Such a type must be a non-special sum or product type.
406    /// All of the elements of the type must satisfy [`AlgebraicType::is_valid_for_client_type_use`].
407    ///
408    /// This method does not actually follow `Ref`s to check the types they point to,
409    /// it only checks the structure of this type.
410    pub fn is_valid_for_client_type_definition(&self) -> bool {
411        // Special types should not be used to generate type definitions.
412        if self.is_special() {
413            return false;
414        }
415        match self {
416            AlgebraicType::Sum(sum) => sum
417                .variants
418                .iter()
419                .all(|variant| variant.algebraic_type.is_valid_for_client_type_use()),
420            AlgebraicType::Product(product) => product
421                .elements
422                .iter()
423                .all(|elem| elem.algebraic_type.is_valid_for_client_type_use()),
424            _ => false,
425        }
426    }
427
428    /// Validates that the type can be used to generate a *use* of a type in a `SpacetimeDB` client module.
429    /// (As opposed to a *definition* of a type.)
430    ///
431    /// This means that the type is either:
432    /// - a reference
433    /// - a special, known type
434    /// - a non-compound type like `U8`, `I32`, `F64`, etc.
435    /// - or a map, array, or option built from types that satisfy [`AlgebraicType::is_valid_for_client_type_use`]
436    ///
437    /// This method does not actually follow `Ref`s to check the types they point to,
438    /// it only checks the structure of the type.
439    pub fn is_valid_for_client_type_use(&self) -> bool {
440        match self {
441            AlgebraicType::Sum(sum) => {
442                if let Some(wrapped) = sum.as_option() {
443                    wrapped.is_valid_for_client_type_use()
444                } else {
445                    sum.is_special() || sum.is_empty()
446                }
447            }
448            AlgebraicType::Product(product) => product.is_special() || product.is_unit(),
449            AlgebraicType::Array(array) => array.elem_ty.is_valid_for_client_type_use(),
450            AlgebraicType::Ref(_) => true,
451            _ => true,
452        }
453    }
454
455    pub fn type_check(&self, value: &AlgebraicValue, typespace: &Typespace) -> bool {
456        match (self, value) {
457            (_, AlgebraicValue::Min | AlgebraicValue::Max) => true,
458            (AlgebraicType::Ref(r), _) => {
459                if let Some(resolved_ty) = typespace.get(*r) {
460                    resolved_ty.type_check(value, typespace)
461                } else {
462                    false
463                }
464            }
465            (AlgebraicType::Sum(sum_ty), AlgebraicValue::Sum(sv)) => sum_ty.type_check(sv, typespace),
466            (AlgebraicType::Product(product_ty), AlgebraicValue::Product(pv)) => product_ty.type_check(pv, typespace),
467            (AlgebraicType::Array(array_ty), AlgebraicValue::Array(arr)) => array_ty.type_check(arr, typespace),
468
469            (AlgebraicType::String, AlgebraicValue::String(_))
470            | (AlgebraicType::Bool, AlgebraicValue::Bool(_))
471            | (AlgebraicType::I8, AlgebraicValue::I8(_))
472            | (AlgebraicType::U8, AlgebraicValue::U8(_))
473            | (AlgebraicType::I16, AlgebraicValue::I16(_))
474            | (AlgebraicType::U16, AlgebraicValue::U16(_))
475            | (AlgebraicType::I32, AlgebraicValue::I32(_))
476            | (AlgebraicType::U32, AlgebraicValue::U32(_))
477            | (AlgebraicType::I64, AlgebraicValue::I64(_))
478            | (AlgebraicType::U64, AlgebraicValue::U64(_))
479            | (AlgebraicType::I128, AlgebraicValue::I128(_))
480            | (AlgebraicType::U128, AlgebraicValue::U128(_))
481            | (AlgebraicType::I256, AlgebraicValue::I256(_))
482            | (AlgebraicType::U256, AlgebraicValue::U256(_))
483            | (AlgebraicType::F32, AlgebraicValue::F32(_))
484            | (AlgebraicType::F64, AlgebraicValue::F64(_)) => true,
485            _ => false,
486        }
487    }
488}
489#[cfg(test)]
490mod tests {
491    use super::AlgebraicType;
492    use crate::meta_type::MetaType;
493    use crate::satn::Satn;
494    use crate::{
495        algebraic_type::fmt::fmt_algebraic_type, algebraic_type::map_notation::fmt_algebraic_type as fmt_map,
496        algebraic_type_ref::AlgebraicTypeRef, typespace::Typespace,
497    };
498    use crate::{product, AlgebraicValue, ValueWithType, WithTypespace};
499
500    #[test]
501    fn never() {
502        assert_eq!("(|)", fmt_algebraic_type(&AlgebraicType::never()).to_string());
503    }
504
505    #[test]
506    fn never_map() {
507        assert_eq!("{ ty_: Sum }", fmt_map(&AlgebraicType::never()).to_string());
508    }
509
510    #[test]
511    fn unit() {
512        assert_eq!("()", fmt_algebraic_type(&AlgebraicType::unit()).to_string());
513    }
514
515    #[test]
516    fn unit_map() {
517        assert_eq!("{ ty_: Product }", fmt_map(&AlgebraicType::unit()).to_string());
518    }
519
520    #[test]
521    fn primitive() {
522        assert_eq!("U8", fmt_algebraic_type(&AlgebraicType::U8).to_string());
523    }
524
525    #[test]
526    fn primitive_map() {
527        assert_eq!("{ ty_: U8 }", fmt_map(&AlgebraicType::U8).to_string());
528    }
529
530    #[test]
531    fn option() {
532        let option = AlgebraicType::option(AlgebraicType::never());
533        assert_eq!("(some: (|) | none: ())", fmt_algebraic_type(&option).to_string());
534    }
535
536    #[test]
537    fn option_map() {
538        let option = AlgebraicType::option(AlgebraicType::never());
539        assert_eq!(
540            "{ ty_: Sum, some: { ty_: Sum }, none: { ty_: Product } }",
541            fmt_map(&option).to_string()
542        );
543    }
544
545    #[test]
546    fn algebraic_type() {
547        let algebraic_type = AlgebraicType::meta_type();
548        assert_eq!(
549            "(\
550                ref: U32 \
551                | sum: (variants: Array<(\
552                    name: (some: String | none: ()), \
553                    algebraic_type: &0\
554                )>) \
555                | product: (elements: Array<(\
556                    name: (some: String | none: ()), \
557                    algebraic_type: &0\
558                )>) \
559                | array: &0 \
560                | string: () \
561                | bool: () \
562                | i8: () | u8: () \
563                | i16: () | u16: () \
564                | i32: () | u32: () \
565                | i64: () | u64: () \
566                | i128: () | u128: () \
567                | i256: () | u256: () \
568                | f32: () | f64: ()\
569            )",
570            fmt_algebraic_type(&algebraic_type).to_string()
571        );
572    }
573
574    #[test]
575    fn algebraic_type_map() {
576        let algebraic_type = AlgebraicType::meta_type();
577        assert_eq!(
578            "{ \
579                ty_: Sum, \
580                ref: { ty_: U32 }, \
581                sum: { \
582                    ty_: Product, \
583                    variants: { \
584                        ty_: Array, \
585                        0: { \
586                            ty_: Product, \
587                            name: { ty_: Sum, some: { ty_: String }, none: { ty_: Product } }, \
588                            algebraic_type: { ty_: Ref, 0: 0 } \
589                        } \
590                    } \
591                }, \
592                product: { \
593                    ty_: Product, \
594                    elements: { \
595                        ty_: Array, \
596                        0: { \
597                            ty_: Product, \
598                            name: { ty_: Sum, some: { ty_: String }, none: { ty_: Product } }, \
599                            algebraic_type: { ty_: Ref, 0: 0 } \
600                        } \
601                    } \
602                }, \
603                array: { ty_: Ref, 0: 0 }, \
604                string: { ty_: Product }, \
605                bool: { ty_: Product }, \
606                i8: { ty_: Product }, u8: { ty_: Product }, \
607                i16: { ty_: Product }, u16: { ty_: Product }, \
608                i32: { ty_: Product }, u32: { ty_: Product }, \
609                i64: { ty_: Product }, u64: { ty_: Product }, \
610                i128: { ty_: Product }, u128: { ty_: Product }, \
611                i256: { ty_: Product }, u256: { ty_: Product }, \
612                f32: { ty_: Product }, f64: { ty_: Product } \
613            }",
614            fmt_map(&algebraic_type).to_string()
615        );
616    }
617
618    #[test]
619    fn nested_products_and_sums() {
620        let builtin = AlgebraicType::U8;
621        let product = AlgebraicType::product([("thing", AlgebraicType::U8)]);
622        let sum = AlgebraicType::sum([builtin.clone(), builtin.clone(), product]);
623        let next = AlgebraicType::product([
624            (Some("test"), builtin.clone()),
625            (None, sum),
626            (None, builtin),
627            (Some("never"), AlgebraicType::never()),
628        ]);
629        assert_eq!(
630            "(test: U8, 1: (U8 | U8 | (thing: U8)), 2: U8, never: (|))",
631            fmt_algebraic_type(&next).to_string()
632        );
633    }
634
635    fn in_space<'a, T: crate::Value>(ts: &'a Typespace, ty: &'a T::Type, val: &'a T) -> ValueWithType<'a, T> {
636        WithTypespace::new(ts, ty).with_value(val)
637    }
638
639    #[test]
640    fn option_as_value() {
641        let option = AlgebraicType::option(AlgebraicType::never());
642        let algebraic_type = AlgebraicType::meta_type();
643        let typespace = Typespace::new(vec![algebraic_type]);
644        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));
645        assert_eq!(
646            r#"(sum = (variants = [(name = (some = "some"), algebraic_type = (sum = (variants = []))), (name = (some = "none"), algebraic_type = (product = (elements = [])))]))"#,
647            in_space(&typespace, &at_ref, &option.as_value()).to_satn()
648        );
649    }
650
651    #[test]
652    fn algebraic_type_as_value() {
653        let algebraic_type = AlgebraicType::meta_type();
654        let typespace = Typespace::new(vec![algebraic_type.clone()]);
655        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));
656
657        let ref0 = "algebraic_type = (ref = 0)";
658        let unit = "algebraic_type = (product = (elements = []))";
659        let aggr_elems_ty = format!(
660            "algebraic_type = (array = (product = (elements = [\
661                (\
662                    name = (some = \"name\"), \
663                    algebraic_type = (sum = (variants = [\
664                        (name = (some = \"some\"), algebraic_type = (string = ())), \
665                        (name = (some = \"none\"), {unit})\
666                    ]))\
667                ), \
668                (name = (some = \"algebraic_type\"), {ref0})\
669            ])))"
670        );
671
672        assert_eq!(
673            format!(
674                "(\
675                sum = (\
676                    variants = [\
677                        (name = (some = \"ref\"), algebraic_type = (u32 = ())), \
678                        (\
679                            name = (some = \"sum\"), \
680                            algebraic_type = (product = (elements = [\
681                                (name = (some = \"variants\"), {aggr_elems_ty})\
682                            ]))\
683                        ), \
684                        (\
685                            name = (some = \"product\"), \
686                            algebraic_type = (product = (elements = [\
687                                (name = (some = \"elements\"), {aggr_elems_ty})\
688                            ]))\
689                        ), \
690                        (name = (some = \"array\"), {ref0}), \
691                        (name = (some = \"string\"), {unit}), \
692                        (name = (some = \"bool\"), {unit}), \
693                        (name = (some = \"i8\"), {unit}), \
694                        (name = (some = \"u8\"), {unit}), \
695                        (name = (some = \"i16\"), {unit}), \
696                        (name = (some = \"u16\"), {unit}), \
697                        (name = (some = \"i32\"), {unit}), \
698                        (name = (some = \"u32\"), {unit}), \
699                        (name = (some = \"i64\"), {unit}), \
700                        (name = (some = \"u64\"), {unit}), \
701                        (name = (some = \"i128\"), {unit}), \
702                        (name = (some = \"u128\"), {unit}), \
703                        (name = (some = \"i256\"), {unit}), \
704                        (name = (some = \"u256\"), {unit}), \
705                        (name = (some = \"f32\"), {unit}), \
706                        (name = (some = \"f64\"), {unit})\
707                    ]\
708                )\
709            )"
710            ),
711            in_space(&typespace, &at_ref, &algebraic_type.as_value()).to_satn()
712        );
713    }
714
715    #[test]
716    fn option_from_value() {
717        let option = AlgebraicType::option(AlgebraicType::never());
718        AlgebraicType::from_value(&option.as_value()).expect("No errors.");
719    }
720
721    #[test]
722    fn builtin_from_value() {
723        let u8 = AlgebraicType::U8;
724        AlgebraicType::from_value(&u8.as_value()).expect("No errors.");
725    }
726
727    #[test]
728    fn algebraic_type_from_value() {
729        let algebraic_type = AlgebraicType::meta_type();
730        AlgebraicType::from_value(&algebraic_type.as_value()).expect("No errors.");
731    }
732
733    #[test]
734    fn special_types_are_special() {
735        assert!(AlgebraicType::identity().is_identity());
736        assert!(AlgebraicType::identity().is_special());
737        assert!(AlgebraicType::connection_id().is_connection_id());
738        assert!(AlgebraicType::connection_id().is_special());
739        assert!(AlgebraicType::timestamp().is_timestamp());
740        assert!(AlgebraicType::timestamp().is_special());
741        assert!(AlgebraicType::time_duration().is_special());
742        assert!(AlgebraicType::time_duration().is_time_duration());
743    }
744
745    #[test]
746    fn type_check() {
747        let av = AlgebraicValue::sum(1, AlgebraicValue::from(product![0u16, 1u32]));
748        let at = AlgebraicType::sum([
749            ("a", AlgebraicType::U8),
750            ("b", AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U32])),
751        ]);
752
753        at.type_check(&av, Typespace::EMPTY);
754    }
755}