Skip to content

Commit a7dc1e9

Browse files
committed
case conversion
1 parent a7ff6f8 commit a7dc1e9

5 files changed

Lines changed: 269 additions & 153 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/schema/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ enum-as-inner.workspace = true
3333
enum-map.workspace = true
3434
insta.workspace = true
3535
termcolor.workspace = true
36+
convert_case.workspace = true
3637

3738
[dev-dependencies]
3839
spacetimedb-lib = { path = "../lib", features = ["test"] }

crates/schema/src/def/validate/v10.rs

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use spacetimedb_lib::de::DeserializeSeed as _;
44
use spacetimedb_sats::{Typespace, WithTypespace};
55

66
use crate::def::validate::v9::{
7-
check_function_names_are_unique, check_scheduled_functions_exist, generate_schedule_name, identifier,
8-
CoreValidator, TableValidator, ViewValidator,
7+
check_function_names_are_unique, check_scheduled_functions_exist, generate_schedule_name, CoreValidator,
8+
TableValidator, ViewValidator,
99
};
1010
use crate::def::*;
1111
use crate::error::ValidationError;
@@ -15,8 +15,10 @@ use crate::{def::validate::Result, error::TypeLocation};
1515
/// Validate a `RawModuleDefV9` and convert it into a `ModuleDef`,
1616
/// or return a stream of errors if the definition is invalid.
1717
pub fn validate(def: RawModuleDefV10) -> Result<ModuleDef> {
18-
let typespace = def.typespace().cloned().unwrap_or_else(|| Typespace::EMPTY.clone());
18+
let mut typespace = def.typespace().cloned().unwrap_or_else(|| Typespace::EMPTY.clone());
1919
let known_type_definitions = def.types().into_iter().flatten().map(|def| def.ty);
20+
let case_policy = def.case_conversion_policy();
21+
CoreValidator::typespace_case_conversion(case_policy, &mut typespace);
2022

2123
let mut validator = ModuleValidatorV10 {
2224
core: CoreValidator {
@@ -25,6 +27,7 @@ pub fn validate(def: RawModuleDefV10) -> Result<ModuleDef> {
2527
type_namespace: Default::default(),
2628
lifecycle_reducers: Default::default(),
2729
typespace_for_generate: TypespaceForGenerate::builder(&typespace, known_type_definitions),
30+
case_policy,
2831
},
2932
};
3033

@@ -124,7 +127,11 @@ pub fn validate(def: RawModuleDefV10) -> Result<ModuleDef> {
124127
.into_iter()
125128
.flatten()
126129
.map(|lifecycle_def| {
127-
let function_name = ReducerName::new(identifier(lifecycle_def.function_name.clone())?);
130+
let function_name = ReducerName::new(
131+
validator
132+
.core
133+
.identifier_with_case(lifecycle_def.function_name.clone())?,
134+
);
128135

129136
let (pos, _) = reducers_vec
130137
.iter()
@@ -281,7 +288,7 @@ impl<'a> ModuleValidatorV10<'a> {
281288
})?;
282289

283290
let mut table_validator =
284-
TableValidator::new(raw_table_name.clone(), product_type_ref, product_type, &mut self.core);
291+
TableValidator::new(raw_table_name.clone(), product_type_ref, product_type, &mut self.core)?;
285292

286293
// Validate columns first
287294
let mut columns: Vec<ColumnDef> = (0..product_type.elements.len())
@@ -341,7 +348,7 @@ impl<'a> ModuleValidatorV10<'a> {
341348
let name = table_validator
342349
.add_to_global_namespace(raw_table_name.clone())
343350
.and_then(|name| {
344-
let name = identifier(name)?;
351+
let name = self.core.identifier_with_case(name)?;
345352
if table_type != TableType::System && name.starts_with("st_") {
346353
Err(ValidationError::TableNameReserved { table: name }.into())
347354
} else {
@@ -426,7 +433,7 @@ impl<'a> ModuleValidatorV10<'a> {
426433
arg_name,
427434
});
428435

429-
let name_result = identifier(source_name.clone());
436+
let name_result = self.core.identifier_with_case(source_name.clone());
430437

431438
let return_res: Result<_> = (ok_return_type.is_unit() && err_return_type.is_string())
432439
.then_some((ok_return_type.clone(), err_return_type.clone()))
@@ -462,15 +469,15 @@ impl<'a> ModuleValidatorV10<'a> {
462469
&mut self,
463470
schedule: RawScheduleDefV10,
464471
tables: &HashMap<Identifier, TableDef>,
465-
) -> Result<(ScheduleDef, RawIdentifier)> {
472+
) -> Result<(ScheduleDef, Identifier)> {
466473
let RawScheduleDefV10 {
467474
source_name,
468475
table_name,
469476
schedule_at_col,
470477
function_name,
471478
} = schedule;
472479

473-
let table_ident = identifier(table_name.clone())?;
480+
let table_ident = self.core.identifier_with_case(table_name.clone())?;
474481

475482
// Look up the table to validate the schedule
476483
let table = tables.get(&table_ident).ok_or_else(|| ValidationError::TableNotFound {
@@ -491,13 +498,13 @@ impl<'a> ModuleValidatorV10<'a> {
491498
self.core
492499
.validate_schedule_def(
493500
table_name.clone(),
494-
identifier(source_name)?,
501+
self.core.identifier_with_case(source_name)?,
495502
function_name,
496503
product_type,
497504
schedule_at_col,
498505
table.primary_key,
499506
)
500-
.map(|schedule_def| (schedule_def, table_name))
507+
.map(|schedule_def| (schedule_def, table_ident))
501508
}
502509

503510
fn validate_lifecycle_reducer(
@@ -537,7 +544,7 @@ impl<'a> ModuleValidatorV10<'a> {
537544
&return_type,
538545
);
539546

540-
let name_result = identifier(source_name);
547+
let name_result = self.core.identifier_with_case(source_name);
541548

542549
let (name_result, params_for_generate, return_type_for_generate) =
543550
(name_result, params_for_generate, return_type_for_generate).combine_errors()?;
@@ -619,9 +626,9 @@ impl<'a> ModuleValidatorV10<'a> {
619626
&params,
620627
&params_for_generate,
621628
&mut self.core,
622-
);
629+
)?;
623630

624-
let name_result = view_validator.add_to_global_namespace(name).and_then(identifier);
631+
let name_result = view_validator.add_to_global_namespace(name);
625632

626633
let n = product_type.elements.len();
627634
let return_columns = (0..n)
@@ -637,7 +644,7 @@ impl<'a> ModuleValidatorV10<'a> {
637644
(name_result, return_type_for_generate, return_columns, param_columns).combine_errors()?;
638645

639646
Ok(ViewDef {
640-
name: name_result,
647+
name: self.core.identifier_with_case(name_result)?,
641648
is_anonymous,
642649
is_public,
643650
params,
@@ -679,13 +686,13 @@ fn attach_lifecycles_to_reducers(
679686

680687
fn attach_schedules_to_tables(
681688
tables: &mut HashMap<Identifier, TableDef>,
682-
schedules: Vec<(ScheduleDef, RawIdentifier)>,
689+
schedules: Vec<(ScheduleDef, Identifier)>,
683690
) -> Result<()> {
684691
for schedule in schedules {
685692
let (schedule, table_name) = schedule;
686693
let table = tables.values_mut().find(|t| *t.name == *table_name).ok_or_else(|| {
687694
ValidationError::MissingScheduleTable {
688-
table_name: table_name.clone(),
695+
table_name: table_name.as_raw().clone(),
689696
schedule_name: schedule.name.clone(),
690697
}
691698
})?;
@@ -715,11 +722,12 @@ mod tests {
715722
IndexAlgorithm, IndexDef, SequenceDef, UniqueConstraintData,
716723
};
717724
use crate::error::*;
725+
use crate::identifier::Identifier;
718726
use crate::type_for_generate::ClientCodegenError;
719727

720728
use itertools::Itertools;
721729
use spacetimedb_data_structures::expect_error_matching;
722-
use spacetimedb_lib::db::raw_def::v10::RawModuleDefV10Builder;
730+
use spacetimedb_lib::db::raw_def::v10::{CaseConversionPolicy, RawModuleDefV10Builder};
723731
use spacetimedb_lib::db::raw_def::v9::{btree, direct, hash};
724732
use spacetimedb_lib::db::raw_def::*;
725733
use spacetimedb_lib::ScheduleAt;
@@ -729,7 +737,7 @@ mod tests {
729737

730738
/// This test attempts to exercise every successful path in the validation code.
731739
#[test]
732-
fn valid_definition() {
740+
fn test_valid_definition_with_default_policy() {
733741
let mut builder = RawModuleDefV10Builder::new();
734742

735743
let product_type = AlgebraicType::product([("a", AlgebraicType::U64), ("b", AlgebraicType::String)]);
@@ -752,8 +760,8 @@ mod tests {
752760
"Apples",
753761
ProductType::from([
754762
("id", AlgebraicType::U64),
755-
("name", AlgebraicType::String),
756-
("count", AlgebraicType::U16),
763+
("Apple_name", AlgebraicType::String),
764+
("countFresh", AlgebraicType::U16),
757765
("type", sum_type_ref.into()),
758766
]),
759767
true,
@@ -816,9 +824,11 @@ mod tests {
816824

817825
let def: ModuleDef = builder.finish().try_into().unwrap();
818826

819-
let apples = expect_identifier("Apples");
820-
let bananas = expect_identifier("Bananas");
821-
let deliveries = expect_identifier("Deliveries");
827+
let casing_policy = CaseConversionPolicy::default();
828+
assert_eq!(casing_policy, CaseConversionPolicy::SnakeCase);
829+
let apples = Identifier::for_test("apples");
830+
let bananas = Identifier::for_test("bananas");
831+
let deliveries = Identifier::for_test("deliveries");
822832

823833
assert_eq!(def.tables.len(), 3);
824834

@@ -832,10 +842,10 @@ mod tests {
832842
assert_eq!(apples_def.columns[0].name, expect_identifier("id"));
833843
assert_eq!(apples_def.columns[0].ty, AlgebraicType::U64);
834844
assert_eq!(apples_def.columns[0].default_value, None);
835-
assert_eq!(apples_def.columns[1].name, expect_identifier("name"));
845+
assert_eq!(apples_def.columns[1].name, expect_identifier("apple_name"));
836846
assert_eq!(apples_def.columns[1].ty, AlgebraicType::String);
837847
assert_eq!(apples_def.columns[1].default_value, None);
838-
assert_eq!(apples_def.columns[2].name, expect_identifier("count"));
848+
assert_eq!(apples_def.columns[2].name, expect_identifier("count_fresh"));
839849
assert_eq!(apples_def.columns[2].ty, AlgebraicType::U16);
840850
assert_eq!(apples_def.columns[2].default_value, Some(AlgebraicValue::U16(37)));
841851
assert_eq!(apples_def.columns[3].name, expect_identifier("type"));
@@ -846,7 +856,7 @@ mod tests {
846856
assert_eq!(apples_def.primary_key, None);
847857

848858
assert_eq!(apples_def.constraints.len(), 2);
849-
let apples_unique_constraint = "Apples_type_key";
859+
let apples_unique_constraint = "apples_type_key";
850860
assert_eq!(
851861
apples_def.constraints[apples_unique_constraint].data,
852862
ConstraintData::Unique(UniqueConstraintData {
@@ -945,7 +955,7 @@ mod tests {
945955
check_product_type(&def, bananas_def);
946956
check_product_type(&def, delivery_def);
947957

948-
let product_type_name = expect_type_name("scope1::scope2::ReferencedProduct");
958+
let product_type_name = expect_type_name("Scope1::Scope2::ReferencedProduct");
949959
let sum_type_name = expect_type_name("ReferencedSum");
950960
let apples_type_name = expect_type_name("Apples");
951961
let bananas_type_name = expect_type_name("Bananas");
@@ -1355,7 +1365,7 @@ mod tests {
13551365
let result: Result<ModuleDef> = builder.finish().try_into();
13561366

13571367
expect_error_matching!(result, ValidationError::DuplicateTypeName { name } => {
1358-
name == &expect_type_name("scope1::scope2::Duplicate")
1368+
name == &expect_type_name("Scope1::Scope2::Duplicate")
13591369
});
13601370
}
13611371

@@ -1394,7 +1404,7 @@ mod tests {
13941404
let result: Result<ModuleDef> = builder.finish().try_into();
13951405

13961406
expect_error_matching!(result, ValidationError::MissingScheduledFunction { schedule, function } => {
1397-
&schedule[..] == "Deliveries_sched" &&
1407+
&schedule[..] == "deliveries_sched" &&
13981408
function == &expect_identifier("check_deliveries")
13991409
});
14001410
}

0 commit comments

Comments
 (0)