You've already forked seamantic
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
e066509031
|
|||
|
0a585c09e5
|
|||
|
5d9f14d8b3
|
|||
|
3bfebe424a
|
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"vadimcn.vscode-lldb",
|
||||
"barbosshack.crates-io",
|
||||
"usernamehw.errorlens",
|
||||
"tamasfe.even-better-toml",
|
||||
"rust-lang.rust-analyzer",
|
||||
]
|
||||
}
|
||||
Vendored
-33
@@ -1,33 +0,0 @@
|
||||
{
|
||||
// VSCode
|
||||
"editor.detectIndentation": false,
|
||||
"editor.insertSpaces": false,
|
||||
"editor.tabSize": 4,
|
||||
"files.exclude": {
|
||||
"**/target": true,
|
||||
"**/Cargo.lock": true,
|
||||
},
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimFinalNewlines": true,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"files.watcherExclude": {
|
||||
"**/.git/**": true,
|
||||
"**/target/**": true,
|
||||
},
|
||||
// Extensions
|
||||
"crates.listPreReleases": true,
|
||||
"evenBetterToml.formatter.alignComments": true,
|
||||
"evenBetterToml.formatter.alignEntries": false,
|
||||
"evenBetterToml.formatter.allowedBlankLines": 1,
|
||||
"evenBetterToml.formatter.arrayAutoExpand": true,
|
||||
"evenBetterToml.formatter.arrayTrailingComma": true,
|
||||
"evenBetterToml.formatter.columnWidth": 80,
|
||||
"evenBetterToml.formatter.reorderKeys": true,
|
||||
"evenBetterToml.formatter.trailingNewline": true,
|
||||
"rust-analyzer.imports.granularity.enforce": true,
|
||||
"rust-analyzer.imports.granularity.group": "module",
|
||||
"rust-analyzer.imports.group.enable": true,
|
||||
"rust-analyzer.imports.merge.glob": false,
|
||||
"rust-analyzer.imports.preferNoStd": true,
|
||||
"rust-analyzer.showUnlinkedFileNotification": false,
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{
|
||||
"project_name": null,
|
||||
|
||||
"auto_install_extensions": {
|
||||
"tombi": true,
|
||||
"cargo-appraiser": true,
|
||||
},
|
||||
|
||||
"languages": {
|
||||
"TOML": {
|
||||
"format_on_save": "on",
|
||||
"formatter": { "language_server": { "name": "tombi" } },
|
||||
},
|
||||
},
|
||||
|
||||
"lsp": {
|
||||
"rust-analyzer": {
|
||||
"initialization_options": {
|
||||
"imports": {
|
||||
"granularity": { "enforce": true, "group": "module" },
|
||||
"group": { "enable": true },
|
||||
"merge": { "glob": false },
|
||||
"preferNoStd": true,
|
||||
},
|
||||
"server": {
|
||||
"extraEnv": {
|
||||
"RUSTUP_TOOLCHAIN": "stable",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Generated
+1537
-294
File diff suppressed because it is too large
Load Diff
+23
-21
@@ -1,18 +1,22 @@
|
||||
[workspace]
|
||||
members = ["seamantic"]
|
||||
resolver = "2"
|
||||
members = ["seamantic"]
|
||||
|
||||
[workspace.package]
|
||||
authors = []
|
||||
edition = "2024"
|
||||
license-file = "LICENSE.md"
|
||||
rust-version = "1.85.0"
|
||||
|
||||
[workspace.lints.rust]
|
||||
arithmetic_overflow = "forbid"
|
||||
missing_docs = "forbid"
|
||||
unsafe_code = "forbid"
|
||||
unused_doc_comments = "forbid"
|
||||
edition = "2024"
|
||||
rust-version = "1.88.0"
|
||||
|
||||
[workspace.dependencies]
|
||||
seamantic = { path = "seamantic", version = "=0.0.13", default-features = false }
|
||||
|
||||
sea-orm = { version = "=2.0.0-rc.38", default-features = false }
|
||||
sea-orm-migration = { version = "=2.0.0-rc.38", default-features = false }
|
||||
|
||||
serde = { version = "^1", default-features = false }
|
||||
serde_test = { version = "^1", default-features = false }
|
||||
tokio = { version = "^1", default-features = false }
|
||||
|
||||
[workspace.lints.clippy]
|
||||
arithmetic_side_effects = "forbid"
|
||||
@@ -24,20 +28,18 @@ indexing_slicing = "forbid"
|
||||
integer_division = "forbid"
|
||||
integer_division_remainder_used = "forbid"
|
||||
transmute_undefined_repr = "forbid"
|
||||
unchecked_duration_subtraction = "forbid"
|
||||
unchecked_time_subtraction = "forbid"
|
||||
unwrap_used = "forbid"
|
||||
|
||||
[workspace.lints.rust]
|
||||
arithmetic_overflow = "forbid"
|
||||
missing_docs = "forbid"
|
||||
unsafe_code = "forbid"
|
||||
unused_doc_comments = "forbid"
|
||||
|
||||
[profile.release]
|
||||
codegen-units = 1
|
||||
lto = "fat"
|
||||
opt-level = 3
|
||||
overflow-checks = true
|
||||
strip = "debuginfo"
|
||||
|
||||
[workspace.dependencies]
|
||||
sea-orm = { version = "2.0.0-rc.16", default-features = false }
|
||||
sea-orm-migration = { version = "2.0.0-rc.16", default-features = false }
|
||||
|
||||
serde = { version = "^1", default-features = false }
|
||||
serde_test = { version = "^1", default-features = false }
|
||||
tokio = { version = "^1", default-features = false }
|
||||
overflow-checks = true
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
|
||||
@@ -9,7 +9,7 @@ A library to enhance SeaORM
|
||||
- build: `cargo hack --feature-powerset build`
|
||||
- clippy: `cargo hack --feature-powerset clippy -- -D warnings`
|
||||
- test: `cargo hack --feature-powerset test`
|
||||
- test old: `cargo +1.85 hack --feature-powerset test`
|
||||
- test old: `cargo +1.88 hack --feature-powerset test`
|
||||
- example: `cargo run --example=migrations --features=sqlite`
|
||||
- fmt: `cargo fmt --check`
|
||||
- docs: `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features`
|
||||
|
||||
+16
-17
@@ -1,40 +1,39 @@
|
||||
[package]
|
||||
name = "seamantic"
|
||||
version = "0.0.9"
|
||||
version = "0.0.13"
|
||||
license-file.workspace = true
|
||||
|
||||
categories = []
|
||||
description = "A library to enhance SeaORM"
|
||||
repository = "https://github.com/QuantumShade/seamantic"
|
||||
categories = []
|
||||
|
||||
authors.workspace = true
|
||||
edition.workspace = true
|
||||
license-file.workspace = true
|
||||
rust-version.workspace = true
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[[example]]
|
||||
name = "migrations"
|
||||
path = "examples/migrations/main.rs"
|
||||
required-features = ["sqlite"]
|
||||
|
||||
[dependencies]
|
||||
sea-orm = { workspace = true }
|
||||
sea-orm-migration = { workspace = true }
|
||||
serde = { workspace = true, features = ["derive", "std"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
sea-orm = { workspace = true, features = ["entity-registry", "schema-sync"] }
|
||||
sea-orm-migration = { workspace = true, features = ["runtime-tokio-rustls"] }
|
||||
serde_test = { workspace = true }
|
||||
tokio = { workspace = true, features = ["macros", "rt"] }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
serde = ["dep:serde"]
|
||||
sqlite = ["sea-orm-migration/sqlx-sqlite"]
|
||||
|
||||
[dependencies]
|
||||
sea-orm = { workspace = true }
|
||||
sea-orm-migration = { workspace = true }
|
||||
|
||||
serde = { workspace = true, features = ["derive", "std"], optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
sea-orm-migration = { workspace = true, features = ["runtime-tokio-rustls"] }
|
||||
serde_test = { workspace = true }
|
||||
tokio = { workspace = true, features = ["rt", "macros"] }
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -13,7 +13,7 @@ type DurationRepr = u64;
|
||||
///
|
||||
/// ### Warning:
|
||||
/// Sub-second precision will be lost
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[repr(transparent)]
|
||||
|
||||
@@ -12,6 +12,8 @@ use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, T
|
||||
pub type SeaOrmRepr = i64;
|
||||
|
||||
/// An opaque type representing a row ID
|
||||
///
|
||||
/// IDs should be tagged with `#[sea_orm(primary_key, auto_increment = false)]`
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||
#[repr(transparent)]
|
||||
|
||||
+198
-11
@@ -11,34 +11,221 @@ use sea_orm_migration::sea_query::{ColumnDef, IntoIden};
|
||||
/// and should be tagged with:
|
||||
///
|
||||
/// `#[sea_orm(primary_key, auto_increment = false)]`
|
||||
///
|
||||
/// When using the new entity format:
|
||||
///
|
||||
/// use sea_orm::entity::prelude::*;
|
||||
/// use seamantic::model::id::Id;
|
||||
///
|
||||
/// #[sea_orm::model]
|
||||
/// #[derive(Debug, Clone, DeriveEntityModel)]
|
||||
/// #[sea_orm(table_name = "rowid_test")]
|
||||
/// pub struct Model {
|
||||
/// #[sea_orm(primary_key, auto_increment = false)]
|
||||
/// id: Id<Model>,
|
||||
/// }
|
||||
/// impl ActiveModelBehavior for ActiveModel {}
|
||||
pub fn sqlite_rowid_alias<T: IntoIden>(name: T) -> ColumnDef {
|
||||
integer_null(name).primary_key().take()
|
||||
}
|
||||
|
||||
/// Set the column to be a case insensitive string
|
||||
///
|
||||
/// When using the new entity format:
|
||||
///
|
||||
/// use sea_orm::entity::prelude::*;
|
||||
/// use seamantic::model::id::Id;
|
||||
///
|
||||
/// #[sea_orm::model]
|
||||
/// #[derive(Debug, Clone, DeriveEntityModel)]
|
||||
/// #[sea_orm(table_name = "nocase_test")]
|
||||
/// pub struct Model {
|
||||
/// #[sea_orm(primary_key, auto_increment = false)]
|
||||
/// id: i64,
|
||||
/// #[sea_orm(extra = "COLLATE NOCASE")]
|
||||
/// nocase: String,
|
||||
/// }
|
||||
/// impl ActiveModelBehavior for ActiveModel {}
|
||||
pub fn sqlite_case_insensitive_string<T: IntoIden>(name: T) -> ColumnDef {
|
||||
string(name).extra("COLLATE NOCASE").take()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod entity_tests {
|
||||
use sea_orm::{ConnectOptions, Database, DatabaseConnection};
|
||||
|
||||
pub async fn new_initialized_memory_db() -> DatabaseConnection {
|
||||
let options = ConnectOptions::new("sqlite::memory:");
|
||||
|
||||
let db = Database::connect(options)
|
||||
.await
|
||||
.expect("Database::connect()");
|
||||
db.get_schema_registry("seamantic::schema::sqlite::*")
|
||||
.sync(&db)
|
||||
.await
|
||||
.expect("db.get_schema_registry().sync()");
|
||||
db
|
||||
}
|
||||
|
||||
mod rowid_test {
|
||||
use sea_orm::ActiveValue::{NotSet, Set};
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
use crate::model::id::{Id, SeaOrmRepr};
|
||||
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "rowid_test")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
id: Id<Model>,
|
||||
}
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sqlite_rowid_alias() {
|
||||
let db = super::new_initialized_memory_db().await;
|
||||
|
||||
// Starts at 1 and increments
|
||||
for i in 1..=3 {
|
||||
let model = ActiveModel { id: NotSet };
|
||||
let model = model.insert(&db).await.expect("insert");
|
||||
assert_eq!(model.id.into_raw(), i);
|
||||
}
|
||||
|
||||
// Delete the top number and re-add
|
||||
for i in 3..=3 {
|
||||
let model = ActiveModel {
|
||||
id: Set(Id::from_raw(i)),
|
||||
};
|
||||
model.delete(&db).await.expect("delete");
|
||||
let model = ActiveModel { id: NotSet };
|
||||
let model = model.insert(&db).await.expect("insert");
|
||||
assert_eq!(model.id.into_raw(), i);
|
||||
}
|
||||
|
||||
// Jump to 100 and increment
|
||||
for i in 100..=103 {
|
||||
let model = ActiveModel {
|
||||
id: Set(Id::from_raw(i)),
|
||||
};
|
||||
let model = model.insert(&db).await.expect("insert");
|
||||
assert_eq!(model.id.into_raw(), i);
|
||||
}
|
||||
|
||||
// Continue to increment
|
||||
for i in 104..=104 {
|
||||
let model = ActiveModel { id: NotSet };
|
||||
let model = model.insert(&db).await.expect("insert");
|
||||
assert_eq!(model.id.into_raw(), i);
|
||||
}
|
||||
|
||||
// Jump to SeaOrmRepr::MAX and increment
|
||||
for i in SeaOrmRepr::MAX..=SeaOrmRepr::MAX {
|
||||
let model = ActiveModel {
|
||||
id: Set(Id::from_raw(i)),
|
||||
};
|
||||
let model = model.insert(&db).await.expect("insert");
|
||||
assert_eq!(model.id.into_raw(), i);
|
||||
}
|
||||
|
||||
// Next ones are random around the center
|
||||
for _ in 0..3 {
|
||||
let model = ActiveModel { id: NotSet };
|
||||
let model = model.insert(&db).await.expect("insert");
|
||||
assert!(model.id.into_raw() > 0);
|
||||
}
|
||||
|
||||
// Zero ID is valid
|
||||
for i in 0..=0 {
|
||||
let model = ActiveModel {
|
||||
id: Set(Id::from_raw(i)),
|
||||
};
|
||||
let model = model.insert(&db).await.expect("insert");
|
||||
assert_eq!(model.id.into_raw(), i);
|
||||
}
|
||||
|
||||
// Negative ID is valid
|
||||
for i in SeaOrmRepr::MIN..=(SeaOrmRepr::MIN + 3) {
|
||||
let model = ActiveModel {
|
||||
id: Set(Id::from_raw(i)),
|
||||
};
|
||||
let model = model.insert(&db).await.expect("insert");
|
||||
assert_eq!(model.id.into_raw(), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod nocase_test {
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use sea_orm::entity::prelude::*;
|
||||
|
||||
#[sea_orm::model]
|
||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "nocase_test")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
id: i64,
|
||||
case: String,
|
||||
#[sea_orm(extra = "COLLATE NOCASE")]
|
||||
nocase: String,
|
||||
}
|
||||
impl ActiveModelBehavior for ActiveModel {}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_sqlite_case_insensitive_string() {
|
||||
let db = super::new_initialized_memory_db().await;
|
||||
|
||||
let i = 50;
|
||||
|
||||
// Insert a lowercase string
|
||||
{
|
||||
let model = ActiveModel {
|
||||
id: Set(i),
|
||||
case: Set("abcd".to_owned()),
|
||||
nocase: Set("abcd".to_owned()),
|
||||
};
|
||||
model.insert(&db).await.expect("insert");
|
||||
}
|
||||
|
||||
// Query by uppercase string
|
||||
{
|
||||
Entity::find()
|
||||
.filter(Column::Case.eq("ABCD"))
|
||||
.one(&db)
|
||||
.await
|
||||
.expect("find by case insensitive string")
|
||||
.ok_or(())
|
||||
.expect_err("find by case insensitive string");
|
||||
|
||||
let model = Entity::find()
|
||||
.filter(Column::Nocase.eq("ABCD"))
|
||||
.one(&db)
|
||||
.await
|
||||
.expect("find by case insensitive string")
|
||||
.expect("find by case insensitive string");
|
||||
assert_eq!(model.id, i);
|
||||
// The string should be read back as-is
|
||||
assert_eq!(model.nocase, "abcd");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use sea_orm::ActiveValue::{NotSet, Set};
|
||||
use sea_orm::entity::prelude::*;
|
||||
use sea_orm::{ConnectOptions, Database, DatabaseConnection};
|
||||
use sea_orm_migration::async_trait::async_trait;
|
||||
use sea_orm_migration::sea_orm::QueryFilter;
|
||||
use sea_orm_migration::sea_orm::{
|
||||
ActiveModelBehavior, ActiveModelTrait, ActiveValue::NotSet, ActiveValue::Set, ColumnTrait,
|
||||
ConnectOptions, Database, DatabaseConnection, DbErr, DeriveEntityModel,
|
||||
DeriveMigrationName, DerivePrimaryKey, EntityTrait, EnumIter, PrimaryKeyTrait, RelationDef,
|
||||
RelationTrait,
|
||||
};
|
||||
use sea_orm_migration::sea_query::{self, Iden, Table};
|
||||
use sea_orm_migration::{MigrationTrait, MigratorTrait, SchemaManager};
|
||||
use sea_orm_migration::prelude::*;
|
||||
|
||||
use crate::model::id::{Id, SeaOrmRepr};
|
||||
|
||||
use super::{sqlite_case_insensitive_string, sqlite_rowid_alias};
|
||||
|
||||
async fn new_memory_db() -> DatabaseConnection {
|
||||
let options = ConnectOptions::new("sqlite:/tmp/db?mode=memory");
|
||||
let options = ConnectOptions::new("sqlite::memory:");
|
||||
Database::connect(options).await.expect("Database::connect")
|
||||
}
|
||||
|
||||
@@ -228,7 +415,7 @@ mod tests {
|
||||
// Query by uppercase string
|
||||
{
|
||||
let model = Entity::find()
|
||||
.filter(Column::CiStr.contains("ABCD"))
|
||||
.filter(Column::CiStr.eq("ABCD"))
|
||||
.one(&db)
|
||||
.await
|
||||
.expect("find by case insensitive string")
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
toml-version = "v1.0.0"
|
||||
|
||||
[format.rules]
|
||||
indent-style = "tab"
|
||||
indent-width = 4
|
||||
Reference in New Issue
Block a user