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 validate;
46
47pub type IdentifierMap<T> = HashMap<Identifier, T>;
49
50pub type StrMap<T> = HashMap<Box<str>, T>;
52
53#[derive(Debug, Clone)]
94#[non_exhaustive]
95pub struct ModuleDef {
96 tables: IdentifierMap<TableDef>,
98
99 reducers: IndexMap<Identifier, ReducerDef>,
103
104 lifecycle_reducers: EnumMap<Lifecycle, Option<ReducerId>>,
106
107 types: HashMap<ScopedTypeName, TypeDef>,
109
110 typespace: Typespace,
112
113 typespace_for_generate: TypespaceForGenerate,
115
116 stored_in_table_def: StrMap<Identifier>,
121
122 refmap: HashMap<AlgebraicTypeRef, ScopedTypeName>,
124
125 row_level_security_raw: HashMap<RawSql, RawRowLevelSecurityDefV9>,
129}
130
131impl ModuleDef {
132 pub fn tables(&self) -> impl Iterator<Item = &TableDef> {
134 self.tables.values()
135 }
136
137 pub fn indexes(&self) -> impl Iterator<Item = &IndexDef> {
139 self.tables().flat_map(|table| table.indexes.values())
140 }
141
142 pub fn constraints(&self) -> impl Iterator<Item = &ConstraintDef> {
144 self.tables().flat_map(|table| table.constraints.values())
145 }
146
147 pub fn sequences(&self) -> impl Iterator<Item = &SequenceDef> {
149 self.tables().flat_map(|table| table.sequences.values())
150 }
151
152 pub fn schedules(&self) -> impl Iterator<Item = &ScheduleDef> {
154 self.tables().filter_map(|table| table.schedule.as_ref())
155 }
156
157 pub fn reducers(&self) -> impl Iterator<Item = &ReducerDef> {
159 self.reducers.values()
160 }
161
162 pub fn types(&self) -> impl Iterator<Item = &TypeDef> {
164 self.types.values()
165 }
166
167 pub fn row_level_security(&self) -> impl Iterator<Item = &RawRowLevelSecurityDefV9> {
169 self.row_level_security_raw.values()
170 }
171
172 pub fn typespace(&self) -> &Typespace {
185 &self.typespace
186 }
187
188 pub fn typespace_for_generate(&self) -> &TypespaceForGenerate {
190 &self.typespace_for_generate
191 }
192
193 pub fn stored_in_table_def(&self, name: &str) -> Option<&TableDef> {
197 self.stored_in_table_def
198 .get(name)
199 .and_then(|table_name| self.tables.get(table_name))
200 }
201
202 pub fn lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> Option<&T> {
204 T::lookup(self, key)
205 }
206
207 pub fn lookup_expect<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {
210 T::lookup(self, key).expect("expected ModuleDef to contain key, but it does not")
211 }
212
213 pub fn table<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&TableDef> {
215 self.tables.get(name)
217 }
218
219 pub fn reducer<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&ReducerDef> {
221 self.reducers.get(name)
223 }
224
225 pub fn reducer_full<K: ?Sized + Hash + Equivalent<Identifier>>(
227 &self,
228 name: &K,
229 ) -> Option<(ReducerId, &ReducerDef)> {
230 self.reducers.get_full(name).map(|(idx, _, def)| (idx.into(), def))
232 }
233
234 pub fn reducer_by_id(&self, id: ReducerId) -> &ReducerDef {
236 &self.reducers[id.idx()]
237 }
238
239 pub fn get_reducer_by_id(&self, id: ReducerId) -> Option<&ReducerDef> {
241 self.reducers.get_index(id.idx()).map(|(_, def)| def)
242 }
243
244 pub fn lifecycle_reducer(&self, lifecycle: Lifecycle) -> Option<(ReducerId, &ReducerDef)> {
246 self.lifecycle_reducers[lifecycle].map(|i| (i, &self.reducers[i.idx()]))
247 }
248
249 pub fn reducer_arg_deserialize_seed<K: ?Sized + Hash + Equivalent<Identifier>>(
252 &self,
253 name: &K,
254 ) -> Option<(ReducerId, ReducerArgsDeserializeSeed)> {
255 let (id, reducer) = self.reducer_full(name)?;
256 Some((id, ReducerArgsDeserializeSeed(self.typespace.with_type(reducer))))
257 }
258
259 pub fn type_def_from_ref(&self, r: AlgebraicTypeRef) -> Option<(&ScopedTypeName, &TypeDef)> {
261 let name = self.refmap.get(&r)?;
262 let def = self
263 .types
264 .get(name)
265 .expect("if it was in refmap, it should be in types");
266
267 Some((name, def))
268 }
269
270 pub fn table_schema<K: ?Sized + Hash + Equivalent<Identifier>>(
273 &self,
274 name: &K,
275 table_id: TableId,
276 ) -> Option<TableSchema> {
277 let table_def = self.tables.get(name)?;
279 Some(TableSchema::from_module_def(self, table_def, (), table_id))
280 }
281
282 pub fn expect_lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {
284 if let Some(result) = T::lookup(self, key) {
285 result
286 } else {
287 panic!("expected ModuleDef to contain {:?}, but it does not", key);
288 }
289 }
290
291 pub fn expect_contains<Def: ModuleDefLookup>(&self, def: &Def) {
293 if let Some(my_def) = self.lookup(def.key()) {
294 assert_eq!(
295 def as *const Def, my_def as *const Def,
296 "expected ModuleDef to contain {:?}, but it contained {:?}",
297 def, 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 From<BTreeAlgorithm> for IndexAlgorithm {
602 fn from(val: BTreeAlgorithm) -> Self {
603 IndexAlgorithm::BTree(val)
604 }
605}
606
607#[derive(Debug, Clone, Eq, PartialEq)]
609pub struct DirectAlgorithm {
610 pub column: ColId,
612}
613
614impl From<DirectAlgorithm> for IndexAlgorithm {
615 fn from(val: DirectAlgorithm) -> Self {
616 IndexAlgorithm::Direct(val)
617 }
618}
619
620#[derive(Debug, Clone, Eq, PartialEq)]
625#[non_exhaustive]
626pub struct ColumnDef {
627 pub name: Identifier,
631
632 pub col_id: ColId,
634
635 pub ty: AlgebraicType,
642
643 pub ty_for_generate: AlgebraicTypeUse,
645
646 pub table_name: Identifier,
648}
649
650#[derive(Debug, Clone, Eq, PartialEq)]
652pub struct ConstraintDef {
653 pub name: Box<str>,
655
656 pub data: ConstraintData,
658}
659
660impl From<ConstraintDef> for RawConstraintDefV9 {
661 fn from(val: ConstraintDef) -> Self {
662 RawConstraintDefV9 {
663 name: Some(val.name),
664 data: val.data.into(),
665 }
666 }
667}
668
669#[derive(Debug, Clone, Eq, PartialEq)]
671#[non_exhaustive]
672pub enum ConstraintData {
673 Unique(UniqueConstraintData),
674}
675
676impl ConstraintData {
677 pub fn unique_columns(&self) -> Option<&ColSet> {
680 match &self {
681 ConstraintData::Unique(UniqueConstraintData { columns }) => Some(columns),
682 }
683 }
684}
685
686impl From<ConstraintData> for RawConstraintDataV9 {
687 fn from(val: ConstraintData) -> Self {
688 match val {
689 ConstraintData::Unique(unique) => RawConstraintDataV9::Unique(unique.into()),
690 }
691 }
692}
693
694#[derive(Debug, Clone, Eq, PartialEq)]
698pub struct UniqueConstraintData {
699 pub columns: ColSet,
701}
702
703impl From<UniqueConstraintData> for RawUniqueConstraintDataV9 {
704 fn from(val: UniqueConstraintData) -> Self {
705 RawUniqueConstraintDataV9 {
706 columns: val.columns.into(),
707 }
708 }
709}
710
711impl From<UniqueConstraintData> for ConstraintData {
712 fn from(val: UniqueConstraintData) -> Self {
713 ConstraintData::Unique(val)
714 }
715}
716
717#[derive(Debug, Clone, Eq, PartialEq)]
719pub struct RowLevelSecurityDef {
720 pub sql: RawSql,
722}
723
724impl From<RowLevelSecurityDef> for RawRowLevelSecurityDefV9 {
725 fn from(val: RowLevelSecurityDef) -> Self {
726 RawRowLevelSecurityDefV9 { sql: val.sql }
727 }
728}
729
730#[derive(Debug, Clone, Eq, PartialEq)]
732#[non_exhaustive]
733pub struct ScheduleDef {
734 pub name: Box<str>,
736
737 pub at_column: ColId,
741
742 pub id_column: ColId,
746
747 pub reducer_name: Identifier,
750}
751
752impl From<ScheduleDef> for RawScheduleDefV9 {
753 fn from(val: ScheduleDef) -> Self {
754 RawScheduleDefV9 {
755 name: Some(val.name),
756 reducer_name: val.reducer_name.into(),
757 scheduled_at_column: val.at_column,
758 }
759 }
760}
761
762#[derive(Debug, Clone, Eq, PartialEq)]
764#[non_exhaustive]
765pub struct TypeDef {
766 pub name: ScopedTypeName,
768
769 pub ty: AlgebraicTypeRef,
773
774 pub custom_ordering: bool,
776}
777impl From<TypeDef> for RawTypeDefV9 {
778 fn from(val: TypeDef) -> Self {
779 RawTypeDefV9 {
780 name: val.name.into(),
781 ty: val.ty,
782 custom_ordering: val.custom_ordering,
783 }
784 }
785}
786
787#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
792pub struct ScopedTypeName {
793 scope: Box<[Identifier]>,
798
799 name: Identifier,
803}
804impl ScopedTypeName {
805 pub fn new(scope: Box<[Identifier]>, name: Identifier) -> Self {
807 ScopedTypeName { scope, name }
808 }
809
810 pub fn try_new(
813 scope: impl IntoIterator<Item = RawIdentifier>,
814 name: impl Into<RawIdentifier>,
815 ) -> Result<Self, ErrorStream<IdentifierError>> {
816 let scope = scope
817 .into_iter()
818 .map(|chunk| Identifier::new(chunk).map_err(ErrorStream::from))
819 .collect_all_errors();
820 let name = Identifier::new(name.into()).map_err(ErrorStream::from);
821 let (scope, name) = (scope, name).combine_errors()?;
822 Ok(ScopedTypeName { scope, name })
823 }
824
825 pub fn from_name(name: Identifier) -> Self {
827 ScopedTypeName {
828 scope: Box::new([]),
829 name,
830 }
831 }
832
833 pub fn name(&self) -> &Identifier {
835 &self.name
836 }
837
838 pub fn as_identifier(&self) -> Option<&Identifier> {
840 self.scope.is_empty().then_some(&self.name)
841 }
842
843 pub fn name_segments(&self) -> impl Iterator<Item = &Identifier> {
845 self.scope.iter().chain(std::iter::once(&self.name))
846 }
847}
848impl fmt::Debug for ScopedTypeName {
849 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
850 f.write_char('"')?;
853 for scope in &*self.scope {
854 write!(f, "{}::", scope)?;
855 }
856 write!(f, "{}\"", self.name)
857 }
858}
859impl fmt::Display for ScopedTypeName {
860 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
861 for scope in &*self.scope {
862 write!(f, "{}::", scope)?;
863 }
864 fmt::Display::fmt(&self.name, f)
865 }
866}
867impl TryFrom<RawScopedTypeNameV9> for ScopedTypeName {
868 type Error = ErrorStream<IdentifierError>;
869
870 fn try_from(value: RawScopedTypeNameV9) -> Result<Self, Self::Error> {
871 Self::try_new(value.scope.into_vec(), value.name)
872 }
873}
874impl From<ScopedTypeName> for RawScopedTypeNameV9 {
875 fn from(val: ScopedTypeName) -> Self {
876 RawScopedTypeNameV9 {
877 scope: val.scope.into_vec().into_iter().map_into().collect(),
878 name: val.name.into(),
879 }
880 }
881}
882
883#[derive(Debug, Clone, Eq, PartialEq)]
885#[non_exhaustive]
886pub struct ReducerDef {
887 pub name: Identifier,
889
890 pub params: ProductType,
894
895 pub params_for_generate: ProductTypeDef,
899
900 pub lifecycle: Option<Lifecycle>,
902}
903
904impl From<ReducerDef> for RawReducerDefV9 {
905 fn from(val: ReducerDef) -> Self {
906 RawReducerDefV9 {
907 name: val.name.into(),
908 params: val.params,
909 lifecycle: val.lifecycle,
910 }
911 }
912}
913
914impl ModuleDefLookup for TableDef {
915 type Key<'a> = &'a Identifier;
916
917 fn key(&self) -> Self::Key<'_> {
918 &self.name
919 }
920
921 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
922 module_def.tables.get(key)
923 }
924}
925
926impl ModuleDefLookup for SequenceDef {
927 type Key<'a> = &'a str;
928
929 fn key(&self) -> Self::Key<'_> {
930 &self.name
931 }
932
933 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
934 module_def.stored_in_table_def(key)?.sequences.get(key)
935 }
936}
937
938impl ModuleDefLookup for IndexDef {
939 type Key<'a> = &'a str;
940
941 fn key(&self) -> Self::Key<'_> {
942 &self.name
943 }
944
945 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
946 module_def.stored_in_table_def(key)?.indexes.get(key)
947 }
948}
949
950impl ModuleDefLookup for ColumnDef {
951 type Key<'a> = (&'a Identifier, &'a Identifier);
955
956 fn key(&self) -> Self::Key<'_> {
957 (&self.table_name, &self.name)
958 }
959
960 fn lookup<'a>(module_def: &'a ModuleDef, (table_name, name): Self::Key<'_>) -> Option<&'a Self> {
961 module_def
962 .tables
963 .get(table_name)
964 .and_then(|table| table.get_column_by_name(name))
965 }
966}
967
968impl ModuleDefLookup for ConstraintDef {
969 type Key<'a> = &'a str;
970
971 fn key(&self) -> Self::Key<'_> {
972 &self.name
973 }
974
975 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
976 module_def.stored_in_table_def(key)?.constraints.get(key)
977 }
978}
979
980impl ModuleDefLookup for RawRowLevelSecurityDefV9 {
981 type Key<'a> = &'a RawSql;
982
983 fn key(&self) -> Self::Key<'_> {
984 &self.sql
985 }
986
987 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
988 module_def.row_level_security_raw.get(key)
989 }
990}
991
992impl ModuleDefLookup for ScheduleDef {
993 type Key<'a> = &'a str;
994
995 fn key(&self) -> Self::Key<'_> {
996 &self.name
997 }
998
999 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1000 let schedule = module_def.stored_in_table_def(key)?.schedule.as_ref()?;
1001 if &schedule.name[..] == key {
1002 Some(schedule)
1003 } else {
1004 None
1005 }
1006 }
1007}
1008
1009impl ModuleDefLookup for TypeDef {
1010 type Key<'a> = &'a ScopedTypeName;
1011
1012 fn key(&self) -> Self::Key<'_> {
1013 &self.name
1014 }
1015
1016 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1017 module_def.types.get(key)
1018 }
1019}
1020
1021impl ModuleDefLookup for ReducerDef {
1022 type Key<'a> = &'a Identifier;
1023
1024 fn key(&self) -> Self::Key<'_> {
1025 &self.name
1026 }
1027
1028 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1029 module_def.reducers.get(key)
1030 }
1031}
1032
1033fn to_raw<Def, RawDef, Name, A>(data: HashMap<Name, Def, A>) -> Vec<RawDef>
1034where
1035 Def: ModuleDefLookup + Into<RawDef>,
1036 Name: Eq + Ord + 'static,
1037{
1038 let sorted: BTreeMap<Name, Def> = data.into_iter().collect();
1039 sorted.into_values().map_into().collect()
1040}
1041
1042#[cfg(test)]
1043mod tests {
1044 use super::*;
1045 use proptest::prelude::*;
1046
1047 proptest! {
1048 #[test]
1049 fn to_raw_deterministic(vec in prop::collection::vec(any::<u32>(), 0..5)) {
1050 let mut map = HashMap::new();
1051 let name = ScopedTypeName::try_new([], "fake_name").unwrap();
1052 for k in vec {
1053 let def = TypeDef { name: name.clone(), ty: AlgebraicTypeRef(k), custom_ordering: false };
1054 map.insert(k, def);
1055 }
1056 let raw: Vec<RawTypeDefV9> = to_raw(map.clone());
1057 let raw2: Vec<RawTypeDefV9> = to_raw(map);
1058 prop_assert_eq!(raw, raw2);
1059 }
1060 }
1061}