1use std::collections::BTreeMap;
19use std::fmt::{self, Debug, Write};
20use std::hash::Hash;
21
22use crate::error::{IdentifierError, ValidationErrors};
23use crate::identifier::Identifier;
24use crate::schema::{Schema, TableSchema};
25use crate::type_for_generate::{AlgebraicTypeUse, ProductTypeDef, TypespaceForGenerate};
26use deserialize::ReducerArgsDeserializeSeed;
27use enum_map::EnumMap;
28use hashbrown::Equivalent;
29use indexmap::IndexMap;
30use itertools::Itertools;
31use spacetimedb_data_structures::error_stream::{CollectAllErrors, CombineErrors, ErrorStream};
32use spacetimedb_data_structures::map::HashMap;
33use spacetimedb_lib::db::raw_def;
34use spacetimedb_lib::db::raw_def::v9::{
35 Lifecycle, RawConstraintDataV9, RawConstraintDefV9, RawIdentifier, RawIndexAlgorithm, RawIndexDefV9,
36 RawModuleDefV9, RawReducerDefV9, RawRowLevelSecurityDefV9, RawScheduleDefV9, RawScopedTypeNameV9, RawSequenceDefV9,
37 RawSql, RawTableDefV9, RawTypeDefV9, RawUniqueConstraintDataV9, TableAccess, TableType,
38};
39use spacetimedb_lib::{ProductType, RawModuleDef};
40use spacetimedb_primitives::{ColId, ColList, ColOrCols, ColSet, ReducerId, TableId};
41use spacetimedb_sats::AlgebraicType;
42use spacetimedb_sats::{AlgebraicTypeRef, Typespace};
43
44pub mod deserialize;
45pub mod error;
46pub mod validate;
47
48pub type IdentifierMap<T> = HashMap<Identifier, T>;
50
51pub type StrMap<T> = HashMap<Box<str>, T>;
53
54#[derive(Debug, Clone)]
95#[non_exhaustive]
96pub struct ModuleDef {
97 tables: IdentifierMap<TableDef>,
99
100 reducers: IndexMap<Identifier, ReducerDef>,
104
105 lifecycle_reducers: EnumMap<Lifecycle, Option<ReducerId>>,
107
108 types: HashMap<ScopedTypeName, TypeDef>,
110
111 typespace: Typespace,
113
114 typespace_for_generate: TypespaceForGenerate,
116
117 stored_in_table_def: StrMap<Identifier>,
122
123 refmap: HashMap<AlgebraicTypeRef, ScopedTypeName>,
125
126 row_level_security_raw: HashMap<RawSql, RawRowLevelSecurityDefV9>,
130}
131
132impl ModuleDef {
133 pub fn tables(&self) -> impl Iterator<Item = &TableDef> {
135 self.tables.values()
136 }
137
138 pub fn indexes(&self) -> impl Iterator<Item = &IndexDef> {
140 self.tables().flat_map(|table| table.indexes.values())
141 }
142
143 pub fn constraints(&self) -> impl Iterator<Item = &ConstraintDef> {
145 self.tables().flat_map(|table| table.constraints.values())
146 }
147
148 pub fn sequences(&self) -> impl Iterator<Item = &SequenceDef> {
150 self.tables().flat_map(|table| table.sequences.values())
151 }
152
153 pub fn schedules(&self) -> impl Iterator<Item = &ScheduleDef> {
155 self.tables().filter_map(|table| table.schedule.as_ref())
156 }
157
158 pub fn reducers(&self) -> impl Iterator<Item = &ReducerDef> {
160 self.reducers.values()
161 }
162
163 pub fn types(&self) -> impl Iterator<Item = &TypeDef> {
165 self.types.values()
166 }
167
168 pub fn row_level_security(&self) -> impl Iterator<Item = &RawRowLevelSecurityDefV9> {
170 self.row_level_security_raw.values()
171 }
172
173 pub fn typespace(&self) -> &Typespace {
186 &self.typespace
187 }
188
189 pub fn typespace_for_generate(&self) -> &TypespaceForGenerate {
191 &self.typespace_for_generate
192 }
193
194 pub fn stored_in_table_def(&self, name: &str) -> Option<&TableDef> {
198 self.stored_in_table_def
199 .get(name)
200 .and_then(|table_name| self.tables.get(table_name))
201 }
202
203 pub fn lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> Option<&T> {
205 T::lookup(self, key)
206 }
207
208 pub fn lookup_expect<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {
211 T::lookup(self, key).expect("expected ModuleDef to contain key, but it does not")
212 }
213
214 pub fn table<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&TableDef> {
216 self.tables.get(name)
218 }
219
220 pub fn reducer<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&ReducerDef> {
222 self.reducers.get(name)
224 }
225
226 pub fn reducer_full<K: ?Sized + Hash + Equivalent<Identifier>>(
228 &self,
229 name: &K,
230 ) -> Option<(ReducerId, &ReducerDef)> {
231 self.reducers.get_full(name).map(|(idx, _, def)| (idx.into(), def))
233 }
234
235 pub fn reducer_by_id(&self, id: ReducerId) -> &ReducerDef {
237 &self.reducers[id.idx()]
238 }
239
240 pub fn get_reducer_by_id(&self, id: ReducerId) -> Option<&ReducerDef> {
242 self.reducers.get_index(id.idx()).map(|(_, def)| def)
243 }
244
245 pub fn lifecycle_reducer(&self, lifecycle: Lifecycle) -> Option<(ReducerId, &ReducerDef)> {
247 self.lifecycle_reducers[lifecycle].map(|i| (i, &self.reducers[i.idx()]))
248 }
249
250 pub fn reducer_arg_deserialize_seed<K: ?Sized + Hash + Equivalent<Identifier>>(
253 &self,
254 name: &K,
255 ) -> Option<(ReducerId, ReducerArgsDeserializeSeed)> {
256 let (id, reducer) = self.reducer_full(name)?;
257 Some((id, ReducerArgsDeserializeSeed(self.typespace.with_type(reducer))))
258 }
259
260 pub fn type_def_from_ref(&self, r: AlgebraicTypeRef) -> Option<(&ScopedTypeName, &TypeDef)> {
262 let name = self.refmap.get(&r)?;
263 let def = self
264 .types
265 .get(name)
266 .expect("if it was in refmap, it should be in types");
267
268 Some((name, def))
269 }
270
271 pub fn table_schema<K: ?Sized + Hash + Equivalent<Identifier>>(
274 &self,
275 name: &K,
276 table_id: TableId,
277 ) -> Option<TableSchema> {
278 let table_def = self.tables.get(name)?;
280 Some(TableSchema::from_module_def(self, table_def, (), table_id))
281 }
282
283 pub fn expect_lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {
285 if let Some(result) = T::lookup(self, key) {
286 result
287 } else {
288 panic!("expected ModuleDef to contain {key:?}, but it does not");
289 }
290 }
291
292 pub fn expect_contains<Def: ModuleDefLookup>(&self, def: &Def) {
294 if let Some(my_def) = self.lookup(def.key()) {
295 assert_eq!(
296 def as *const Def, my_def as *const Def,
297 "expected ModuleDef to contain {def:?}, but it contained {my_def:?}"
298 );
299 } else {
300 panic!("expected ModuleDef to contain {:?}, but it does not", def.key());
301 }
302 }
303}
304
305impl TryFrom<RawModuleDef> for ModuleDef {
306 type Error = ValidationErrors;
307
308 fn try_from(raw: RawModuleDef) -> Result<Self, Self::Error> {
309 match raw {
310 RawModuleDef::V8BackCompat(v8_mod) => Self::try_from(v8_mod),
311 RawModuleDef::V9(v9_mod) => Self::try_from(v9_mod),
312 _ => unimplemented!(),
313 }
314 }
315}
316impl TryFrom<raw_def::v8::RawModuleDefV8> for ModuleDef {
317 type Error = ValidationErrors;
318
319 fn try_from(v8_mod: raw_def::v8::RawModuleDefV8) -> Result<Self, Self::Error> {
320 validate::v8::validate(v8_mod)
323 }
324}
325impl TryFrom<raw_def::v9::RawModuleDefV9> for ModuleDef {
326 type Error = ValidationErrors;
327
328 fn try_from(v9_mod: raw_def::v9::RawModuleDefV9) -> Result<Self, Self::Error> {
329 validate::v9::validate(v9_mod)
330 }
331}
332impl From<ModuleDef> for RawModuleDefV9 {
333 fn from(val: ModuleDef) -> Self {
334 let ModuleDef {
335 tables,
336 reducers,
337 lifecycle_reducers: _,
338 types,
339 typespace,
340 stored_in_table_def: _,
341 typespace_for_generate: _,
342 refmap: _,
343 row_level_security_raw,
344 } = val;
345
346 RawModuleDefV9 {
347 tables: to_raw(tables),
348 reducers: reducers.into_iter().map(|(_, def)| def.into()).collect(),
349 types: to_raw(types),
350 misc_exports: vec![],
351 typespace,
352 row_level_security: row_level_security_raw.into_iter().map(|(_, def)| def).collect(),
353 }
354 }
355}
356
357pub trait ModuleDefLookup: Sized + Debug + 'static {
361 type Key<'a>: Debug + Copy;
363
364 fn key(&self) -> Self::Key<'_>;
366
367 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self>;
369}
370
371#[derive(Debug, Clone, Eq, PartialEq)]
386#[non_exhaustive]
387pub struct TableDef {
388 pub name: Identifier,
392
393 pub product_type_ref: AlgebraicTypeRef,
399
400 pub primary_key: Option<ColId>,
407
408 pub columns: Vec<ColumnDef>,
411
412 pub indexes: StrMap<IndexDef>,
414
415 pub constraints: StrMap<ConstraintDef>,
417
418 pub sequences: StrMap<SequenceDef>,
420
421 pub schedule: Option<ScheduleDef>,
423
424 pub table_type: TableType,
426
427 pub table_access: TableAccess,
429}
430
431impl TableDef {
432 pub fn get_column(&self, id: ColId) -> Option<&ColumnDef> {
434 self.columns.get(id.idx())
435 }
436 pub fn get_column_by_name(&self, name: &Identifier) -> Option<&ColumnDef> {
438 self.columns.iter().find(|c| &c.name == name)
439 }
440}
441
442impl From<TableDef> for RawTableDefV9 {
443 fn from(val: TableDef) -> Self {
444 let TableDef {
445 name,
446 product_type_ref,
447 primary_key,
448 columns: _, indexes,
450 constraints,
451 sequences,
452 schedule,
453 table_type,
454 table_access,
455 } = val;
456
457 RawTableDefV9 {
458 name: name.into(),
459 product_type_ref,
460 primary_key: ColList::from_iter(primary_key),
461 indexes: to_raw(indexes),
462 constraints: to_raw(constraints),
463 sequences: to_raw(sequences),
464 schedule: schedule.map(Into::into),
465 table_type,
466 table_access,
467 }
468 }
469}
470
471#[derive(Debug, Clone, Eq, PartialEq)]
473pub struct SequenceDef {
474 pub name: Box<str>,
476
477 pub column: ColId,
482
483 pub start: Option<i128>,
487
488 pub min_value: Option<i128>,
491
492 pub max_value: Option<i128>,
495
496 pub increment: i128,
498}
499
500impl From<SequenceDef> for RawSequenceDefV9 {
501 fn from(val: SequenceDef) -> Self {
502 RawSequenceDefV9 {
503 name: Some(val.name),
504 column: val.column,
505 start: val.start,
506 min_value: val.min_value,
507 max_value: val.max_value,
508 increment: val.increment,
509 }
510 }
511}
512
513#[derive(Debug, Clone, Eq, PartialEq)]
518#[non_exhaustive]
519pub struct IndexDef {
520 pub name: Box<str>,
522
523 pub accessor_name: Option<Identifier>,
534
535 pub algorithm: IndexAlgorithm,
537}
538
539impl IndexDef {
540 pub fn generated(&self) -> bool {
542 self.accessor_name.is_none()
543 }
544}
545
546impl From<IndexDef> for RawIndexDefV9 {
547 fn from(val: IndexDef) -> Self {
548 RawIndexDefV9 {
549 name: Some(val.name),
550 algorithm: match val.algorithm {
551 IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => RawIndexAlgorithm::BTree { columns },
552 IndexAlgorithm::Direct(DirectAlgorithm { column }) => RawIndexAlgorithm::Direct { column },
553 },
554 accessor_name: val.accessor_name.map(Into::into),
555 }
556 }
557}
558
559#[non_exhaustive]
561#[derive(Debug, Clone, Eq, PartialEq)]
562pub enum IndexAlgorithm {
563 BTree(BTreeAlgorithm),
565 Direct(DirectAlgorithm),
567}
568
569impl IndexAlgorithm {
570 pub fn columns(&self) -> ColOrCols<'_> {
572 match self {
573 Self::BTree(btree) => ColOrCols::ColList(&btree.columns),
574 Self::Direct(direct) => ColOrCols::Col(direct.column),
575 }
576 }
577 pub fn find_col_index(&self, pos: usize) -> Option<ColId> {
581 self.columns().iter().find(|col_id| col_id.idx() == pos)
582 }
583}
584
585impl From<IndexAlgorithm> for RawIndexAlgorithm {
586 fn from(val: IndexAlgorithm) -> Self {
587 match val {
588 IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => Self::BTree { columns },
589 IndexAlgorithm::Direct(DirectAlgorithm { column }) => Self::Direct { column },
590 }
591 }
592}
593
594#[derive(Debug, Clone, Eq, PartialEq)]
596pub struct BTreeAlgorithm {
597 pub columns: ColList,
599}
600
601impl<CL: Into<ColList>> From<CL> for BTreeAlgorithm {
602 fn from(columns: CL) -> Self {
603 let columns = columns.into();
604 Self { columns }
605 }
606}
607
608impl From<BTreeAlgorithm> for IndexAlgorithm {
609 fn from(val: BTreeAlgorithm) -> Self {
610 IndexAlgorithm::BTree(val)
611 }
612}
613
614#[derive(Debug, Clone, Eq, PartialEq)]
616pub struct DirectAlgorithm {
617 pub column: ColId,
619}
620
621impl<C: Into<ColId>> From<C> for DirectAlgorithm {
622 fn from(column: C) -> Self {
623 let column = column.into();
624 Self { column }
625 }
626}
627
628impl From<DirectAlgorithm> for IndexAlgorithm {
629 fn from(val: DirectAlgorithm) -> Self {
630 IndexAlgorithm::Direct(val)
631 }
632}
633
634#[derive(Debug, Clone, Eq, PartialEq)]
639#[non_exhaustive]
640pub struct ColumnDef {
641 pub name: Identifier,
645
646 pub col_id: ColId,
648
649 pub ty: AlgebraicType,
656
657 pub ty_for_generate: AlgebraicTypeUse,
659
660 pub table_name: Identifier,
662}
663
664#[derive(Debug, Clone, Eq, PartialEq)]
666pub struct ConstraintDef {
667 pub name: Box<str>,
669
670 pub data: ConstraintData,
672}
673
674impl From<ConstraintDef> for RawConstraintDefV9 {
675 fn from(val: ConstraintDef) -> Self {
676 RawConstraintDefV9 {
677 name: Some(val.name),
678 data: val.data.into(),
679 }
680 }
681}
682
683#[derive(Debug, Clone, Eq, PartialEq)]
685#[non_exhaustive]
686pub enum ConstraintData {
687 Unique(UniqueConstraintData),
688}
689
690impl ConstraintData {
691 pub fn unique_columns(&self) -> Option<&ColSet> {
694 match &self {
695 ConstraintData::Unique(UniqueConstraintData { columns }) => Some(columns),
696 }
697 }
698}
699
700impl From<ConstraintData> for RawConstraintDataV9 {
701 fn from(val: ConstraintData) -> Self {
702 match val {
703 ConstraintData::Unique(unique) => RawConstraintDataV9::Unique(unique.into()),
704 }
705 }
706}
707
708#[derive(Debug, Clone, Eq, PartialEq)]
712pub struct UniqueConstraintData {
713 pub columns: ColSet,
715}
716
717impl From<UniqueConstraintData> for RawUniqueConstraintDataV9 {
718 fn from(val: UniqueConstraintData) -> Self {
719 RawUniqueConstraintDataV9 {
720 columns: val.columns.into(),
721 }
722 }
723}
724
725impl From<UniqueConstraintData> for ConstraintData {
726 fn from(val: UniqueConstraintData) -> Self {
727 ConstraintData::Unique(val)
728 }
729}
730
731#[derive(Debug, Clone, Eq, PartialEq)]
733pub struct RowLevelSecurityDef {
734 pub sql: RawSql,
736}
737
738impl From<RowLevelSecurityDef> for RawRowLevelSecurityDefV9 {
739 fn from(val: RowLevelSecurityDef) -> Self {
740 RawRowLevelSecurityDefV9 { sql: val.sql }
741 }
742}
743
744#[derive(Debug, Clone, Eq, PartialEq)]
746#[non_exhaustive]
747pub struct ScheduleDef {
748 pub name: Box<str>,
750
751 pub at_column: ColId,
755
756 pub id_column: ColId,
760
761 pub reducer_name: Identifier,
764}
765
766impl From<ScheduleDef> for RawScheduleDefV9 {
767 fn from(val: ScheduleDef) -> Self {
768 RawScheduleDefV9 {
769 name: Some(val.name),
770 reducer_name: val.reducer_name.into(),
771 scheduled_at_column: val.at_column,
772 }
773 }
774}
775
776#[derive(Debug, Clone, Eq, PartialEq)]
778#[non_exhaustive]
779pub struct TypeDef {
780 pub name: ScopedTypeName,
782
783 pub ty: AlgebraicTypeRef,
787
788 pub custom_ordering: bool,
790}
791impl From<TypeDef> for RawTypeDefV9 {
792 fn from(val: TypeDef) -> Self {
793 RawTypeDefV9 {
794 name: val.name.into(),
795 ty: val.ty,
796 custom_ordering: val.custom_ordering,
797 }
798 }
799}
800
801#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
806pub struct ScopedTypeName {
807 scope: Box<[Identifier]>,
812
813 name: Identifier,
817}
818impl ScopedTypeName {
819 pub fn new(scope: Box<[Identifier]>, name: Identifier) -> Self {
821 ScopedTypeName { scope, name }
822 }
823
824 pub fn try_new(
827 scope: impl IntoIterator<Item = RawIdentifier>,
828 name: impl Into<RawIdentifier>,
829 ) -> Result<Self, ErrorStream<IdentifierError>> {
830 let scope = scope
831 .into_iter()
832 .map(|chunk| Identifier::new(chunk).map_err(ErrorStream::from))
833 .collect_all_errors();
834 let name = Identifier::new(name.into()).map_err(ErrorStream::from);
835 let (scope, name) = (scope, name).combine_errors()?;
836 Ok(ScopedTypeName { scope, name })
837 }
838
839 pub fn from_name(name: Identifier) -> Self {
841 ScopedTypeName {
842 scope: Box::new([]),
843 name,
844 }
845 }
846
847 pub fn name(&self) -> &Identifier {
849 &self.name
850 }
851
852 pub fn as_identifier(&self) -> Option<&Identifier> {
854 self.scope.is_empty().then_some(&self.name)
855 }
856
857 pub fn name_segments(&self) -> impl Iterator<Item = &Identifier> {
859 self.scope.iter().chain(std::iter::once(&self.name))
860 }
861}
862impl fmt::Debug for ScopedTypeName {
863 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
864 f.write_char('"')?;
867 for scope in &*self.scope {
868 write!(f, "{scope}::")?;
869 }
870 write!(f, "{}\"", self.name)
871 }
872}
873impl fmt::Display for ScopedTypeName {
874 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
875 for scope in &*self.scope {
876 write!(f, "{scope}::")?;
877 }
878 fmt::Display::fmt(&self.name, f)
879 }
880}
881impl TryFrom<RawScopedTypeNameV9> for ScopedTypeName {
882 type Error = ErrorStream<IdentifierError>;
883
884 fn try_from(value: RawScopedTypeNameV9) -> Result<Self, Self::Error> {
885 Self::try_new(value.scope.into_vec(), value.name)
886 }
887}
888impl From<ScopedTypeName> for RawScopedTypeNameV9 {
889 fn from(val: ScopedTypeName) -> Self {
890 RawScopedTypeNameV9 {
891 scope: val.scope.into_vec().into_iter().map_into().collect(),
892 name: val.name.into(),
893 }
894 }
895}
896
897#[derive(Debug, Clone, Eq, PartialEq)]
899#[non_exhaustive]
900pub struct ReducerDef {
901 pub name: Identifier,
903
904 pub params: ProductType,
908
909 pub params_for_generate: ProductTypeDef,
913
914 pub lifecycle: Option<Lifecycle>,
916}
917
918impl From<ReducerDef> for RawReducerDefV9 {
919 fn from(val: ReducerDef) -> Self {
920 RawReducerDefV9 {
921 name: val.name.into(),
922 params: val.params,
923 lifecycle: val.lifecycle,
924 }
925 }
926}
927
928impl ModuleDefLookup for TableDef {
929 type Key<'a> = &'a Identifier;
930
931 fn key(&self) -> Self::Key<'_> {
932 &self.name
933 }
934
935 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
936 module_def.tables.get(key)
937 }
938}
939
940impl ModuleDefLookup for SequenceDef {
941 type Key<'a> = &'a str;
942
943 fn key(&self) -> Self::Key<'_> {
944 &self.name
945 }
946
947 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
948 module_def.stored_in_table_def(key)?.sequences.get(key)
949 }
950}
951
952impl ModuleDefLookup for IndexDef {
953 type Key<'a> = &'a str;
954
955 fn key(&self) -> Self::Key<'_> {
956 &self.name
957 }
958
959 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
960 module_def.stored_in_table_def(key)?.indexes.get(key)
961 }
962}
963
964impl ModuleDefLookup for ColumnDef {
965 type Key<'a> = (&'a Identifier, &'a Identifier);
969
970 fn key(&self) -> Self::Key<'_> {
971 (&self.table_name, &self.name)
972 }
973
974 fn lookup<'a>(module_def: &'a ModuleDef, (table_name, name): Self::Key<'_>) -> Option<&'a Self> {
975 module_def
976 .tables
977 .get(table_name)
978 .and_then(|table| table.get_column_by_name(name))
979 }
980}
981
982impl ModuleDefLookup for ConstraintDef {
983 type Key<'a> = &'a str;
984
985 fn key(&self) -> Self::Key<'_> {
986 &self.name
987 }
988
989 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
990 module_def.stored_in_table_def(key)?.constraints.get(key)
991 }
992}
993
994impl ModuleDefLookup for RawRowLevelSecurityDefV9 {
995 type Key<'a> = &'a RawSql;
996
997 fn key(&self) -> Self::Key<'_> {
998 &self.sql
999 }
1000
1001 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1002 module_def.row_level_security_raw.get(key)
1003 }
1004}
1005
1006impl ModuleDefLookup for ScheduleDef {
1007 type Key<'a> = &'a str;
1008
1009 fn key(&self) -> Self::Key<'_> {
1010 &self.name
1011 }
1012
1013 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1014 let schedule = module_def.stored_in_table_def(key)?.schedule.as_ref()?;
1015 if &schedule.name[..] == key {
1016 Some(schedule)
1017 } else {
1018 None
1019 }
1020 }
1021}
1022
1023impl ModuleDefLookup for TypeDef {
1024 type Key<'a> = &'a ScopedTypeName;
1025
1026 fn key(&self) -> Self::Key<'_> {
1027 &self.name
1028 }
1029
1030 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1031 module_def.types.get(key)
1032 }
1033}
1034
1035impl ModuleDefLookup for ReducerDef {
1036 type Key<'a> = &'a Identifier;
1037
1038 fn key(&self) -> Self::Key<'_> {
1039 &self.name
1040 }
1041
1042 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1043 module_def.reducers.get(key)
1044 }
1045}
1046
1047fn to_raw<Def, RawDef, Name, A>(data: HashMap<Name, Def, A>) -> Vec<RawDef>
1048where
1049 Def: ModuleDefLookup + Into<RawDef>,
1050 Name: Eq + Ord + 'static,
1051{
1052 let sorted: BTreeMap<Name, Def> = data.into_iter().collect();
1053 sorted.into_values().map_into().collect()
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058 use super::*;
1059 use proptest::prelude::*;
1060
1061 proptest! {
1062 #[test]
1063 fn to_raw_deterministic(vec in prop::collection::vec(any::<u32>(), 0..5)) {
1064 let mut map = HashMap::new();
1065 let name = ScopedTypeName::try_new([], "fake_name").unwrap();
1066 for k in vec {
1067 let def = TypeDef { name: name.clone(), ty: AlgebraicTypeRef(k), custom_ordering: false };
1068 map.insert(k, def);
1069 }
1070 let raw: Vec<RawTypeDefV9> = to_raw(map.clone());
1071 let raw2: Vec<RawTypeDefV9> = to_raw(map);
1072 prop_assert_eq!(raw, raw2);
1073 }
1074 }
1075}