diff --git a/psl/parser-database/src/attributes.rs b/psl/parser-database/src/attributes.rs index 98c1f8b6ad42..eccb2d5617a1 100644 --- a/psl/parser-database/src/attributes.rs +++ b/psl/parser-database/src/attributes.rs @@ -98,11 +98,15 @@ fn resolve_enum_attributes<'db>(enum_id: crate::EnumId, ast_enum: &'db ast::Enum } ctx.validate_visited_arguments(); } + + // @ignore + if ctx.visit_optional_single_attr("ignore") { + enum_attributes.ignored_values.insert(value_id); + ctx.validate_visited_arguments(); + } ctx.validate_visited_attributes(); } - // Now validate the enum attributes. - ctx.visit_attributes(enum_id); // @@map diff --git a/psl/parser-database/src/types.rs b/psl/parser-database/src/types.rs index ee0fca129ac7..6c4ec7d05af4 100644 --- a/psl/parser-database/src/types.rs +++ b/psl/parser-database/src/types.rs @@ -700,6 +700,8 @@ pub(super) struct EnumAttributes { pub(super) mapped_name: Option, /// @map on enum values. pub(super) mapped_values: HashMap, + /// @ignore on enum values. + pub(super) ignored_values: std::collections::HashSet, /// ```ignore /// @@schema("public") /// ^^^^^^^^ diff --git a/psl/parser-database/src/walkers/enum.rs b/psl/parser-database/src/walkers/enum.rs index 455bea664ea4..37fa7ec13b21 100644 --- a/psl/parser-database/src/walkers/enum.rs +++ b/psl/parser-database/src/walkers/enum.rs @@ -122,4 +122,11 @@ impl<'db> EnumValueWalker<'db> { .get(&(self.id.1)) .map(|id| &self.db[*id]) } + + /// True if the enum value is ignored. + pub fn is_ignored(self) -> bool { + self.db.types.enum_attributes[&self.id.0] + .ignored_values + .contains(&self.id.1) + } } diff --git a/psl/psl/Cargo.toml b/psl/psl/Cargo.toml index d61422ddef12..8539b8e5eb14 100644 --- a/psl/psl/Cargo.toml +++ b/psl/psl/Cargo.toml @@ -16,6 +16,7 @@ all = ["postgresql", "sqlite", "mysql", "cockroachdb", "mssql", "mongodb"] psl-core.workspace = true [dev-dependencies] +psl-core = { workspace = true, features = ["postgresql", "sqlite", "mysql", "cockroachdb", "mssql", "mongodb"] } base64.workspace = true dissimilar.workspace = true expect-test.workspace = true diff --git a/psl/psl/tests/attributes/ignore_positive.rs b/psl/psl/tests/attributes/ignore_positive.rs index 9cc1837dba2c..27d9975a5333 100644 --- a/psl/psl/tests/attributes/ignore_positive.rs +++ b/psl/psl/tests/attributes/ignore_positive.rs @@ -303,3 +303,21 @@ fn allow_ignore_on_relation_fields_on_valid_models() { .assert_has_relation_field("rel_e") .assert_ignored(true); } + +#[test] +fn allow_ignore_on_enum_values() { + let dml = r#" + enum SupportedCarTypes { + COUPE @ignore + SEDAN + VAN + } + "#; + + let datamodel = parse_schema(dml); + let enum_walker = datamodel.assert_has_enum("SupportedCarTypes"); + + enum_walker.assert_has_value("COUPE").assert_ignored(true); + enum_walker.assert_has_value("SEDAN").assert_ignored(false); + enum_walker.assert_has_value("VAN").assert_ignored(false); +} diff --git a/psl/psl/tests/common/asserts.rs b/psl/psl/tests/common/asserts.rs index 1bda7087e4b7..022502ba4f1e 100644 --- a/psl/psl/tests/common/asserts.rs +++ b/psl/psl/tests/common/asserts.rs @@ -14,6 +14,7 @@ use psl::{Diagnostics, StringFromEnvVar}; pub(crate) trait DatamodelAssert<'a> { fn assert_has_model(&'a self, name: &str) -> walkers::ModelWalker<'a>; fn assert_has_type(&'a self, name: &str) -> walkers::CompositeTypeWalker<'a>; + fn assert_has_enum(&'a self, name: &str) -> walkers::EnumWalker<'a>; } pub(crate) trait DatasourceAsserts { @@ -154,6 +155,14 @@ impl<'a> DatamodelAssert<'a> for psl::ValidatedSchema { .find(|m| m.name() == name) .expect("Type {name} not found") } + + #[track_caller] + fn assert_has_enum(&'a self, name: &str) -> walkers::EnumWalker<'a> { + self.db + .walk_enums() + .find(|e| e.name() == name) + .expect("Enum {name} not found") + } } impl RelationFieldAssert for walkers::RelationFieldWalker<'_> { @@ -755,3 +764,27 @@ impl IndexAssert for walkers::PrimaryKeyWalker<'_> { unreachable!("Primary key cannot define the index type."); } } +pub(crate) trait EnumAssert<'a> { + fn assert_has_value(&'a self, name: &str) -> walkers::EnumValueWalker<'a>; +} + +impl<'a> EnumAssert<'a> for walkers::EnumWalker<'a> { + #[track_caller] + fn assert_has_value(&'a self, name: &str) -> walkers::EnumValueWalker<'a> { + self.values() + .find(|v| v.name() == name) + .expect("Enum value {name} not found") + } +} + +pub(crate) trait EnumValueAssert { + fn assert_ignored(&self, ignored: bool) -> &Self; +} + +impl EnumValueAssert for walkers::EnumValueWalker<'_> { + #[track_caller] + fn assert_ignored(&self, ignored: bool) -> &Self { + assert_eq!(self.is_ignored(), ignored); + self + } +} diff --git a/query-engine/dmmf/src/ast_builders/datamodel_ast_builder.rs b/query-engine/dmmf/src/ast_builders/datamodel_ast_builder.rs index f0eb945e76da..f6abe438388d 100644 --- a/query-engine/dmmf/src/ast_builders/datamodel_ast_builder.rs +++ b/query-engine/dmmf/src/ast_builders/datamodel_ast_builder.rs @@ -48,6 +48,9 @@ fn enum_to_dmmf(en: walkers::EnumWalker<'_>) -> Enum { }; for enum_value in en.values() { + if enum_value.is_ignored() { + continue; + } enm.values.push(enum_value_to_dmmf(enum_value)); } diff --git a/schema-engine/datamodel-renderer/src/datamodel/enumerator.rs b/schema-engine/datamodel-renderer/src/datamodel/enumerator.rs index 8a517fc612d8..ea788d184728 100644 --- a/schema-engine/datamodel-renderer/src/datamodel/enumerator.rs +++ b/schema-engine/datamodel-renderer/src/datamodel/enumerator.rs @@ -9,6 +9,7 @@ pub struct EnumVariant<'a> { comment_out: bool, map: Option>, documentation: Option>, + ignore: Option>, } impl<'a> EnumVariant<'a> { @@ -26,6 +27,7 @@ impl<'a> EnumVariant<'a> { comment_out: false, map: None, documentation: None, + ignore: None, } } @@ -55,6 +57,18 @@ impl<'a> EnumVariant<'a> { self.comment_out = true; } + /// Ignores the variant. + /// + /// ```ignore + /// enum Foo { + /// Bar @ignore + /// ^^^^^ this + /// } + /// ``` + pub fn ignore(&mut self) { + self.ignore = Some(FieldAttribute::new(Function::new("ignore"))); + } + /// Documentation of a variant. /// /// ```ignore @@ -91,6 +105,11 @@ impl fmt::Display for EnumVariant<'_> { map.fmt(f)?; } + if let Some(ref ignore) = self.ignore { + f.write_str(" ")?; + ignore.fmt(f)?; + } + Ok(()) } }