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