Skip to main content

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(var_names: impl Iterator<Item = &'static 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    /// Constructs an `AlgebraicValue` of this type from an `i128`
375    /// using saturating conversion.
376    ///
377    /// Returns `None` if this is not an integer type.
378    /// If `value` does not fit in this type,
379    /// it is saturated to the type's range.
380    /// Negative values convert to `0` for unsigned types.
381    pub fn saturating_value_from_i128(&self, value: i128) -> Option<AlgebraicValue> {
382        let signed = |min, max| value.clamp(min, max);
383        let unsigned = |max| value.clamp(0, max);
384
385        match self {
386            Self::I8 => Some((signed(i8::MIN as i128, i8::MAX as i128) as i8).into()),
387            Self::I16 => Some((signed(i16::MIN as i128, i16::MAX as i128) as i16).into()),
388            Self::I32 => Some((signed(i32::MIN as i128, i32::MAX as i128) as i32).into()),
389            Self::I64 => Some((signed(i64::MIN as i128, i64::MAX as i128) as i64).into()),
390            Self::I128 => Some(value.into()),
391            Self::I256 => Some(i256::from(value).into()),
392
393            Self::U8 => Some((unsigned(u8::MAX as i128) as u8).into()),
394            Self::U16 => Some((unsigned(u16::MAX as i128) as u16).into()),
395            Self::U32 => Some((unsigned(u32::MAX as i128) as u32).into()),
396            Self::U64 => Some((unsigned(u64::MAX as i128) as u64).into()),
397            Self::U128 => Some((value.max(0) as u128).into()),
398            Self::U256 => Some(u256::from(value.max(0) as u128).into()),
399            _ => None,
400        }
401    }
402
403    #[inline]
404    /// Given an AlgebraicType, returns the min value for that type.
405    pub fn min_value(&self) -> Option<AlgebraicValue> {
406        match *self {
407            Self::I8 => Some(i8::MIN.into()),
408            Self::U8 => Some(u8::MIN.into()),
409            Self::I16 => Some(i16::MIN.into()),
410            Self::U16 => Some(u16::MIN.into()),
411            Self::I32 => Some(i32::MIN.into()),
412            Self::U32 => Some(u32::MIN.into()),
413            Self::I64 => Some(i64::MIN.into()),
414            Self::U64 => Some(u64::MIN.into()),
415            Self::I128 => Some(i128::MIN.into()),
416            Self::U128 => Some(u128::MIN.into()),
417            Self::I256 => Some(i256::MIN.into()),
418            Self::U256 => Some(u256::MIN.into()),
419            Self::F32 => Some(f32::MIN.into()),
420            Self::F64 => Some(f64::MIN.into()),
421            _ => None,
422        }
423    }
424
425    #[inline]
426    /// Given an AlgebraicType, returns the max value for that type.
427    pub fn max_value(&self) -> Option<AlgebraicValue> {
428        match *self {
429            Self::I8 => Some(i8::MAX.into()),
430            Self::U8 => Some(u8::MAX.into()),
431            Self::I16 => Some(i16::MAX.into()),
432            Self::U16 => Some(u16::MAX.into()),
433            Self::I32 => Some(i32::MAX.into()),
434            Self::U32 => Some(u32::MAX.into()),
435            Self::I64 => Some(i64::MAX.into()),
436            Self::U64 => Some(u64::MAX.into()),
437            Self::I128 => Some(i128::MAX.into()),
438            Self::U128 => Some(u128::MAX.into()),
439            Self::I256 => Some(i256::MAX.into()),
440            Self::U256 => Some(u256::MAX.into()),
441            Self::F32 => Some(f32::MAX.into()),
442            Self::F64 => Some(f64::MAX.into()),
443            _ => None,
444        }
445    }
446
447    /// Check if the type is one of a small number of special, known types
448    /// with specific layouts.
449    /// See also [`ProductType::is_special`] and [`SumType::is_special`].
450    pub fn is_special(&self) -> bool {
451        match self {
452            AlgebraicType::Product(product) => product.is_special(),
453            AlgebraicType::Sum(sum) => sum.is_special(),
454            _ => false,
455        }
456    }
457
458    /// Validates that the type can be used to generate a type definition
459    /// in a `SpacetimeDB` client module.
460    ///
461    /// Such a type must be a non-special sum or product type.
462    /// All of the elements of the type must 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 this type.
466    pub fn is_valid_for_client_type_definition(&self) -> bool {
467        // Special types should not be used to generate type definitions.
468        if self.is_special() {
469            return false;
470        }
471        match self {
472            AlgebraicType::Sum(sum) => sum
473                .variants
474                .iter()
475                .all(|variant| variant.algebraic_type.is_valid_for_client_type_use()),
476            AlgebraicType::Product(product) => product
477                .elements
478                .iter()
479                .all(|elem| elem.algebraic_type.is_valid_for_client_type_use()),
480            _ => false,
481        }
482    }
483
484    /// Validates that the type can be used to generate a *use* of a type in a `SpacetimeDB` client module.
485    /// (As opposed to a *definition* of a type.)
486    ///
487    /// This means that the type is either:
488    /// - a reference
489    /// - a special, known type
490    /// - a non-compound type like `U8`, `I32`, `F64`, etc.
491    /// - or a map, array, option, or result built from types that satisfy [`AlgebraicType::is_valid_for_client_type_use`]
492    ///
493    /// This method does not actually follow `Ref`s to check the types they point to,
494    /// it only checks the structure of the type.
495    pub fn is_valid_for_client_type_use(&self) -> bool {
496        match self {
497            AlgebraicType::Sum(sum) => {
498                if let Some(wrapped) = sum.as_option() {
499                    wrapped.is_valid_for_client_type_use()
500                } else if let Some((ok_ty, err_ty)) = sum.as_result() {
501                    ok_ty.is_valid_for_client_type_use() && err_ty.is_valid_for_client_type_use()
502                } else {
503                    sum.is_special() || sum.is_empty()
504                }
505            }
506            AlgebraicType::Product(product) => product.is_special() || product.is_unit(),
507            AlgebraicType::Array(array) => array.elem_ty.is_valid_for_client_type_use(),
508            AlgebraicType::Ref(_) => true,
509            _ => true,
510        }
511    }
512
513    pub fn type_check(&self, value: &AlgebraicValue, typespace: &Typespace) -> bool {
514        match (self, value) {
515            (_, AlgebraicValue::Min | AlgebraicValue::Max) => true,
516            (AlgebraicType::Ref(r), _) => {
517                if let Some(resolved_ty) = typespace.get(*r) {
518                    resolved_ty.type_check(value, typespace)
519                } else {
520                    false
521                }
522            }
523            (AlgebraicType::Sum(sum_ty), AlgebraicValue::Sum(sv)) => sum_ty.type_check(sv, typespace),
524            (AlgebraicType::Product(product_ty), AlgebraicValue::Product(pv)) => product_ty.type_check(pv, typespace),
525            (AlgebraicType::Array(array_ty), AlgebraicValue::Array(arr)) => array_ty.type_check(arr, typespace),
526
527            (AlgebraicType::String, AlgebraicValue::String(_))
528            | (AlgebraicType::Bool, AlgebraicValue::Bool(_))
529            | (AlgebraicType::I8, AlgebraicValue::I8(_))
530            | (AlgebraicType::U8, AlgebraicValue::U8(_))
531            | (AlgebraicType::I16, AlgebraicValue::I16(_))
532            | (AlgebraicType::U16, AlgebraicValue::U16(_))
533            | (AlgebraicType::I32, AlgebraicValue::I32(_))
534            | (AlgebraicType::U32, AlgebraicValue::U32(_))
535            | (AlgebraicType::I64, AlgebraicValue::I64(_))
536            | (AlgebraicType::U64, AlgebraicValue::U64(_))
537            | (AlgebraicType::I128, AlgebraicValue::I128(_))
538            | (AlgebraicType::U128, AlgebraicValue::U128(_))
539            | (AlgebraicType::I256, AlgebraicValue::I256(_))
540            | (AlgebraicType::U256, AlgebraicValue::U256(_))
541            | (AlgebraicType::F32, AlgebraicValue::F32(_))
542            | (AlgebraicType::F64, AlgebraicValue::F64(_)) => true,
543            _ => false,
544        }
545    }
546}
547#[cfg(test)]
548mod tests {
549    use super::AlgebraicType;
550    use crate::meta_type::MetaType;
551    use crate::satn::Satn;
552    use crate::{
553        algebraic_type::fmt::fmt_algebraic_type, algebraic_type::map_notation::fmt_algebraic_type as fmt_map,
554        algebraic_type_ref::AlgebraicTypeRef, typespace::Typespace,
555    };
556    use crate::{product, AlgebraicValue, ValueWithType, WithTypespace};
557
558    #[test]
559    fn never() {
560        assert_eq!("(|)", fmt_algebraic_type(&AlgebraicType::never()).to_string());
561    }
562
563    #[test]
564    fn never_map() {
565        assert_eq!("{ ty_: Sum }", fmt_map(&AlgebraicType::never()).to_string());
566    }
567
568    #[test]
569    fn unit() {
570        assert_eq!("()", fmt_algebraic_type(&AlgebraicType::unit()).to_string());
571    }
572
573    #[test]
574    fn unit_map() {
575        assert_eq!("{ ty_: Product }", fmt_map(&AlgebraicType::unit()).to_string());
576    }
577
578    #[test]
579    fn primitive() {
580        assert_eq!("U8", fmt_algebraic_type(&AlgebraicType::U8).to_string());
581    }
582
583    #[test]
584    fn primitive_map() {
585        assert_eq!("{ ty_: U8 }", fmt_map(&AlgebraicType::U8).to_string());
586    }
587
588    #[test]
589    fn option() {
590        let option = AlgebraicType::option(AlgebraicType::never());
591        assert_eq!("(some: (|) | none: ())", fmt_algebraic_type(&option).to_string());
592    }
593
594    #[test]
595    fn option_map() {
596        let option = AlgebraicType::option(AlgebraicType::never());
597        assert_eq!(
598            "{ ty_: Sum, some: { ty_: Sum }, none: { ty_: Product } }",
599            fmt_map(&option).to_string()
600        );
601    }
602
603    #[test]
604    fn result() {
605        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);
606        assert_eq!("(ok: U8 | err: String)", fmt_algebraic_type(&result).to_string());
607    }
608
609    #[test]
610    fn result_map() {
611        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);
612        assert_eq!(
613            "{ ty_: Sum, ok: { ty_: U8 }, err: { ty_: String } }",
614            fmt_map(&result).to_string()
615        );
616    }
617
618    #[test]
619    fn algebraic_type() {
620        let algebraic_type = AlgebraicType::meta_type();
621        assert_eq!(
622            "(\
623                ref: U32 \
624                | sum: (variants: Array<(\
625                    name: (some: String | none: ()), \
626                    algebraic_type: &0\
627                )>) \
628                | product: (elements: Array<(\
629                    name: (some: String | none: ()), \
630                    algebraic_type: &0\
631                )>) \
632                | array: &0 \
633                | string: () \
634                | bool: () \
635                | i8: () | u8: () \
636                | i16: () | u16: () \
637                | i32: () | u32: () \
638                | i64: () | u64: () \
639                | i128: () | u128: () \
640                | i256: () | u256: () \
641                | f32: () | f64: ()\
642            )",
643            fmt_algebraic_type(&algebraic_type).to_string()
644        );
645    }
646
647    #[test]
648    fn algebraic_type_map() {
649        let algebraic_type = AlgebraicType::meta_type();
650        assert_eq!(
651            "{ \
652                ty_: Sum, \
653                ref: { ty_: U32 }, \
654                sum: { \
655                    ty_: Product, \
656                    variants: { \
657                        ty_: Array, \
658                        0: { \
659                            ty_: Product, \
660                            name: { ty_: Sum, some: { ty_: String }, none: { ty_: Product } }, \
661                            algebraic_type: { ty_: Ref, 0: 0 } \
662                        } \
663                    } \
664                }, \
665                product: { \
666                    ty_: Product, \
667                    elements: { \
668                        ty_: Array, \
669                        0: { \
670                            ty_: Product, \
671                            name: { ty_: Sum, some: { ty_: String }, none: { ty_: Product } }, \
672                            algebraic_type: { ty_: Ref, 0: 0 } \
673                        } \
674                    } \
675                }, \
676                array: { ty_: Ref, 0: 0 }, \
677                string: { ty_: Product }, \
678                bool: { ty_: Product }, \
679                i8: { ty_: Product }, u8: { ty_: Product }, \
680                i16: { ty_: Product }, u16: { ty_: Product }, \
681                i32: { ty_: Product }, u32: { ty_: Product }, \
682                i64: { ty_: Product }, u64: { ty_: Product }, \
683                i128: { ty_: Product }, u128: { ty_: Product }, \
684                i256: { ty_: Product }, u256: { ty_: Product }, \
685                f32: { ty_: Product }, f64: { ty_: Product } \
686            }",
687            fmt_map(&algebraic_type).to_string()
688        );
689    }
690
691    #[test]
692    fn nested_products_and_sums() {
693        let builtin = AlgebraicType::U8;
694        let product = AlgebraicType::product([("thing", AlgebraicType::U8)]);
695        let sum = AlgebraicType::sum([builtin.clone(), builtin.clone(), product]);
696        let next = AlgebraicType::product([
697            (Some("test"), builtin.clone()),
698            (None, sum),
699            (None, builtin),
700            (Some("never"), AlgebraicType::never()),
701        ]);
702        assert_eq!(
703            "(test: U8, 1: (U8 | U8 | (thing: U8)), 2: U8, never: (|))",
704            fmt_algebraic_type(&next).to_string()
705        );
706    }
707
708    fn in_space<'a, T: crate::Value>(ts: &'a Typespace, ty: &'a T::Type, val: &'a T) -> ValueWithType<'a, T> {
709        WithTypespace::new(ts, ty).with_value(val)
710    }
711
712    #[test]
713    fn option_as_value() {
714        let option = AlgebraicType::option(AlgebraicType::never());
715        let algebraic_type = AlgebraicType::meta_type();
716        let typespace = Typespace::new(vec![algebraic_type]);
717        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));
718        assert_eq!(
719            r#"(sum = (variants = [(name = (some = "some"), algebraic_type = (sum = (variants = []))), (name = (some = "none"), algebraic_type = (product = (elements = [])))]))"#,
720            in_space(&typespace, &at_ref, &option.as_value()).to_satn()
721        );
722    }
723
724    #[test]
725    fn result_as_value() {
726        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);
727        let algebraic_type = AlgebraicType::meta_type();
728        let typespace = Typespace::new(vec![algebraic_type]);
729        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));
730        assert_eq!(
731            r#"(sum = (variants = [(name = (some = "ok"), algebraic_type = (u8 = ())), (name = (some = "err"), algebraic_type = (string = ()))]))"#,
732            in_space(&typespace, &at_ref, &result.as_value()).to_satn()
733        );
734    }
735
736    #[test]
737    fn algebraic_type_as_value() {
738        let algebraic_type = AlgebraicType::meta_type();
739        let typespace = Typespace::new(vec![algebraic_type.clone()]);
740        let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0));
741
742        let ref0 = "algebraic_type = (ref = 0)";
743        let unit = "algebraic_type = (product = (elements = []))";
744        let aggr_elems_ty = format!(
745            "algebraic_type = (array = (product = (elements = [\
746                (\
747                    name = (some = \"name\"), \
748                    algebraic_type = (sum = (variants = [\
749                        (name = (some = \"some\"), algebraic_type = (string = ())), \
750                        (name = (some = \"none\"), {unit})\
751                    ]))\
752                ), \
753                (name = (some = \"algebraic_type\"), {ref0})\
754            ])))"
755        );
756
757        assert_eq!(
758            format!(
759                "(\
760                sum = (\
761                    variants = [\
762                        (name = (some = \"ref\"), algebraic_type = (u32 = ())), \
763                        (\
764                            name = (some = \"sum\"), \
765                            algebraic_type = (product = (elements = [\
766                                (name = (some = \"variants\"), {aggr_elems_ty})\
767                            ]))\
768                        ), \
769                        (\
770                            name = (some = \"product\"), \
771                            algebraic_type = (product = (elements = [\
772                                (name = (some = \"elements\"), {aggr_elems_ty})\
773                            ]))\
774                        ), \
775                        (name = (some = \"array\"), {ref0}), \
776                        (name = (some = \"string\"), {unit}), \
777                        (name = (some = \"bool\"), {unit}), \
778                        (name = (some = \"i8\"), {unit}), \
779                        (name = (some = \"u8\"), {unit}), \
780                        (name = (some = \"i16\"), {unit}), \
781                        (name = (some = \"u16\"), {unit}), \
782                        (name = (some = \"i32\"), {unit}), \
783                        (name = (some = \"u32\"), {unit}), \
784                        (name = (some = \"i64\"), {unit}), \
785                        (name = (some = \"u64\"), {unit}), \
786                        (name = (some = \"i128\"), {unit}), \
787                        (name = (some = \"u128\"), {unit}), \
788                        (name = (some = \"i256\"), {unit}), \
789                        (name = (some = \"u256\"), {unit}), \
790                        (name = (some = \"f32\"), {unit}), \
791                        (name = (some = \"f64\"), {unit})\
792                    ]\
793                )\
794            )"
795            ),
796            in_space(&typespace, &at_ref, &algebraic_type.as_value()).to_satn()
797        );
798    }
799
800    #[test]
801    fn option_from_value() {
802        let option = AlgebraicType::option(AlgebraicType::never());
803        AlgebraicType::from_value(&option.as_value()).expect("No errors.");
804    }
805
806    #[test]
807    fn result_from_value() {
808        let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String);
809        AlgebraicType::from_value(&result.as_value()).expect("No errors.");
810    }
811
812    #[test]
813    fn builtin_from_value() {
814        let u8 = AlgebraicType::U8;
815        AlgebraicType::from_value(&u8.as_value()).expect("No errors.");
816    }
817
818    #[test]
819    fn algebraic_type_from_value() {
820        let algebraic_type = AlgebraicType::meta_type();
821        AlgebraicType::from_value(&algebraic_type.as_value()).expect("No errors.");
822    }
823
824    #[test]
825    fn special_types_are_special() {
826        assert!(AlgebraicType::identity().is_identity());
827        assert!(AlgebraicType::identity().is_special());
828        assert!(AlgebraicType::connection_id().is_connection_id());
829        assert!(AlgebraicType::connection_id().is_special());
830        assert!(AlgebraicType::timestamp().is_timestamp());
831        assert!(AlgebraicType::timestamp().is_special());
832        assert!(AlgebraicType::time_duration().is_special());
833        assert!(AlgebraicType::time_duration().is_time_duration());
834        assert!(AlgebraicType::uuid().is_uuid());
835        assert!(AlgebraicType::uuid().is_special());
836    }
837
838    #[test]
839    fn type_check() {
840        let av = AlgebraicValue::sum(1, AlgebraicValue::from(product![0u16, 1u32]));
841        let at = AlgebraicType::sum([
842            ("a", AlgebraicType::U8),
843            ("b", AlgebraicType::product([AlgebraicType::U16, AlgebraicType::U32])),
844        ]);
845
846        at.type_check(&av, Typespace::EMPTY);
847    }
848}