1use std::fmt::{self, Debug, Write};
19use std::hash::Hash;
20
21use crate::error::{IdentifierError, ValidationErrors};
22use crate::identifier::Identifier;
23use crate::schema::{Schema, TableSchema};
24use crate::type_for_generate::{AlgebraicTypeUse, ProductTypeDef, TypespaceForGenerate};
25use deserialize::ReducerArgsDeserializeSeed;
26use hashbrown::Equivalent;
27use itertools::Itertools;
28use spacetimedb_data_structures::error_stream::{CollectAllErrors, CombineErrors, ErrorStream};
29use spacetimedb_data_structures::map::HashMap;
30use spacetimedb_lib::db::raw_def;
31use spacetimedb_lib::db::raw_def::v9::{
32 Lifecycle, RawConstraintDataV9, RawConstraintDefV9, RawIdentifier, RawIndexAlgorithm, RawIndexDefV9,
33 RawModuleDefV9, RawReducerDefV9, RawRowLevelSecurityDefV9, RawScheduleDefV9, RawScopedTypeNameV9, RawSequenceDefV9,
34 RawSql, RawTableDefV9, RawTypeDefV9, RawUniqueConstraintDataV9, TableAccess, TableType,
35};
36use spacetimedb_lib::{ProductType, RawModuleDef};
37use spacetimedb_primitives::{ColId, ColList, ColSet, TableId};
38use spacetimedb_sats::AlgebraicType;
39use spacetimedb_sats::{AlgebraicTypeRef, Typespace};
40
41pub mod deserialize;
42pub mod validate;
43
44pub type IdentifierMap<T> = HashMap<Identifier, T>;
46
47#[derive(Debug, Clone)]
81#[non_exhaustive]
82pub struct ModuleDef {
83 tables: IdentifierMap<TableDef>,
85
86 reducers: IdentifierMap<ReducerDef>,
88
89 types: HashMap<ScopedTypeName, TypeDef>,
91
92 typespace: Typespace,
94
95 typespace_for_generate: TypespaceForGenerate,
97
98 stored_in_table_def: IdentifierMap<Identifier>,
103
104 refmap: HashMap<AlgebraicTypeRef, ScopedTypeName>,
106
107 row_level_security_raw: HashMap<RawSql, RawRowLevelSecurityDefV9>,
111}
112
113impl ModuleDef {
114 pub fn tables(&self) -> impl Iterator<Item = &TableDef> {
116 self.tables.values()
117 }
118
119 pub fn indexes(&self) -> impl Iterator<Item = &IndexDef> {
121 self.tables().flat_map(|table| table.indexes.values())
122 }
123
124 pub fn constraints(&self) -> impl Iterator<Item = &ConstraintDef> {
126 self.tables().flat_map(|table| table.constraints.values())
127 }
128
129 pub fn sequences(&self) -> impl Iterator<Item = &SequenceDef> {
131 self.tables().flat_map(|table| table.sequences.values())
132 }
133
134 pub fn schedules(&self) -> impl Iterator<Item = &ScheduleDef> {
136 self.tables().filter_map(|table| table.schedule.as_ref())
137 }
138
139 pub fn reducers(&self) -> impl Iterator<Item = &ReducerDef> {
141 self.reducers.values()
142 }
143
144 pub fn types(&self) -> impl Iterator<Item = &TypeDef> {
146 self.types.values()
147 }
148
149 pub fn row_level_security(&self) -> impl Iterator<Item = &RawRowLevelSecurityDefV9> {
151 self.row_level_security_raw.values()
152 }
153
154 pub fn typespace(&self) -> &Typespace {
167 &self.typespace
168 }
169
170 pub fn typespace_for_generate(&self) -> &TypespaceForGenerate {
172 &self.typespace_for_generate
173 }
174
175 pub fn stored_in_table_def(&self, name: &Identifier) -> Option<&TableDef> {
179 self.stored_in_table_def
180 .get(name)
181 .and_then(|table_name| self.tables.get(table_name))
182 }
183
184 pub fn lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> Option<&T> {
186 T::lookup(self, key)
187 }
188
189 pub fn lookup_expect<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {
192 T::lookup(self, key).expect("expected ModuleDef to contain key, but it does not")
193 }
194
195 pub fn table<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&TableDef> {
197 self.tables.get(name)
199 }
200
201 pub fn reducer<K: ?Sized + Hash + Equivalent<Identifier>>(&self, name: &K) -> Option<&ReducerDef> {
203 self.reducers.get(name)
205 }
206
207 pub fn reducer_arg_deserialize_seed<K: ?Sized + Hash + Equivalent<Identifier>>(
210 &self,
211 name: &K,
212 ) -> Option<ReducerArgsDeserializeSeed> {
213 let reducer = self.reducer(name)?;
214 Some(ReducerArgsDeserializeSeed(self.typespace.with_type(reducer)))
215 }
216
217 pub fn type_def_from_ref(&self, r: AlgebraicTypeRef) -> Option<(&ScopedTypeName, &TypeDef)> {
219 let name = self.refmap.get(&r)?;
220 let def = self
221 .types
222 .get(name)
223 .expect("if it was in refmap, it should be in types");
224
225 Some((name, def))
226 }
227
228 pub fn table_schema<K: ?Sized + Hash + Equivalent<Identifier>>(
231 &self,
232 name: &K,
233 table_id: TableId,
234 ) -> Option<TableSchema> {
235 let table_def = self.tables.get(name)?;
237 Some(TableSchema::from_module_def(self, table_def, (), table_id))
238 }
239
240 fn generate_indexes(&mut self) {
245 for table in self.tables.values_mut() {
246 for constraint in table.constraints.values() {
247 let ConstraintData::Unique(UniqueConstraintData { columns }) = &constraint.data;
248
249 if table.indexes.values().any(|index| {
251 let IndexDef {
252 algorithm: IndexAlgorithm::BTree(BTreeAlgorithm { columns: index_columns }),
253 ..
254 } = index;
255
256 index_columns == &**columns
257 }) {
258 continue;
259 }
260
261 let constraint_name = &constraint
263 .name
264 .trim_start_matches(&format!("ct_{}_", table.name))
265 .trim_end_matches("_unique");
266 let mut index_name = Identifier::new(format!("idx_{}_{}_unique", table.name, constraint_name).into())
267 .expect("validated identifier parts");
268
269 while self.stored_in_table_def.contains_key(&index_name) {
272 index_name =
273 Identifier::new(format!("{}_1", index_name).into()).expect("validated identifier parts");
274 }
275
276 table.indexes.insert(
277 index_name.clone(),
278 IndexDef {
279 name: index_name.clone(),
280 algorithm: IndexAlgorithm::BTree(BTreeAlgorithm {
281 columns: columns.clone().into(),
282 }),
283 accessor_name: None, },
285 );
286 self.stored_in_table_def.insert(index_name, table.name.clone());
287 }
288 }
289 }
290
291 pub fn expect_lookup<T: ModuleDefLookup>(&self, key: T::Key<'_>) -> &T {
293 if let Some(result) = T::lookup(self, key) {
294 result
295 } else {
296 panic!("expected ModuleDef to contain {:?}, but it does not", key);
297 }
298 }
299
300 pub fn expect_contains<Def: ModuleDefLookup>(&self, def: &Def) {
302 if let Some(my_def) = self.lookup(def.key()) {
303 assert_eq!(
304 def as *const Def, my_def as *const Def,
305 "expected ModuleDef to contain {:?}, but it contained {:?}",
306 def, my_def
307 );
308 } else {
309 panic!("expected ModuleDef to contain {:?}, but it does not", def.key());
310 }
311 }
312}
313
314impl TryFrom<RawModuleDef> for ModuleDef {
315 type Error = ValidationErrors;
316
317 fn try_from(raw: RawModuleDef) -> Result<Self, Self::Error> {
318 match raw {
319 RawModuleDef::V8BackCompat(v8_mod) => Self::try_from(v8_mod),
320 RawModuleDef::V9(v9_mod) => Self::try_from(v9_mod),
321 _ => unimplemented!(),
322 }
323 }
324}
325impl TryFrom<raw_def::v8::RawModuleDefV8> for ModuleDef {
326 type Error = ValidationErrors;
327
328 fn try_from(v8_mod: raw_def::v8::RawModuleDefV8) -> Result<Self, Self::Error> {
329 validate::v8::validate(v8_mod)
332 }
333}
334impl TryFrom<raw_def::v9::RawModuleDefV9> for ModuleDef {
335 type Error = ValidationErrors;
336
337 fn try_from(v9_mod: raw_def::v9::RawModuleDefV9) -> Result<Self, Self::Error> {
338 let mut result = validate::v9::validate(v9_mod)?;
339 result.generate_indexes();
340 Ok(result)
341 }
342}
343impl From<ModuleDef> for RawModuleDefV9 {
344 fn from(val: ModuleDef) -> Self {
345 let ModuleDef {
346 tables,
347 reducers,
348 types,
349 typespace,
350 stored_in_table_def: _,
351 typespace_for_generate: _,
352 refmap: _,
353 row_level_security_raw,
354 } = val;
355
356 RawModuleDefV9 {
357 tables: to_raw(tables, |table: &RawTableDefV9| &table.name),
358 reducers: to_raw(reducers, |reducer: &RawReducerDefV9| &reducer.name),
359 types: to_raw(types, |type_: &RawTypeDefV9| &type_.name),
360 misc_exports: vec![],
361 typespace,
362 row_level_security: row_level_security_raw.into_iter().map(|(_, def)| def).collect(),
363 }
364 }
365}
366
367pub trait ModuleDefLookup: Sized + Debug + 'static {
371 type Key<'a>: Debug + Copy;
373
374 fn key(&self) -> Self::Key<'_>;
376
377 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self>;
379}
380
381#[derive(Debug, Clone, Eq, PartialEq)]
396#[non_exhaustive]
397pub struct TableDef {
398 pub name: Identifier,
402
403 pub product_type_ref: AlgebraicTypeRef,
409
410 pub primary_key: Option<ColId>,
417
418 pub columns: Vec<ColumnDef>,
421
422 pub indexes: IdentifierMap<IndexDef>,
424
425 pub constraints: IdentifierMap<ConstraintDef>,
427
428 pub sequences: IdentifierMap<SequenceDef>,
430
431 pub schedule: Option<ScheduleDef>,
433
434 pub table_type: TableType,
436
437 pub table_access: TableAccess,
439}
440
441impl TableDef {
442 pub fn get_column(&self, id: ColId) -> Option<&ColumnDef> {
444 self.columns.get(id.idx())
445 }
446 pub fn get_column_by_name(&self, name: &Identifier) -> Option<&ColumnDef> {
448 self.columns.iter().find(|c| &c.name == name)
449 }
450}
451
452impl From<TableDef> for RawTableDefV9 {
453 fn from(val: TableDef) -> Self {
454 let TableDef {
455 name,
456 product_type_ref,
457 primary_key,
458 columns: _, indexes,
460 constraints,
461 sequences,
462 schedule,
463 table_type,
464 table_access,
465 } = val;
466
467 RawTableDefV9 {
468 name: name.into(),
469 product_type_ref,
470 primary_key: ColList::from_iter(primary_key),
471 indexes: to_raw(indexes, |index: &RawIndexDefV9| &index.name),
472 constraints: to_raw(constraints, |constraint: &RawConstraintDefV9| &constraint.name),
473 sequences: to_raw(sequences, |sequence: &RawSequenceDefV9| &sequence.name),
474 schedule: schedule.map(Into::into),
475 table_type,
476 table_access,
477 }
478 }
479}
480
481#[derive(Debug, Clone, Eq, PartialEq)]
483pub struct SequenceDef {
484 pub name: Identifier,
486
487 pub column: ColId,
492
493 pub start: Option<i128>,
497
498 pub min_value: Option<i128>,
501
502 pub max_value: Option<i128>,
505
506 pub increment: i128,
508}
509
510impl From<SequenceDef> for RawSequenceDefV9 {
511 fn from(val: SequenceDef) -> Self {
512 RawSequenceDefV9 {
513 name: val.name.into(),
514 column: val.column,
515 start: val.start,
516 min_value: val.min_value,
517 max_value: val.max_value,
518 increment: val.increment,
519 }
520 }
521}
522
523#[derive(Debug, Clone, Eq, PartialEq)]
528#[non_exhaustive]
529pub struct IndexDef {
530 pub name: Identifier,
532
533 pub accessor_name: Option<Identifier>,
544
545 pub algorithm: IndexAlgorithm,
547}
548
549impl IndexDef {
550 pub fn generated(&self) -> bool {
552 self.accessor_name.is_none()
553 }
554}
555
556impl From<IndexDef> for RawIndexDefV9 {
557 fn from(val: IndexDef) -> Self {
558 RawIndexDefV9 {
559 name: val.name.into(),
560 algorithm: match val.algorithm {
561 IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => RawIndexAlgorithm::BTree { columns },
562 },
563 accessor_name: val.accessor_name.map(Into::into),
564 }
565 }
566}
567
568#[non_exhaustive]
570#[derive(Debug, Clone, Eq, PartialEq)]
571pub enum IndexAlgorithm {
572 BTree(BTreeAlgorithm),
574}
575
576impl IndexAlgorithm {
577 pub fn columns(&self) -> &ColList {
579 match self {
580 IndexAlgorithm::BTree(btree) => &btree.columns,
581 }
582 }
583}
584
585impl From<IndexAlgorithm> for RawIndexAlgorithm {
586 fn from(val: IndexAlgorithm) -> Self {
587 match val {
588 IndexAlgorithm::BTree(BTreeAlgorithm { columns }) => RawIndexAlgorithm::BTree { columns },
589 }
590 }
591}
592
593#[derive(Debug, Clone, Eq, PartialEq)]
595pub struct BTreeAlgorithm {
596 pub columns: ColList,
598}
599
600impl From<BTreeAlgorithm> for IndexAlgorithm {
601 fn from(val: BTreeAlgorithm) -> Self {
602 IndexAlgorithm::BTree(val)
603 }
604}
605
606#[derive(Debug, Clone, Eq, PartialEq)]
611#[non_exhaustive]
612pub struct ColumnDef {
613 pub name: Identifier,
617
618 pub col_id: ColId,
620
621 pub ty: AlgebraicType,
628
629 pub ty_for_generate: AlgebraicTypeUse,
631
632 pub table_name: Identifier,
634}
635
636#[derive(Debug, Clone, Eq, PartialEq)]
638pub struct ConstraintDef {
639 pub name: Identifier,
641
642 pub data: ConstraintData,
644}
645
646impl From<ConstraintDef> for RawConstraintDefV9 {
647 fn from(val: ConstraintDef) -> Self {
648 RawConstraintDefV9 {
649 name: val.name.into(),
650 data: val.data.into(),
651 }
652 }
653}
654
655#[derive(Debug, Clone, Eq, PartialEq)]
657#[non_exhaustive]
658pub enum ConstraintData {
659 Unique(UniqueConstraintData),
660}
661
662impl ConstraintData {
663 pub fn unique_columns(&self) -> Option<&ColSet> {
666 match &self {
667 ConstraintData::Unique(UniqueConstraintData { columns }) => Some(columns),
668 }
669 }
670}
671
672impl From<ConstraintData> for RawConstraintDataV9 {
673 fn from(val: ConstraintData) -> Self {
674 match val {
675 ConstraintData::Unique(unique) => RawConstraintDataV9::Unique(unique.into()),
676 }
677 }
678}
679
680#[derive(Debug, Clone, Eq, PartialEq)]
684pub struct UniqueConstraintData {
685 pub columns: ColSet,
687}
688
689impl From<UniqueConstraintData> for RawUniqueConstraintDataV9 {
690 fn from(val: UniqueConstraintData) -> Self {
691 RawUniqueConstraintDataV9 {
692 columns: val.columns.into(),
693 }
694 }
695}
696
697impl From<UniqueConstraintData> for ConstraintData {
698 fn from(val: UniqueConstraintData) -> Self {
699 ConstraintData::Unique(val)
700 }
701}
702
703#[derive(Debug, Clone, Eq, PartialEq)]
705pub struct RowLevelSecurityDef {
706 pub sql: RawSql,
708}
709
710impl From<RowLevelSecurityDef> for RawRowLevelSecurityDefV9 {
711 fn from(val: RowLevelSecurityDef) -> Self {
712 RawRowLevelSecurityDefV9 { sql: val.sql }
713 }
714}
715
716#[derive(Debug, Clone, Eq, PartialEq)]
718#[non_exhaustive]
719pub struct ScheduleDef {
720 pub name: Identifier,
722
723 pub at_column: ColId,
727
728 pub id_column: ColId,
732
733 pub reducer_name: Identifier,
736}
737
738impl From<ScheduleDef> for RawScheduleDefV9 {
739 fn from(val: ScheduleDef) -> Self {
740 RawScheduleDefV9 {
741 name: val.name.into(),
742 reducer_name: val.reducer_name.into(),
743 scheduled_at_column: val.at_column,
744 }
745 }
746}
747
748#[derive(Debug, Clone, Eq, PartialEq)]
750#[non_exhaustive]
751pub struct TypeDef {
752 pub name: ScopedTypeName,
754
755 pub ty: AlgebraicTypeRef,
759
760 pub custom_ordering: bool,
762}
763impl From<TypeDef> for RawTypeDefV9 {
764 fn from(val: TypeDef) -> Self {
765 RawTypeDefV9 {
766 name: val.name.into(),
767 ty: val.ty,
768 custom_ordering: val.custom_ordering,
769 }
770 }
771}
772
773#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
778pub struct ScopedTypeName {
779 scope: Box<[Identifier]>,
784
785 name: Identifier,
789}
790impl ScopedTypeName {
791 pub fn new(scope: Box<[Identifier]>, name: Identifier) -> Self {
793 ScopedTypeName { scope, name }
794 }
795
796 pub fn try_new(
799 scope: impl IntoIterator<Item = RawIdentifier>,
800 name: impl Into<RawIdentifier>,
801 ) -> Result<Self, ErrorStream<IdentifierError>> {
802 let scope = scope
803 .into_iter()
804 .map(|chunk| Identifier::new(chunk).map_err(ErrorStream::from))
805 .collect_all_errors();
806 let name = Identifier::new(name.into()).map_err(ErrorStream::from);
807 let (scope, name) = (scope, name).combine_errors()?;
808 Ok(ScopedTypeName { scope, name })
809 }
810
811 pub fn from_name(name: Identifier) -> Self {
813 ScopedTypeName {
814 scope: Box::new([]),
815 name,
816 }
817 }
818
819 pub fn name(&self) -> &Identifier {
821 &self.name
822 }
823
824 pub fn as_identifier(&self) -> Option<&Identifier> {
826 self.scope.is_empty().then_some(&self.name)
827 }
828
829 pub fn name_segments(&self) -> impl Iterator<Item = &Identifier> {
831 self.scope.iter().chain(std::iter::once(&self.name))
832 }
833}
834impl fmt::Debug for ScopedTypeName {
835 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
836 f.write_char('"')?;
839 for scope in &*self.scope {
840 write!(f, "{}::", scope)?;
841 }
842 write!(f, "{}\"", self.name)
843 }
844}
845impl fmt::Display for ScopedTypeName {
846 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
847 for scope in &*self.scope {
848 write!(f, "{}::", scope)?;
849 }
850 fmt::Display::fmt(&self.name, f)
851 }
852}
853impl TryFrom<RawScopedTypeNameV9> for ScopedTypeName {
854 type Error = ErrorStream<IdentifierError>;
855
856 fn try_from(value: RawScopedTypeNameV9) -> Result<Self, Self::Error> {
857 Self::try_new(value.scope.into_vec(), value.name)
858 }
859}
860impl From<ScopedTypeName> for RawScopedTypeNameV9 {
861 fn from(val: ScopedTypeName) -> Self {
862 RawScopedTypeNameV9 {
863 scope: val.scope.into_vec().into_iter().map_into().collect(),
864 name: val.name.into(),
865 }
866 }
867}
868
869#[derive(Debug, Clone, Eq, PartialEq)]
871#[non_exhaustive]
872pub struct ReducerDef {
873 pub name: Identifier,
875
876 pub params: ProductType,
880
881 pub params_for_generate: ProductTypeDef,
885
886 pub lifecycle: Option<Lifecycle>,
888}
889
890impl From<ReducerDef> for RawReducerDefV9 {
891 fn from(val: ReducerDef) -> Self {
892 RawReducerDefV9 {
893 name: val.name.into(),
894 params: val.params,
895 lifecycle: val.lifecycle,
896 }
897 }
898}
899
900impl ModuleDefLookup for TableDef {
901 type Key<'a> = &'a Identifier;
902
903 fn key(&self) -> Self::Key<'_> {
904 &self.name
905 }
906
907 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
908 module_def.tables.get(key)
909 }
910}
911
912impl ModuleDefLookup for SequenceDef {
913 type Key<'a> = &'a Identifier;
914
915 fn key(&self) -> Self::Key<'_> {
916 &self.name
917 }
918
919 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
920 module_def.stored_in_table_def(key)?.sequences.get(key)
921 }
922}
923
924impl ModuleDefLookup for IndexDef {
925 type Key<'a> = &'a Identifier;
926
927 fn key(&self) -> Self::Key<'_> {
928 &self.name
929 }
930
931 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
932 module_def.stored_in_table_def(key)?.indexes.get(key)
933 }
934}
935
936impl ModuleDefLookup for ColumnDef {
937 type Key<'a> = (&'a Identifier, &'a Identifier);
941
942 fn key(&self) -> Self::Key<'_> {
943 (&self.table_name, &self.name)
944 }
945
946 fn lookup<'a>(module_def: &'a ModuleDef, (table_name, name): Self::Key<'_>) -> Option<&'a Self> {
947 module_def
948 .tables
949 .get(table_name)
950 .and_then(|table| table.get_column_by_name(name))
951 }
952}
953
954impl ModuleDefLookup for ConstraintDef {
955 type Key<'a> = &'a Identifier;
956
957 fn key(&self) -> Self::Key<'_> {
958 &self.name
959 }
960
961 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
962 module_def.stored_in_table_def(key)?.constraints.get(key)
963 }
964}
965
966impl ModuleDefLookup for RawRowLevelSecurityDefV9 {
967 type Key<'a> = &'a RawSql;
968
969 fn key(&self) -> Self::Key<'_> {
970 &self.sql
971 }
972
973 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
974 module_def.row_level_security_raw.get(key)
975 }
976}
977
978impl ModuleDefLookup for ScheduleDef {
979 type Key<'a> = &'a Identifier;
980
981 fn key(&self) -> Self::Key<'_> {
982 &self.name
983 }
984
985 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
986 let schedule = module_def.stored_in_table_def(key)?.schedule.as_ref()?;
987 if &schedule.name == key {
988 Some(schedule)
989 } else {
990 None
991 }
992 }
993}
994
995impl ModuleDefLookup for TypeDef {
996 type Key<'a> = &'a ScopedTypeName;
997
998 fn key(&self) -> Self::Key<'_> {
999 &self.name
1000 }
1001
1002 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1003 module_def.types.get(key)
1004 }
1005}
1006
1007impl ModuleDefLookup for ReducerDef {
1008 type Key<'a> = &'a Identifier;
1009
1010 fn key(&self) -> Self::Key<'_> {
1011 &self.name
1012 }
1013
1014 fn lookup<'a>(module_def: &'a ModuleDef, key: Self::Key<'_>) -> Option<&'a Self> {
1015 module_def.reducers.get(key)
1016 }
1017}
1018
1019fn to_raw<Def, RawDef, Name, RawName>(data: HashMap<Name, Def>, f: impl Fn(&RawDef) -> &RawName) -> Vec<RawDef>
1020where
1021 Def: ModuleDefLookup + Into<RawDef>,
1022 RawName: Eq + Ord + 'static,
1023{
1024 let mut result: Vec<RawDef> = data.into_iter().map(|(_, def)| def.into()).collect();
1025 result.sort_by(|a, b| f(a).cmp(f(b)));
1026 result
1027}