diff --git a/Cargo.toml b/Cargo.toml index fedd23c4aa..7e75ab3c25 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -73,6 +73,7 @@ sea-schema = { version = "0.17.0-rc.15", default-features = false, features = [ "writer", "probe", ], optional = true } +semver = { version = "1.0", optional = true } serde = { version = "1.0", default-features = false } serde_json = { version = "1.0", default-features = false, optional = true } sqlx = { version = "0.8.4", default-features = false, optional = true } @@ -115,7 +116,7 @@ default = [ "with-time", "sqlite-use-returning-for-3_35", ] -entity-registry = ["inventory", "sea-orm-macros/entity-registry"] +entity-registry = ["inventory", "semver", "sea-orm-macros/entity-registry"] json-array = [ "postgres-array", ] # this does not actually enable sqlx-postgres, but only a few traits to support array in sea-query diff --git a/sea-orm-macros/src/derives/entity.rs b/sea-orm-macros/src/derives/entity.rs index bf896bf81e..ca75cdf7d8 100644 --- a/sea-orm-macros/src/derives/entity.rs +++ b/sea-orm-macros/src/derives/entity.rs @@ -166,6 +166,7 @@ impl DeriveEntity { sea_orm::register_entity! { sea_orm::EntityRegistry { module_path: module_path!(), + module_version: option_env!("CARGO_PKG_VERSION"), schema_info: |schema| sea_orm::EntitySchemaInfo::new(Entity, schema), } } diff --git a/sea-orm-sync/Cargo.toml b/sea-orm-sync/Cargo.toml index 3b55d72287..7e66e69613 100644 --- a/sea-orm-sync/Cargo.toml +++ b/sea-orm-sync/Cargo.toml @@ -65,6 +65,7 @@ sea-schema-sync = { version = "0.17.0-rc.15", default-features = false, features "writer", "probe", ], optional = true } +semver = { version = "1.0", optional = true } serde = { version = "1.0", default-features = false } serde_json = { version = "1.0", default-features = false, optional = true } strum = { version = "0.27", default-features = false } @@ -104,7 +105,7 @@ default = [ "with-time", "sqlite-use-returning-for-3_35", ] -entity-registry = ["inventory", "sea-orm-macros/entity-registry"] +entity-registry = ["inventory", "semver", "sea-orm-macros/entity-registry"] json-array = [ "postgres-array", ] # this does not actually enable postgres, but only a few traits to support array in sea-query diff --git a/sea-orm-sync/src/database/db_connection.rs b/sea-orm-sync/src/database/db_connection.rs index 6212f758e2..7b30cdc438 100644 --- a/sea-orm-sync/src/database/db_connection.rs +++ b/sea-orm-sync/src/database/db_connection.rs @@ -579,10 +579,18 @@ impl DatabaseConnection { #[cfg(feature = "entity-registry")] #[cfg_attr(docsrs, doc(cfg(feature = "entity-registry")))] - /// Builds a schema for all the entites in the given module + /// Builds a schema for all the entities in the given module pub fn get_schema_registry(&self, prefix: &str) -> SchemaBuilder { let schema = Schema::new(self.get_database_backend()); - crate::EntityRegistry::build_schema(schema, prefix) + crate::EntityRegistry::build_schema(schema, prefix, None) + } + + #[cfg(feature = "entity-registry")] + #[cfg_attr(docsrs, doc(cfg(feature = "entity-registry")))] + /// Builds a schema for all the entities in the given module and crate version + pub fn get_schema_registry_version(&self, prefix: &str, version_spec: &str) -> SchemaBuilder { + let schema = Schema::new(self.get_database_backend()); + crate::EntityRegistry::build_schema(schema, prefix, Some(version_spec)) } /// Sets a callback to metric this connection diff --git a/sea-orm-sync/src/database/executor.rs b/sea-orm-sync/src/database/executor.rs index 26e34a4d50..5c9598227b 100644 --- a/sea-orm-sync/src/database/executor.rs +++ b/sea-orm-sync/src/database/executor.rs @@ -159,6 +159,14 @@ impl DatabaseExecutor<'_> { /// Builds a schema for all the entities in the given module pub fn get_schema_registry(&self, prefix: &str) -> SchemaBuilder { let schema = Schema::new(self.get_database_backend()); - crate::EntityRegistry::build_schema(schema, prefix) + crate::EntityRegistry::build_schema(schema, prefix, None) + } + + #[cfg(feature = "entity-registry")] + #[cfg_attr(docsrs, doc(cfg(feature = "entity-registry")))] + /// Builds a schema for all the entities in the given module and crate version + pub fn get_schema_registry_version(&self, prefix: &str, version_spec: &str) -> SchemaBuilder { + let schema = Schema::new(self.get_database_backend()); + crate::EntityRegistry::build_schema(schema, prefix, Some(version_spec)) } } diff --git a/sea-orm-sync/src/entity/registry.rs b/sea-orm-sync/src/entity/registry.rs index cb5cea93fc..88a823490c 100644 --- a/sea-orm-sync/src/entity/registry.rs +++ b/sea-orm-sync/src/entity/registry.rs @@ -1,4 +1,5 @@ use crate::{EntitySchemaInfo, Schema, SchemaBuilder}; +use semver::{Version, VersionReq}; use tracing::debug; #[derive(derive_more::Debug)] @@ -6,6 +7,8 @@ use tracing::debug; pub struct EntityRegistry { /// Please use `module_path!()`. pub module_path: &'static str, + /// Please use `option_env!("CARGO_PKG_VERSION")` + pub module_version: Option<&'static str>, /// Function that returns schema info for the Entity. #[debug(skip)] pub schema_info: fn(&Schema) -> EntitySchemaInfo, @@ -17,8 +20,8 @@ inventory::collect!(EntityRegistry); pub use inventory::submit as register_entity; impl EntityRegistry { - /// Builds a schema from all the registered entities, filtering by prefix. - pub fn build_schema(schema: Schema, prefix: &str) -> SchemaBuilder { + /// Builds a schema from all the registered entities, filtering by module prefix and crate version. + pub fn build_schema(schema: Schema, prefix: &str, version_spec: Option<&str>) -> SchemaBuilder { let mut schema = SchemaBuilder::new(schema); let mut string; let mut prefix = prefix.trim_end_matches("*"); @@ -34,15 +37,40 @@ impl EntityRegistry { prefix = &string; } } - debug!("Registering entities with prefix `{prefix}`"); + if let Some(spec) = version_spec { + debug!("Registering entities with prefix `{prefix}` and version `{spec}`"); + } else { + debug!("Registering entities with prefix `{prefix}`"); + } for entity in inventory::iter::() { - if entity.module_path.starts_with(prefix) { + if entity.module_path.starts_with(prefix) + && version_matches(entity.module_version, version_spec) + { schema.register_entity((entity.schema_info)(schema.helper())); - debug!("Registered {}", entity.module_path); + if let Some(version) = entity.module_version { + debug!("Registered {} ({})", entity.module_path, version); + } else { + debug!("Registered {}", entity.module_path); + } } else { - debug!("Skipped {}", entity.module_path); + if let Some(version) = entity.module_version { + debug!("Skipped {} ({})", entity.module_path, version); + } else { + debug!("Skipped {}", entity.module_path); + } } } schema } } + +fn version_matches(version: Option<&str>, version_spec: Option<&str>) -> bool { + match (version, version_spec) { + (Some(version), Some(version_spec)) => VersionReq::parse(version_spec) + .unwrap() + .matches(&Version::parse(version).unwrap()), + + // Either module version or version spec not given + _ => true, + } +} diff --git a/src/database/db_connection.rs b/src/database/db_connection.rs index 108111ad16..c100ba4d4a 100644 --- a/src/database/db_connection.rs +++ b/src/database/db_connection.rs @@ -622,10 +622,18 @@ impl DatabaseConnection { #[cfg(feature = "entity-registry")] #[cfg_attr(docsrs, doc(cfg(feature = "entity-registry")))] - /// Builds a schema for all the entites in the given module + /// Builds a schema for all the entities in the given module pub fn get_schema_registry(&self, prefix: &str) -> SchemaBuilder { let schema = Schema::new(self.get_database_backend()); - crate::EntityRegistry::build_schema(schema, prefix) + crate::EntityRegistry::build_schema(schema, prefix, None) + } + + #[cfg(feature = "entity-registry")] + #[cfg_attr(docsrs, doc(cfg(feature = "entity-registry")))] + /// Builds a schema for all the entities in the given module and crate version + pub fn get_schema_registry_version(&self, prefix: &str, version_spec: &str) -> SchemaBuilder { + let schema = Schema::new(self.get_database_backend()); + crate::EntityRegistry::build_schema(schema, prefix, Some(version_spec)) } /// Sets a callback to metric this connection diff --git a/src/database/executor.rs b/src/database/executor.rs index ec9135c016..0ead357c84 100644 --- a/src/database/executor.rs +++ b/src/database/executor.rs @@ -172,6 +172,14 @@ impl DatabaseExecutor<'_> { /// Builds a schema for all the entities in the given module pub fn get_schema_registry(&self, prefix: &str) -> SchemaBuilder { let schema = Schema::new(self.get_database_backend()); - crate::EntityRegistry::build_schema(schema, prefix) + crate::EntityRegistry::build_schema(schema, prefix, None) + } + + #[cfg(feature = "entity-registry")] + #[cfg_attr(docsrs, doc(cfg(feature = "entity-registry")))] + /// Builds a schema for all the entities in the given module and crate version + pub fn get_schema_registry_version(&self, prefix: &str, version_spec: &str) -> SchemaBuilder { + let schema = Schema::new(self.get_database_backend()); + crate::EntityRegistry::build_schema(schema, prefix, Some(version_spec)) } } diff --git a/src/entity/registry.rs b/src/entity/registry.rs index cb5cea93fc..88a823490c 100644 --- a/src/entity/registry.rs +++ b/src/entity/registry.rs @@ -1,4 +1,5 @@ use crate::{EntitySchemaInfo, Schema, SchemaBuilder}; +use semver::{Version, VersionReq}; use tracing::debug; #[derive(derive_more::Debug)] @@ -6,6 +7,8 @@ use tracing::debug; pub struct EntityRegistry { /// Please use `module_path!()`. pub module_path: &'static str, + /// Please use `option_env!("CARGO_PKG_VERSION")` + pub module_version: Option<&'static str>, /// Function that returns schema info for the Entity. #[debug(skip)] pub schema_info: fn(&Schema) -> EntitySchemaInfo, @@ -17,8 +20,8 @@ inventory::collect!(EntityRegistry); pub use inventory::submit as register_entity; impl EntityRegistry { - /// Builds a schema from all the registered entities, filtering by prefix. - pub fn build_schema(schema: Schema, prefix: &str) -> SchemaBuilder { + /// Builds a schema from all the registered entities, filtering by module prefix and crate version. + pub fn build_schema(schema: Schema, prefix: &str, version_spec: Option<&str>) -> SchemaBuilder { let mut schema = SchemaBuilder::new(schema); let mut string; let mut prefix = prefix.trim_end_matches("*"); @@ -34,15 +37,40 @@ impl EntityRegistry { prefix = &string; } } - debug!("Registering entities with prefix `{prefix}`"); + if let Some(spec) = version_spec { + debug!("Registering entities with prefix `{prefix}` and version `{spec}`"); + } else { + debug!("Registering entities with prefix `{prefix}`"); + } for entity in inventory::iter::() { - if entity.module_path.starts_with(prefix) { + if entity.module_path.starts_with(prefix) + && version_matches(entity.module_version, version_spec) + { schema.register_entity((entity.schema_info)(schema.helper())); - debug!("Registered {}", entity.module_path); + if let Some(version) = entity.module_version { + debug!("Registered {} ({})", entity.module_path, version); + } else { + debug!("Registered {}", entity.module_path); + } } else { - debug!("Skipped {}", entity.module_path); + if let Some(version) = entity.module_version { + debug!("Skipped {} ({})", entity.module_path, version); + } else { + debug!("Skipped {}", entity.module_path); + } } } schema } } + +fn version_matches(version: Option<&str>, version_spec: Option<&str>) -> bool { + match (version, version_spec) { + (Some(version), Some(version_spec)) => VersionReq::parse(version_spec) + .unwrap() + .matches(&Version::parse(version).unwrap()), + + // Either module version or version spec not given + _ => true, + } +}