You've already forked seamantic
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
a5454bda71
|
|||
|
800916c111
|
|||
|
b8f25b635d
|
|||
|
e066509031
|
@@ -0,0 +1,4 @@
|
|||||||
|
allow-expect-in-tests = true
|
||||||
|
check-private-items = true
|
||||||
|
|
||||||
|
allowed-duplicate-crates = []
|
||||||
+22
-6
@@ -1,7 +1,23 @@
|
|||||||
# OS Files
|
*
|
||||||
*~
|
!**/
|
||||||
._*
|
|
||||||
.DS_Store
|
|
||||||
|
|
||||||
# Rust
|
!.zed/settings.json
|
||||||
/target
|
!.gitignore
|
||||||
|
|
||||||
|
!/CONTRIBUTING.md
|
||||||
|
!/LICENSE.md
|
||||||
|
!/README.md
|
||||||
|
|
||||||
|
!/.clippy.toml
|
||||||
|
!/.rustfmt.toml
|
||||||
|
!/.tombi.toml
|
||||||
|
!/Cargo.toml
|
||||||
|
!/Cargo.lock
|
||||||
|
!/justfile
|
||||||
|
!/rust-toolchain.toml
|
||||||
|
|
||||||
|
!/crates/**/*.rs
|
||||||
|
!/crates/**/Cargo.toml
|
||||||
|
!/crates/**/README.md
|
||||||
|
|
||||||
|
!/lints/*.toml
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
hard_tabs = true
|
hard_tabs = true
|
||||||
wrap_comments = true
|
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
[format.rules]
|
||||||
|
indent-style = "tab"
|
||||||
|
indent-width = 4
|
||||||
|
line-width = 120
|
||||||
|
|
||||||
|
# Disable TOML 1.1 features from tombi as long as we support rust <1.94
|
||||||
|
[[schemas]]
|
||||||
|
toml-version = "v1.0.0"
|
||||||
|
path = "tombi://www.schemastore.org/cargo.json"
|
||||||
|
include = ["Cargo.toml"]
|
||||||
+7
-14
@@ -1,19 +1,12 @@
|
|||||||
{
|
{
|
||||||
"project_name": null,
|
|
||||||
|
|
||||||
"auto_install_extensions": {
|
|
||||||
"tombi": true,
|
|
||||||
"cargo-appraiser": true,
|
|
||||||
},
|
|
||||||
|
|
||||||
"languages": {
|
|
||||||
"TOML": {
|
|
||||||
"format_on_save": "on",
|
|
||||||
"formatter": { "language_server": { "name": "tombi" } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"lsp": {
|
"lsp": {
|
||||||
|
"just-lsp": {
|
||||||
|
"initialization_options": {
|
||||||
|
"formatting": {
|
||||||
|
"indentation": "\t",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"rust-analyzer": {
|
"rust-analyzer": {
|
||||||
"initialization_options": {
|
"initialization_options": {
|
||||||
"imports": {
|
"imports": {
|
||||||
|
|||||||
+2
-1
@@ -3,4 +3,5 @@ How to Contribute
|
|||||||
|
|
||||||
We'd love to accept your patches and contributions to this project.
|
We'd love to accept your patches and contributions to this project.
|
||||||
We just need you to follow the Contributor License Agreement outlined
|
We just need you to follow the Contributor License Agreement outlined
|
||||||
in the latest v0.0.x of https://github.com/Skrunix/license
|
in the latest v0.0.x of https://git.skrundz.dev/skrunix/license
|
||||||
|
(mirrored to https://github.com/skrunix/license)
|
||||||
|
|||||||
Generated
+889
-877
File diff suppressed because it is too large
Load Diff
+523
-24
@@ -1,38 +1,537 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
resolver = "3"
|
||||||
members = ["seamantic"]
|
members = ["crates/*"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
rust-version = "1.85.0"
|
rust-version = "1.94"
|
||||||
license-file = "LICENSE.md"
|
license-file = "LICENSE.md"
|
||||||
|
publish = false
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
sea-orm = { version = "2.0.0-rc.27", default-features = false }
|
sea-orm = { version = "^2.0.0", default-features = false }
|
||||||
sea-orm-migration = { version = "2.0.0-rc.27", default-features = false }
|
sea-orm-migration = { version = "^2.0.0", default-features = false }
|
||||||
seamantic = { path = "seamantic", version = "=0.0.12", default-features = false }
|
serde = { version = "^1.0.220", default-features = false }
|
||||||
serde = { version = "^1", default-features = false }
|
serde_test = { version = "^1.0.0", default-features = false }
|
||||||
serde_test = { version = "^1", default-features = false }
|
tokio = { version = "^1.38.2", default-features = false }
|
||||||
tokio = { version = "^1", default-features = false }
|
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
[workspace.lints.clippy]
|
||||||
arithmetic_side_effects = "forbid"
|
cargo = { level = "warn", priority = -1 }
|
||||||
as_conversions = "forbid"
|
complexity = { level = "warn", priority = -1 }
|
||||||
checked_conversions = "forbid"
|
correctness = { level = "deny", priority = -1 }
|
||||||
default_union_representation = "forbid"
|
perf = { level = "warn", priority = -1 }
|
||||||
expect_used = "forbid"
|
style = { level = "warn", priority = -1 }
|
||||||
indexing_slicing = "forbid"
|
suspicious = { level = "warn", priority = -1 }
|
||||||
integer_division = "forbid"
|
|
||||||
integer_division_remainder_used = "forbid"
|
absolute-paths = "allow"
|
||||||
transmute_undefined_repr = "forbid"
|
alloc-instead-of-core = "warn"
|
||||||
unchecked_time_subtraction = "forbid"
|
allow-attributes = "warn"
|
||||||
unwrap_used = "forbid"
|
allow-attributes-without-reason = "deny"
|
||||||
|
arbitrary-source-item-ordering = "allow"
|
||||||
|
arithmetic-side-effects = "deny"
|
||||||
|
as-conversions = "deny"
|
||||||
|
as-pointer-underscore = "deny"
|
||||||
|
as-ptr-cast-mut = "deny"
|
||||||
|
as-underscore = "deny"
|
||||||
|
assertions-on-result-states = "warn"
|
||||||
|
assigning-clones = "warn"
|
||||||
|
big-endian-bytes = "allow"
|
||||||
|
bool-to-int-with-if = "warn"
|
||||||
|
borrow-as-ptr = "warn"
|
||||||
|
branches-sharing-code = "warn"
|
||||||
|
case-sensitive-file-extension-comparisons = "warn"
|
||||||
|
cast-lossless = "deny"
|
||||||
|
cast-possible-truncation = "deny"
|
||||||
|
cast-possible-wrap = "deny"
|
||||||
|
cast-precision-loss = "deny"
|
||||||
|
cast-ptr-alignment = "deny"
|
||||||
|
cast-sign-loss = "deny"
|
||||||
|
cfg-not-test = "deny"
|
||||||
|
checked-conversions = "deny"
|
||||||
|
clear-with-drain = "warn"
|
||||||
|
clone-on-ref-ptr = "warn"
|
||||||
|
cloned-instead-of-copied = "warn"
|
||||||
|
coerce-container-to-any = "warn"
|
||||||
|
cognitive-complexity = "allow"
|
||||||
|
collapsible-else-if = "warn"
|
||||||
|
collection-is-never-read = "warn"
|
||||||
|
comparison-chain = "warn"
|
||||||
|
copy-iterator = "warn"
|
||||||
|
create-dir = "warn"
|
||||||
|
dbg-macro = "warn"
|
||||||
|
debug-assert-with-mut-call = "deny"
|
||||||
|
decimal-bitwise-operands = "warn"
|
||||||
|
decimal-literal-representation = "warn"
|
||||||
|
default-numeric-fallback = "warn"
|
||||||
|
default-trait-access = "warn"
|
||||||
|
default-union-representation = "allow"
|
||||||
|
deref-by-slicing = "warn"
|
||||||
|
derive-partial-eq-without-eq = "warn"
|
||||||
|
disallowed-script-idents = "deny"
|
||||||
|
doc-broken-link = "warn"
|
||||||
|
doc-comment-double-space-linebreaks = "warn"
|
||||||
|
doc-include-without-cfg = "warn"
|
||||||
|
doc-link-code = "warn"
|
||||||
|
doc-link-with-quotes = "warn"
|
||||||
|
doc-markdown = "warn"
|
||||||
|
doc-paragraphs-missing-punctuation = "warn"
|
||||||
|
duration-suboptimal-units = "warn"
|
||||||
|
elidable-lifetime-names = "warn"
|
||||||
|
else-if-without-else = "warn"
|
||||||
|
empty-drop = "warn"
|
||||||
|
empty-enum-variants-with-brackets = "warn"
|
||||||
|
empty-enums = "warn"
|
||||||
|
empty-structs-with-brackets = "warn"
|
||||||
|
enum-glob-use = "warn"
|
||||||
|
equatable-if-let = "warn"
|
||||||
|
error-impl-error = "warn"
|
||||||
|
exhaustive-enums = "warn"
|
||||||
|
exhaustive-structs = "warn"
|
||||||
|
exit = "warn"
|
||||||
|
expect-used = "warn"
|
||||||
|
expl-impl-clone-on-copy = "warn"
|
||||||
|
explicit-deref-methods = "warn"
|
||||||
|
explicit-into-iter-loop = "warn"
|
||||||
|
explicit-iter-loop = "warn"
|
||||||
|
fallible-impl-from = "warn"
|
||||||
|
field-scoped-visibility-modifiers = "warn"
|
||||||
|
filetype-is-file = "warn"
|
||||||
|
filter-map-next = "warn"
|
||||||
|
flat-map-option = "warn"
|
||||||
|
float-arithmetic = "warn"
|
||||||
|
float-cmp = "warn"
|
||||||
|
float-cmp-const = "warn"
|
||||||
|
fn-params-excessive-bools = "allow"
|
||||||
|
fn-to-numeric-cast-any = "warn"
|
||||||
|
format-collect = "warn"
|
||||||
|
format-push-string = "warn"
|
||||||
|
future-not-send = "warn"
|
||||||
|
get-unwrap = "warn"
|
||||||
|
host-endian-bytes = "allow"
|
||||||
|
if-not-else = "warn"
|
||||||
|
if-then-some-else-none = "warn"
|
||||||
|
ignore-without-reason = "warn"
|
||||||
|
ignored-unit-patterns = "warn"
|
||||||
|
impl-trait-in-params = "warn"
|
||||||
|
implicit-clone = "warn"
|
||||||
|
implicit-hasher = "warn"
|
||||||
|
implicit-return = "allow"
|
||||||
|
imprecise-flops = "warn"
|
||||||
|
inconsistent-struct-constructor = "warn"
|
||||||
|
index-refutable-slice = "deny"
|
||||||
|
indexing-slicing = "deny"
|
||||||
|
inefficient-to-string = "warn"
|
||||||
|
infinite-loop = "warn"
|
||||||
|
inline-always = "warn"
|
||||||
|
inline-asm-x86-att-syntax = "warn"
|
||||||
|
inline-asm-x86-intel-syntax = "warn"
|
||||||
|
inline-modules = "allow"
|
||||||
|
inline-trait-bounds = "allow"
|
||||||
|
integer-division = "deny"
|
||||||
|
integer-division-remainder-used = "deny"
|
||||||
|
into-iter-without-iter = "warn"
|
||||||
|
invalid-upcast-comparisons = "warn"
|
||||||
|
ip-constant = "warn"
|
||||||
|
items-after-statements = "warn"
|
||||||
|
iter-filter-is-ok = "warn"
|
||||||
|
iter-filter-is-some = "warn"
|
||||||
|
iter-not-returning-iterator = "warn"
|
||||||
|
iter-on-empty-collections = "warn"
|
||||||
|
iter-on-single-items = "warn"
|
||||||
|
iter-over-hash-type = "warn"
|
||||||
|
iter-with-drain = "warn"
|
||||||
|
iter-without-into-iter = "warn"
|
||||||
|
large-digit-groups = "warn"
|
||||||
|
large-futures = "warn"
|
||||||
|
large-include-file = "warn"
|
||||||
|
large-stack-arrays = "warn"
|
||||||
|
large-stack-frames = "warn"
|
||||||
|
large-types-passed-by-value = "warn"
|
||||||
|
let-underscore-must-use = "warn"
|
||||||
|
let-underscore-untyped = "warn"
|
||||||
|
linkedlist = "warn"
|
||||||
|
literal-string-with-formatting-args = "warn"
|
||||||
|
little-endian-bytes = "allow"
|
||||||
|
lossy-float-literal = "warn"
|
||||||
|
macro-use-imports = "warn"
|
||||||
|
manual-assert = "warn"
|
||||||
|
manual-assert-eq = "warn"
|
||||||
|
manual-ilog2 = "warn"
|
||||||
|
manual-instant-elapsed = "warn"
|
||||||
|
manual-is-power-of-two = "warn"
|
||||||
|
manual-is-variant-and = "warn"
|
||||||
|
manual-let-else = "warn"
|
||||||
|
manual-midpoint = "warn"
|
||||||
|
manual-string-new = "warn"
|
||||||
|
many-single-char-names = "warn"
|
||||||
|
map-err-ignore = "warn"
|
||||||
|
map-unwrap-or = "warn"
|
||||||
|
map-with-unused-argument-over-ranges = "warn"
|
||||||
|
match-bool = "warn"
|
||||||
|
match-same-arms = "warn"
|
||||||
|
match-wild-err-arm = "warn"
|
||||||
|
match-wildcard-for-single-variants = "warn"
|
||||||
|
maybe-infinite-iter = "warn"
|
||||||
|
mem-forget = "warn"
|
||||||
|
min-ident-chars = "allow"
|
||||||
|
mismatching-type-param-order = "warn"
|
||||||
|
missing-assert-message = "warn"
|
||||||
|
missing-asserts-for-indexing = "warn"
|
||||||
|
missing-const-for-fn = "warn"
|
||||||
|
missing-docs-in-private-items = "warn"
|
||||||
|
missing-errors-doc = "warn"
|
||||||
|
missing-fields-in-debug = "warn"
|
||||||
|
missing-inline-in-public-items = "warn"
|
||||||
|
missing-panics-doc = "warn"
|
||||||
|
missing-trait-methods = "warn"
|
||||||
|
mixed-read-write-in-expression = "warn"
|
||||||
|
mod-module-files = "allow"
|
||||||
|
module-name-repetitions = "warn"
|
||||||
|
modulo-arithmetic = "warn"
|
||||||
|
multiple-crate-versions = "allow"
|
||||||
|
multiple-inherent-impl = "warn"
|
||||||
|
multiple-unsafe-ops-per-block = "warn"
|
||||||
|
must-use-candidate = "warn"
|
||||||
|
mut-mut = "warn"
|
||||||
|
mutex-atomic = "warn"
|
||||||
|
mutex-integer = "warn"
|
||||||
|
naive-bytecount = "warn"
|
||||||
|
needless-bitwise-bool = "warn"
|
||||||
|
needless-collect = "warn"
|
||||||
|
needless-continue = "warn"
|
||||||
|
needless-for-each = "warn"
|
||||||
|
needless-pass-by-ref-mut = "warn"
|
||||||
|
needless-pass-by-value = "warn"
|
||||||
|
needless-raw-string-hashes = "warn"
|
||||||
|
needless-raw-strings = "warn"
|
||||||
|
needless-type-cast = "warn"
|
||||||
|
no-effect-underscore-binding = "warn"
|
||||||
|
no-mangle-with-rust-abi = "warn"
|
||||||
|
non-ascii-literal = "allow"
|
||||||
|
non-send-fields-in-send-ty = "warn"
|
||||||
|
non-std-lazy-statics = "warn"
|
||||||
|
non-zero-suggestions = "warn"
|
||||||
|
nonminimal-bool = "warn"
|
||||||
|
nonstandard-macro-braces = "warn"
|
||||||
|
option-as-ref-cloned = "warn"
|
||||||
|
option-if-let-else = "warn"
|
||||||
|
option-option = "warn"
|
||||||
|
or-fun-call = "warn"
|
||||||
|
overly-complex-bool-expr = "allow"
|
||||||
|
panic = "warn"
|
||||||
|
panic-in-result-fn = "warn"
|
||||||
|
partial-pub-fields = "warn"
|
||||||
|
path-buf-push-overwrite = "warn"
|
||||||
|
pathbuf-init-then-push = "warn"
|
||||||
|
pattern-type-mismatch = "allow"
|
||||||
|
pointer-format = "warn"
|
||||||
|
precedence-bits = "warn"
|
||||||
|
print-stderr = "warn"
|
||||||
|
print-stdout = "warn"
|
||||||
|
ptr-as-ptr = "warn"
|
||||||
|
ptr-cast-constness = "warn"
|
||||||
|
ptr-offset-by-literal = "warn"
|
||||||
|
pub-underscore-fields = "warn"
|
||||||
|
pub-use = "allow"
|
||||||
|
pub-with-shorthand = "allow"
|
||||||
|
pub-without-shorthand = "warn"
|
||||||
|
question-mark-used = "allow"
|
||||||
|
range-minus-one = "warn"
|
||||||
|
range-plus-one = "warn"
|
||||||
|
rc-buffer = "warn"
|
||||||
|
rc-mutex = "warn"
|
||||||
|
read-zero-byte-vec = "warn"
|
||||||
|
redundant-clone = "warn"
|
||||||
|
redundant-closure-for-method-calls = "warn"
|
||||||
|
redundant-else = "warn"
|
||||||
|
redundant-pub-crate = "allow"
|
||||||
|
redundant-test-prefix = "warn"
|
||||||
|
redundant-type-annotations = "warn"
|
||||||
|
ref-as-ptr = "warn"
|
||||||
|
ref-binding-to-reference = "warn"
|
||||||
|
ref-option = "warn"
|
||||||
|
ref-option-ref = "warn"
|
||||||
|
ref-patterns = "warn"
|
||||||
|
renamed-function-params = "warn"
|
||||||
|
rest-pat-in-fully-bound-structs = "warn"
|
||||||
|
return-and-then = "warn"
|
||||||
|
return-self-not-must-use = "warn"
|
||||||
|
same-functions-in-if-condition = "warn"
|
||||||
|
same-length-and-capacity = "warn"
|
||||||
|
same-name-method = "warn"
|
||||||
|
search-is-some = "warn"
|
||||||
|
self-named-module-files = "warn"
|
||||||
|
self-only-used-in-recursion = "warn"
|
||||||
|
semicolon-if-nothing-returned = "warn"
|
||||||
|
semicolon-inside-block = "warn"
|
||||||
|
semicolon-outside-block = "allow"
|
||||||
|
separated-literal-suffix = "allow"
|
||||||
|
set-contains-or-insert = "warn"
|
||||||
|
shadow-reuse = "allow"
|
||||||
|
shadow-same = "allow"
|
||||||
|
shadow-unrelated = "allow"
|
||||||
|
should-panic-without-expect = "warn"
|
||||||
|
significant-drop-in-scrutinee = "warn"
|
||||||
|
significant-drop-tightening = "warn"
|
||||||
|
similar-names = "allow"
|
||||||
|
single-call-fn = "allow"
|
||||||
|
single-char-lifetime-names = "allow"
|
||||||
|
single-char-pattern = "allow"
|
||||||
|
single-match-else = "warn"
|
||||||
|
single-option-map = "warn"
|
||||||
|
stable-sort-primitive = "warn"
|
||||||
|
std-instead-of-alloc = "warn"
|
||||||
|
std-instead-of-core = "warn"
|
||||||
|
str-split-at-newline = "warn"
|
||||||
|
str-to-string = "warn"
|
||||||
|
string-add = "warn"
|
||||||
|
string-add-assign = "warn"
|
||||||
|
string-lit-as-bytes = "warn"
|
||||||
|
string-lit-chars-any = "warn"
|
||||||
|
string-slice = "warn"
|
||||||
|
struct-excessive-bools = "allow"
|
||||||
|
struct-field-names = "warn"
|
||||||
|
suboptimal-flops = "warn"
|
||||||
|
suspicious-operation-groupings = "warn"
|
||||||
|
suspicious-xor-used-as-pow = "warn"
|
||||||
|
tests-outside-test-module = "warn"
|
||||||
|
todo = "warn"
|
||||||
|
too-long-first-doc-paragraph = "allow"
|
||||||
|
too-many-lines = "allow"
|
||||||
|
trailing-empty-array = "warn"
|
||||||
|
trait-duplication-in-bounds = "warn"
|
||||||
|
transmute-ptr-to-ptr = "warn"
|
||||||
|
transmute-undefined-repr = "warn"
|
||||||
|
trivial-regex = "warn"
|
||||||
|
trivially-copy-pass-by-ref = "warn"
|
||||||
|
try-err = "warn"
|
||||||
|
tuple-array-conversions = "warn"
|
||||||
|
type-repetition-in-bounds = "warn"
|
||||||
|
unchecked-time-subtraction = "deny"
|
||||||
|
undocumented-unsafe-blocks = "deny"
|
||||||
|
unicode-not-nfc = "warn"
|
||||||
|
unimplemented = "warn"
|
||||||
|
uninhabited-references = "warn"
|
||||||
|
uninlined-format-args = "warn"
|
||||||
|
unnecessary-box-returns = "warn"
|
||||||
|
unnecessary-debug-formatting = "warn"
|
||||||
|
unnecessary-join = "warn"
|
||||||
|
unnecessary-literal-bound = "warn"
|
||||||
|
unnecessary-safety-comment = "warn"
|
||||||
|
unnecessary-safety-doc = "warn"
|
||||||
|
unnecessary-self-imports = "warn"
|
||||||
|
unnecessary-semicolon = "warn"
|
||||||
|
unnecessary-struct-initialization = "warn"
|
||||||
|
unnecessary-trailing-comma = "warn"
|
||||||
|
unnecessary-wraps = "warn"
|
||||||
|
unneeded-field-pattern = "warn"
|
||||||
|
unnested-or-patterns = "warn"
|
||||||
|
unreachable = "warn"
|
||||||
|
unreadable-literal = "warn"
|
||||||
|
unsafe-derive-deserialize = "warn"
|
||||||
|
unseparated-literal-suffix = "warn"
|
||||||
|
unused-async = "warn"
|
||||||
|
unused-async-trait-impl = "warn"
|
||||||
|
unused-peekable = "warn"
|
||||||
|
unused-result-ok = "warn"
|
||||||
|
unused-rounding = "warn"
|
||||||
|
unused-self = "warn"
|
||||||
|
unused-trait-names = "warn"
|
||||||
|
unwrap-in-result = "warn"
|
||||||
|
unwrap-used = "warn"
|
||||||
|
use-debug = "warn"
|
||||||
|
use-self = "warn"
|
||||||
|
used-underscore-binding = "warn"
|
||||||
|
used-underscore-items = "warn"
|
||||||
|
useless-let-if-seq = "warn"
|
||||||
|
verbose-bit-mask = "warn"
|
||||||
|
verbose-file-reads = "warn"
|
||||||
|
volatile-composites = "warn"
|
||||||
|
while-float = "warn"
|
||||||
|
wildcard-enum-match-arm = "warn"
|
||||||
|
wildcard-imports = "warn"
|
||||||
|
with-capacity-zero = "warn"
|
||||||
|
zero-sized-map-values = "warn"
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
arithmetic_overflow = "forbid"
|
deprecated-safe = "forbid"
|
||||||
missing_docs = "forbid"
|
future-incompatible = "deny"
|
||||||
unsafe_code = "forbid"
|
keyword-idents = "deny"
|
||||||
unused_doc_comments = "forbid"
|
let-underscore = "warn"
|
||||||
|
nonstandard-style = "warn"
|
||||||
|
refining-impl-trait = "warn"
|
||||||
|
rust-2018-compatibility = "deny"
|
||||||
|
rust-2018-idioms = "deny"
|
||||||
|
rust-2021-compatibility = "deny"
|
||||||
|
rust-2024-compatibility = "deny"
|
||||||
|
unknown-or-malformed-diagnostic-attributes = "deny"
|
||||||
|
unused = { level = "warn", priority = -1 }
|
||||||
|
|
||||||
|
ambiguous-glob-reexports = "warn"
|
||||||
|
ambiguous-negative-literals = "deny"
|
||||||
|
ambiguous-wide-pointer-comparisons = "warn"
|
||||||
|
arithmetic-overflow = "deny"
|
||||||
|
asm-sub-register = "warn"
|
||||||
|
async-fn-in-trait = "warn"
|
||||||
|
bad-asm-style = "warn"
|
||||||
|
binary-asm-labels = "deny"
|
||||||
|
bindings-with-variant-name = "deny"
|
||||||
|
break-with-label-and-loop = "warn"
|
||||||
|
c-void-returns = "warn"
|
||||||
|
clashing-extern-declarations = "warn"
|
||||||
|
closure-returning-async-block = "warn"
|
||||||
|
confusable-idents = "warn"
|
||||||
|
const-item-interior-mutations = "warn"
|
||||||
|
const-item-mutation = "warn"
|
||||||
|
dangerous-implicit-autorefs = "deny"
|
||||||
|
dangling-pointers-from-locals = "warn"
|
||||||
|
dangling-pointers-from-temporaries = "warn"
|
||||||
|
dead-code-pub-in-binary = "warn"
|
||||||
|
deprecated = "warn"
|
||||||
|
deprecated-in-future = "allow"
|
||||||
|
deprecated-where-clause-location = "warn"
|
||||||
|
deref-into-dyn-supertrait = "warn"
|
||||||
|
deref-nullptr = "deny"
|
||||||
|
double-negations = "warn"
|
||||||
|
drop-bounds = "warn"
|
||||||
|
dropping-copy-types = "warn"
|
||||||
|
dropping-references = "warn"
|
||||||
|
duplicate-features = "deny"
|
||||||
|
duplicate-macro-attributes = "warn"
|
||||||
|
dyn-drop = "warn"
|
||||||
|
enum-intrinsics-non-enums = "deny"
|
||||||
|
explicit-builtin-cfgs-in-flags = "deny"
|
||||||
|
exported-private-dependencies = "warn"
|
||||||
|
ffi-unwind-calls = "warn"
|
||||||
|
for-loops-over-fallibles = "warn"
|
||||||
|
forgetting-copy-types = "warn"
|
||||||
|
forgetting-references = "warn"
|
||||||
|
function-casts-as-integer = "warn"
|
||||||
|
function-item-references = "warn"
|
||||||
|
hidden-glob-reexports = "warn"
|
||||||
|
impl-trait-redundant-captures = "warn"
|
||||||
|
improper-ctypes = "warn"
|
||||||
|
improper-ctypes-definitions = "warn"
|
||||||
|
improper-gpu-kernel-arg = "warn"
|
||||||
|
incomplete-features = "warn"
|
||||||
|
incomplete-include = "deny"
|
||||||
|
ineffective-unstable-trait-impl = "deny"
|
||||||
|
inline-no-sanitize = "warn"
|
||||||
|
integer-to-ptr-transmutes = "warn"
|
||||||
|
internal-features = "warn"
|
||||||
|
invalid-atomic-ordering = "deny"
|
||||||
|
invalid-doc-attributes = "warn"
|
||||||
|
invalid-from-utf8 = "warn"
|
||||||
|
invalid-from-utf8-unchecked = "deny"
|
||||||
|
invalid-nan-comparisons = "warn"
|
||||||
|
invalid-null-arguments = "deny"
|
||||||
|
invalid-reference-casting = "deny"
|
||||||
|
invalid-runtime-symbol-definitions = "deny"
|
||||||
|
invalid-value = "warn"
|
||||||
|
irrefutable-let-patterns = "warn"
|
||||||
|
large-assignments = "warn"
|
||||||
|
linker-info = "allow"
|
||||||
|
linker-messages = "warn"
|
||||||
|
long-running-const-eval = "deny"
|
||||||
|
macro-use-extern-crate = "warn"
|
||||||
|
meta-variable-misuse = "warn"
|
||||||
|
mismatched-lifetime-syntaxes = "warn"
|
||||||
|
missing-abi = "warn"
|
||||||
|
missing-copy-implementations = "warn"
|
||||||
|
missing-debug-implementations = "warn"
|
||||||
|
missing-docs = "warn"
|
||||||
|
missing-gpu-kernel-export-name = "warn"
|
||||||
|
mixed-script-confusables = "warn"
|
||||||
|
mutable-transmutes = "deny"
|
||||||
|
named-arguments-used-positionally = "warn"
|
||||||
|
named-asm-labels = "deny"
|
||||||
|
no-mangle-const-items = "deny"
|
||||||
|
no-mangle-generic-items = "warn"
|
||||||
|
non-ascii-idents = "deny"
|
||||||
|
non-contiguous-range-endpoints = "warn"
|
||||||
|
non-local-definitions = "warn"
|
||||||
|
non-shorthand-field-patterns = "warn"
|
||||||
|
noop-method-call = "warn"
|
||||||
|
opaque-hidden-inferred-bound = "warn"
|
||||||
|
overflowing-literals = "deny"
|
||||||
|
overlapping-range-endpoints = "warn"
|
||||||
|
private-bounds = "warn"
|
||||||
|
private-interfaces = "warn"
|
||||||
|
ptr-to-integer-transmute-in-consts = "warn"
|
||||||
|
redundant-imports = "warn"
|
||||||
|
redundant-lifetimes = "warn"
|
||||||
|
renamed-and-removed-lints = "warn"
|
||||||
|
rtsan-nonblocking-async = "warn"
|
||||||
|
single-use-lifetimes = "warn"
|
||||||
|
special-module-name = "warn"
|
||||||
|
stable-features = "warn"
|
||||||
|
suspicious-double-ref-op = "warn"
|
||||||
|
suspicious-runtime-symbol-definitions = "warn"
|
||||||
|
text-direction-codepoint-in-comment = "deny"
|
||||||
|
text-direction-codepoint-in-literal = "deny"
|
||||||
|
trivial-bounds = "warn"
|
||||||
|
trivial-casts = "warn"
|
||||||
|
trivial-numeric-casts = "warn"
|
||||||
|
type-alias-bounds = "warn"
|
||||||
|
uncommon-codepoints = "warn"
|
||||||
|
unconditional-panic = "deny"
|
||||||
|
unconditional-recursion = "warn"
|
||||||
|
undropped-manually-drops = "deny"
|
||||||
|
unexpected-cfgs = "warn"
|
||||||
|
unfulfilled-lint-expectations = "warn"
|
||||||
|
ungated-async-fn-track-caller = "warn"
|
||||||
|
unit-bindings = "warn"
|
||||||
|
unknown-crate-types = "deny"
|
||||||
|
unknown-lints = "warn"
|
||||||
|
unnameable-test-items = "warn"
|
||||||
|
unnameable-types = "warn"
|
||||||
|
unnecessary-transmutes = "warn"
|
||||||
|
unpredictable-function-pointer-comparisons = "warn"
|
||||||
|
unreachable-cfg-select-predicates = "warn"
|
||||||
|
unreachable-pub = "warn"
|
||||||
|
unsafe-code = "forbid"
|
||||||
|
unstable-features = "forbid"
|
||||||
|
unused-associated-type-bounds = "warn"
|
||||||
|
unused-comparisons = "warn"
|
||||||
|
unused-crate-dependencies = "warn"
|
||||||
|
unused-import-braces = "warn"
|
||||||
|
unused-lifetimes = "warn"
|
||||||
|
unused-qualifications = "warn"
|
||||||
|
unused-results = "warn"
|
||||||
|
useless-deprecated = "deny"
|
||||||
|
useless-ptr-null-checks = "warn"
|
||||||
|
uses-power-alignment = "warn"
|
||||||
|
variant-size-differences = "warn"
|
||||||
|
warnings = "warn"
|
||||||
|
while-true = "warn"
|
||||||
|
|
||||||
|
# unstable lints
|
||||||
|
# default-overrides-default-fields = "deny"
|
||||||
|
# deprecated-llvm-intrinsic = "warn"
|
||||||
|
# implicit-provenance-casts = "deny"
|
||||||
|
# multiple-supertrait-upcastable = "warn"
|
||||||
|
# must-not-suspend = "warn"
|
||||||
|
# non-exhaustive-omitted-patterns = "warn"
|
||||||
|
# resolving-to-items-shadowing-supertrait-items = "warn"
|
||||||
|
# shadowing-supertrait-items = "warn"
|
||||||
|
# tail-call-track-caller = "warn"
|
||||||
|
# test-unstable-lint = "deny"
|
||||||
|
# unqualified-local-imports = "deny"
|
||||||
|
|
||||||
|
[workspace.lints.rustdoc]
|
||||||
|
bare-urls = "warn"
|
||||||
|
broken-intra-doc-links = "warn"
|
||||||
|
invalid-codeblock-attributes = "warn"
|
||||||
|
invalid-html-tags = "warn"
|
||||||
|
invalid-rust-codeblocks = "warn"
|
||||||
|
missing-crate-level-docs = "warn"
|
||||||
|
private-doc-tests = "warn"
|
||||||
|
private-intra-doc-links = "warn"
|
||||||
|
redundant-explicit-links = "warn"
|
||||||
|
unescaped-backticks = "warn"
|
||||||
|
|
||||||
|
# unstable lints
|
||||||
|
# missing-doc-code-examples = "warn"
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
|
|||||||
@@ -1,17 +1,16 @@
|
|||||||
# Seamantic
|
# Seamantic
|
||||||
|
|
||||||
[](https://crates.io/crates/seamantic)
|
## Development
|
||||||
|
|
||||||
A library to enhance SeaORM
|
Run the full validation suite before committing:
|
||||||
|
|
||||||
## Various builds
|
```sh
|
||||||
|
just all
|
||||||
|
just install
|
||||||
|
```
|
||||||
|
|
||||||
- build: `cargo hack --feature-powerset build`
|
##### Publish
|
||||||
- clippy: `cargo hack --feature-powerset clippy -- -D warnings`
|
|
||||||
- test: `cargo hack --feature-powerset test`
|
- `cargo semver-checks --all-features`
|
||||||
- test old: `cargo +1.85 hack --feature-powerset test`
|
- `cargo publish --dry-run -p <crate>`
|
||||||
- example: `cargo run --example=migrations --features=sqlite`
|
- `cargo publish --dry-run --workspace`
|
||||||
- fmt: `cargo fmt --check`
|
|
||||||
- docs: `RUSTDOCFLAGS="--cfg docsrs" cargo +nightly doc --all-features`
|
|
||||||
- semver: `cargo semver-checks --all-features`
|
|
||||||
- publish: `cargo publish --dry-run -p seamantic`
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "seamantic"
|
name = "seamantic"
|
||||||
version = "0.0.12"
|
version = "1.0.0"
|
||||||
|
description = "A library to enhance SeaORM"
|
||||||
|
repository = "https://git.skrundz.dev/quantumshade/seamantic"
|
||||||
|
keywords = ["SeaORM"]
|
||||||
|
categories = ["database"]
|
||||||
|
include = ["/src"]
|
||||||
|
publish = true
|
||||||
|
|
||||||
edition.workspace = true
|
edition.workspace = true
|
||||||
rust-version.workspace = true
|
rust-version.workspace = true
|
||||||
description = "A library to enhance SeaORM"
|
|
||||||
repository = "https://github.com/QuantumShade/seamantic"
|
|
||||||
license-file.workspace = true
|
license-file.workspace = true
|
||||||
categories = []
|
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
@@ -29,7 +33,7 @@ serde_test = { workspace = true }
|
|||||||
tokio = { workspace = true, features = ["macros", "rt"] }
|
tokio = { workspace = true, features = ["macros", "rt"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["sqlite"]
|
||||||
serde = ["dep:serde"]
|
serde = ["dep:serde"]
|
||||||
sqlite = ["sea-orm-migration/sqlx-sqlite"]
|
sqlite = ["sea-orm-migration/sqlx-sqlite"]
|
||||||
|
|
||||||
@@ -14,6 +14,10 @@ pub(super) struct Migration;
|
|||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl MigrationTrait for Migration {
|
impl MigrationTrait for Migration {
|
||||||
|
#[expect(
|
||||||
|
elided_lifetimes_in_paths,
|
||||||
|
reason = "async_trait causes lifetimes to be strange"
|
||||||
|
)]
|
||||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
manager
|
manager
|
||||||
.create_table(
|
.create_table(
|
||||||
@@ -28,6 +32,10 @@ impl MigrationTrait for Migration {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(
|
||||||
|
elided_lifetimes_in_paths,
|
||||||
|
reason = "async_trait causes lifetimes to be strange"
|
||||||
|
)]
|
||||||
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
manager
|
manager
|
||||||
.drop_table(Table::drop().table(Objects::Table).to_owned())
|
.drop_table(Table::drop().table(Objects::Table).to_owned())
|
||||||
@@ -15,3 +15,6 @@ async fn main() {
|
|||||||
let database = Database::connect(options).await.expect("Database::connect");
|
let database = Database::connect(options).await.expect("Database::connect");
|
||||||
Migrator::up(&database, None).await.expect("Migrator::up");
|
Migrator::up(&database, None).await.expect("Migrator::up");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dev-dependencies cannot be optional and `serde_test` is not used
|
||||||
|
use {serde_test as _, tokio as _};
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//! A library for enhacing SeaORM
|
//! A library for enhacing `SeaORM`.
|
||||||
|
|
||||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
|
|
||||||
@@ -46,3 +46,8 @@ macro_rules! migrations {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dev-dependencies cannot be optional and `tokio` and `serde_test` are only used
|
||||||
|
// when the `sqlite` feature is active
|
||||||
|
#[cfg(test)]
|
||||||
|
use {serde_test as _, tokio as _};
|
||||||
@@ -1,37 +1,45 @@
|
|||||||
//! [Duration] utilities
|
//! [Duration] utilities.
|
||||||
|
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
|
|
||||||
use sea_orm::sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr};
|
use sea_orm::sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr};
|
||||||
use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, TryGetable, Value};
|
use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, TryGetable, Value};
|
||||||
|
|
||||||
// "u64 unsupported by sqlx-sqlite", so use i64 as the bit representation
|
/// The internal representation that `SeaORM` uses for durations.
|
||||||
|
///
|
||||||
|
/// "u64 unsupported by sqlx-sqlite", so use i64 as the bit representation.
|
||||||
type SeaOrmRepr = i64;
|
type SeaOrmRepr = i64;
|
||||||
|
/// The internal represenation that [Duration] uses.
|
||||||
type DurationRepr = u64;
|
type DurationRepr = u64;
|
||||||
|
|
||||||
/// Wrapper around [Duration] to store a number of seconds
|
/// Wrapper around [Duration] to store a number of seconds.
|
||||||
///
|
///
|
||||||
/// ### Warning:
|
/// # Warning:
|
||||||
/// Sub-second precision will be lost
|
/// Sub-second precision will be lost.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
#[expect(clippy::exhaustive_structs, reason = "unlikely to change")]
|
||||||
pub struct Seconds(pub Duration);
|
pub struct Seconds(pub Duration);
|
||||||
|
|
||||||
impl From<Duration> for Seconds {
|
impl From<Duration> for Seconds {
|
||||||
|
#[inline]
|
||||||
fn from(value: Duration) -> Self {
|
fn from(value: Duration) -> Self {
|
||||||
Self(Duration::from_secs(value.as_secs()))
|
Self(Duration::from_secs(value.as_secs()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Seconds> for Duration {
|
impl From<Seconds> for Duration {
|
||||||
|
#[inline]
|
||||||
fn from(value: Seconds) -> Self {
|
fn from(value: Seconds) -> Self {
|
||||||
value.0
|
value.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ValueType for Seconds {
|
impl ValueType for Seconds {
|
||||||
|
#[inline]
|
||||||
fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
|
fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
|
||||||
<SeaOrmRepr as ValueType>::try_from(v)
|
<SeaOrmRepr as ValueType>::try_from(v)
|
||||||
.map(|i| DurationRepr::from_ne_bytes(i.to_ne_bytes()))
|
.map(|i| DurationRepr::from_ne_bytes(i.to_ne_bytes()))
|
||||||
@@ -39,26 +47,32 @@ impl ValueType for Seconds {
|
|||||||
.map(Self)
|
.map(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn type_name() -> String {
|
fn type_name() -> String {
|
||||||
core::any::type_name::<Self>().to_string()
|
core::any::type_name::<Self>().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn array_type() -> ArrayType {
|
fn array_type() -> ArrayType {
|
||||||
SeaOrmRepr::array_type()
|
SeaOrmRepr::array_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn column_type() -> ColumnType {
|
fn column_type() -> ColumnType {
|
||||||
SeaOrmRepr::column_type()
|
SeaOrmRepr::column_type()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Seconds> for Value {
|
impl From<Seconds> for Value {
|
||||||
|
#[inline]
|
||||||
fn from(value: Seconds) -> Self {
|
fn from(value: Seconds) -> Self {
|
||||||
value.0.as_secs().into()
|
value.0.as_secs().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl TryGetable for Seconds {
|
impl TryGetable for Seconds {
|
||||||
|
#[inline]
|
||||||
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
|
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
|
||||||
SeaOrmRepr::try_get_by(res, index)
|
SeaOrmRepr::try_get_by(res, index)
|
||||||
.map(|i| DurationRepr::from_ne_bytes(i.to_ne_bytes()))
|
.map(|i| DurationRepr::from_ne_bytes(i.to_ne_bytes()))
|
||||||
@@ -68,6 +82,7 @@ impl TryGetable for Seconds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromU64 for Seconds {
|
impl TryFromU64 for Seconds {
|
||||||
|
#[inline]
|
||||||
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
||||||
SeaOrmRepr::try_from_u64(n)
|
SeaOrmRepr::try_from_u64(n)
|
||||||
.map(|i| DurationRepr::from_ne_bytes(i.to_ne_bytes()))
|
.map(|i| DurationRepr::from_ne_bytes(i.to_ne_bytes()))
|
||||||
@@ -77,6 +92,7 @@ impl TryFromU64 for Seconds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Nullable for Seconds {
|
impl Nullable for Seconds {
|
||||||
|
#[inline]
|
||||||
fn null() -> Value {
|
fn null() -> Value {
|
||||||
SeaOrmRepr::null()
|
SeaOrmRepr::null()
|
||||||
}
|
}
|
||||||
@@ -91,10 +107,9 @@ mod tests {
|
|||||||
|
|
||||||
use super::Seconds;
|
use super::Seconds;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "seconds")]
|
#[sea_orm(table_name = "seconds")]
|
||||||
pub struct Model {
|
struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
id: u8,
|
id: u8,
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
@@ -102,9 +117,9 @@ mod tests {
|
|||||||
nullable: Option<Seconds>,
|
nullable: Option<Seconds>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {}
|
enum Relation {}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Typed IDs for use as primary keys
|
//! Typed IDs for use as primary keys.
|
||||||
|
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
@@ -8,57 +8,70 @@ use core::marker::PhantomData;
|
|||||||
use sea_orm::sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr};
|
use sea_orm::sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr};
|
||||||
use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, TryGetable, Value};
|
use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, TryGetable, Value};
|
||||||
|
|
||||||
/// The internal representation used by the database
|
/// The internal representation used by the database.
|
||||||
pub type SeaOrmRepr = i64;
|
pub type SeaOrmRepr = i64;
|
||||||
|
|
||||||
/// An opaque type representing a row ID
|
/// An opaque type representing a row ID.
|
||||||
///
|
///
|
||||||
/// IDs should be tagged with `#[sea_orm(primary_key, auto_increment = false)]`
|
/// 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", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub struct Id<T> {
|
pub struct Id<T> {
|
||||||
|
/// The ID.
|
||||||
id: SeaOrmRepr,
|
id: SeaOrmRepr,
|
||||||
|
/// `PhantomData` for `T`.
|
||||||
#[cfg_attr(feature = "serde", serde(skip_serializing, default))]
|
#[cfg_attr(feature = "serde", serde(skip_serializing, default))]
|
||||||
_phantom: PhantomData<T>,
|
_phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manual implementation since `T: Clone` is not required
|
// Manual implementation since `T: Clone` is not required.
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl<T> Clone for Id<T> {
|
impl<T> Clone for Id<T> {
|
||||||
|
#[inline]
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
*self
|
*self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manual implementation since `T: Copy` is not required
|
// Manual implementation since `T: Copy` is not required.
|
||||||
impl<T> Copy for Id<T> {}
|
impl<T> Copy for Id<T> {}
|
||||||
|
|
||||||
// Manual implementation since `T: PartialEq` is not required
|
// Manual implementation since `T: PartialEq` is not required.
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl<T> PartialEq for Id<T> {
|
impl<T> PartialEq for Id<T> {
|
||||||
|
#[inline]
|
||||||
fn eq(&self, other: &Self) -> bool {
|
fn eq(&self, other: &Self) -> bool {
|
||||||
self.id == other.id
|
self.id.eq(&other.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manual implementation since `T: Eq` is not required
|
// Manual implementation since `T: Eq` is not required.
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl<T> Eq for Id<T> {}
|
impl<T> Eq for Id<T> {}
|
||||||
|
|
||||||
// Manual implementation since `T: PartialOrd` is not required
|
// Manual implementation since `T: PartialOrd` is not required.
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl<T> PartialOrd for Id<T> {
|
impl<T> PartialOrd for Id<T> {
|
||||||
|
#[inline]
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||||
Some(self.cmp(other))
|
Some(self.cmp(other))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manual implementation since `T: Ord` is not required
|
// Manual implementation since `T: Ord` is not required
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl<T> Ord for Id<T> {
|
impl<T> Ord for Id<T> {
|
||||||
|
#[inline]
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering {
|
||||||
self.id.cmp(&other.id)
|
self.id.cmp(&other.id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Manual implementation since `T: Hash` is not required
|
// Manual implementation since `T: Hash` is not required
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl<T> Hash for Id<T> {
|
impl<T> Hash for Id<T> {
|
||||||
|
#[inline]
|
||||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
self.id.hash(state);
|
self.id.hash(state);
|
||||||
}
|
}
|
||||||
@@ -66,7 +79,9 @@ impl<T> Hash for Id<T> {
|
|||||||
|
|
||||||
impl<T> Id<T> {
|
impl<T> Id<T> {
|
||||||
/// Allows the conversion from a raw value to [Id], though the use is discouraged.
|
/// Allows the conversion from a raw value to [Id], though the use is discouraged.
|
||||||
pub fn from_raw(raw: SeaOrmRepr) -> Self {
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn from_raw(raw: SeaOrmRepr) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id: raw,
|
id: raw,
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
@@ -74,13 +89,16 @@ impl<T> Id<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Allows extracting the raw value, though the use is discouraged.
|
/// Allows extracting the raw value, though the use is discouraged.
|
||||||
pub fn into_raw(self) -> SeaOrmRepr {
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub const fn into_raw(self) -> SeaOrmRepr {
|
||||||
self.id
|
self.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> fmt::Debug for Id<T> {
|
impl<T> fmt::Debug for Id<T> {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
#[inline]
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("Id")
|
f.debug_struct("Id")
|
||||||
.field("T", &core::any::type_name::<T>())
|
.field("T", &core::any::type_name::<T>())
|
||||||
.field("id", &self.id)
|
.field("id", &self.id)
|
||||||
@@ -88,7 +106,9 @@ impl<T> fmt::Debug for Id<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl<T> ValueType for Id<T> {
|
impl<T> ValueType for Id<T> {
|
||||||
|
#[inline]
|
||||||
fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
|
fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
|
||||||
<SeaOrmRepr as ValueType>::try_from(v).map(|id| Self {
|
<SeaOrmRepr as ValueType>::try_from(v).map(|id| Self {
|
||||||
id,
|
id,
|
||||||
@@ -96,26 +116,32 @@ impl<T> ValueType for Id<T> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn type_name() -> String {
|
fn type_name() -> String {
|
||||||
format!("Id<{}>", &core::any::type_name::<T>())
|
format!("Id<{}>", core::any::type_name::<T>())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn array_type() -> ArrayType {
|
fn array_type() -> ArrayType {
|
||||||
SeaOrmRepr::array_type()
|
SeaOrmRepr::array_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn column_type() -> ColumnType {
|
fn column_type() -> ColumnType {
|
||||||
SeaOrmRepr::column_type()
|
SeaOrmRepr::column_type()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> From<Id<T>> for Value {
|
impl<T> From<Id<T>> for Value {
|
||||||
|
#[inline]
|
||||||
fn from(value: Id<T>) -> Self {
|
fn from(value: Id<T>) -> Self {
|
||||||
value.id.into()
|
value.id.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl<T> TryGetable for Id<T> {
|
impl<T> TryGetable for Id<T> {
|
||||||
|
#[inline]
|
||||||
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
|
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
|
||||||
SeaOrmRepr::try_get_by(res, index).map(|id| Self {
|
SeaOrmRepr::try_get_by(res, index).map(|id| Self {
|
||||||
id,
|
id,
|
||||||
@@ -125,6 +151,7 @@ impl<T> TryGetable for Id<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TryFromU64 for Id<T> {
|
impl<T> TryFromU64 for Id<T> {
|
||||||
|
#[inline]
|
||||||
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
||||||
SeaOrmRepr::try_from_u64(n).map(|id| Self {
|
SeaOrmRepr::try_from_u64(n).map(|id| Self {
|
||||||
id,
|
id,
|
||||||
@@ -134,6 +161,7 @@ impl<T> TryFromU64 for Id<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Nullable for Id<T> {
|
impl<T> Nullable for Id<T> {
|
||||||
|
#[inline]
|
||||||
fn null() -> Value {
|
fn null() -> Value {
|
||||||
SeaOrmRepr::null()
|
SeaOrmRepr::null()
|
||||||
}
|
}
|
||||||
@@ -148,24 +176,32 @@ mod tests {
|
|||||||
|
|
||||||
use super::Id;
|
use super::Id;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "ids")]
|
#[sea_orm(table_name = "ids")]
|
||||||
pub struct Model {
|
#[expect(clippy::use_self, reason = "derive macros cause Self to be invalid")]
|
||||||
|
struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
id: Id<Model>,
|
id: Id<Model>,
|
||||||
nullable: Option<Id<Model>>,
|
nullable: Option<Id<Model>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {}
|
enum Relation {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||||
|
fn round_trip() {
|
||||||
|
let raw = 1234;
|
||||||
|
let id = Id::<()>::from_raw(raw);
|
||||||
|
assert_eq!(raw, id.into_raw());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
fn test_serde() {
|
fn ser_de() {
|
||||||
let id: Id<()> = Id::from_raw(1234);
|
let id: Id<()> = Id::from_raw(1234);
|
||||||
serde_test::assert_tokens(&id, &[serde_test::Token::I64(1234)]);
|
serde_test::assert_tokens(&id, &[serde_test::Token::I64(1234)]);
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
//! `SeaORM` column types for enforcing data consistency.
|
||||||
|
|
||||||
|
pub mod duration;
|
||||||
|
pub mod id;
|
||||||
|
pub mod net;
|
||||||
|
pub mod path;
|
||||||
@@ -1,74 +1,89 @@
|
|||||||
//! [IpAddr], [Ipv4Addr], and [Ipv6Addr] utilities
|
//! [`IpAddr`], [`Ipv4Addr`], and [`Ipv6Addr`] utilities.
|
||||||
|
|
||||||
use core::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
use core::net::{IpAddr, Ipv4Addr, Ipv6Addr};
|
||||||
|
|
||||||
use sea_orm::sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr};
|
use sea_orm::sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr};
|
||||||
use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, TryGetable, Value};
|
use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, TryGetable, Value};
|
||||||
|
|
||||||
|
/// The internal representation used by the database.
|
||||||
type SeaOrmRepr = String;
|
type SeaOrmRepr = String;
|
||||||
|
|
||||||
/// Wrapper around [IpAddr]
|
/// Wrapper around [`IpAddr`].
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
#[expect(clippy::exhaustive_structs, reason = "unlikely to change")]
|
||||||
pub struct IpAddress(pub IpAddr);
|
pub struct IpAddress(pub IpAddr);
|
||||||
|
|
||||||
/// Wrapper around [IpAddr]
|
/// Wrapper around [`Ipv4Addr`].
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
#[expect(clippy::exhaustive_structs, reason = "unlikely to change")]
|
||||||
pub struct Ipv4Address(pub Ipv4Addr);
|
pub struct Ipv4Address(pub Ipv4Addr);
|
||||||
|
|
||||||
/// Wrapper around [IpAddr]
|
/// Wrapper around [`Ipv6Addr`].
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
#[expect(clippy::exhaustive_structs, reason = "unlikely to change")]
|
||||||
pub struct Ipv6Address(pub Ipv6Addr);
|
pub struct Ipv6Address(pub Ipv6Addr);
|
||||||
|
|
||||||
|
/// Implements `SeaORM` traits for `IP` types.
|
||||||
macro_rules! impl_addr {
|
macro_rules! impl_addr {
|
||||||
($t:ty, $inner:ty, $repr:ty) => {
|
($t:ty, $inner:ty, $repr:ty) => {
|
||||||
impl From<$inner> for $t {
|
impl From<$inner> for $t {
|
||||||
|
#[inline]
|
||||||
fn from(value: $inner) -> Self {
|
fn from(value: $inner) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<$t> for $inner {
|
impl From<$t> for $inner {
|
||||||
|
#[inline]
|
||||||
fn from(value: $t) -> Self {
|
fn from(value: $t) -> Self {
|
||||||
value.0
|
value.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ValueType for $t {
|
impl ValueType for $t {
|
||||||
|
#[inline]
|
||||||
fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
|
fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
|
||||||
<SeaOrmRepr as ValueType>::try_from(v)
|
<SeaOrmRepr as ValueType>::try_from(v)
|
||||||
.and_then(|s| s.parse().map_err(|_| ValueTypeErr))
|
.and_then(|s| s.parse().map_err(|_| ValueTypeErr))
|
||||||
.map(Self)
|
.map(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn type_name() -> String {
|
fn type_name() -> String {
|
||||||
core::any::type_name::<Self>().to_string()
|
core::any::type_name::<Self>().to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn array_type() -> ArrayType {
|
fn array_type() -> ArrayType {
|
||||||
SeaOrmRepr::array_type()
|
SeaOrmRepr::array_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn column_type() -> ColumnType {
|
fn column_type() -> ColumnType {
|
||||||
SeaOrmRepr::column_type()
|
SeaOrmRepr::column_type()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<$t> for Value {
|
impl From<$t> for Value {
|
||||||
|
#[inline]
|
||||||
fn from(value: $t) -> Self {
|
fn from(value: $t) -> Self {
|
||||||
value.0.to_string().into()
|
value.0.to_string().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl TryGetable for $t {
|
impl TryGetable for $t {
|
||||||
|
#[inline]
|
||||||
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
|
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
|
||||||
SeaOrmRepr::try_get_by(res, index)
|
SeaOrmRepr::try_get_by(res, index)
|
||||||
.and_then(|s| s.parse().map_err(|_| TryGetError::Null(Self::type_name())))
|
.and_then(|s| s.parse().map_err(|_| TryGetError::Null(Self::type_name())))
|
||||||
@@ -77,6 +92,7 @@ macro_rules! impl_addr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromU64 for $t {
|
impl TryFromU64 for $t {
|
||||||
|
#[inline]
|
||||||
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
||||||
SeaOrmRepr::try_from_u64(n)
|
SeaOrmRepr::try_from_u64(n)
|
||||||
.and_then(|s| {
|
.and_then(|s| {
|
||||||
@@ -88,6 +104,7 @@ macro_rules! impl_addr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Nullable for $t {
|
impl Nullable for $t {
|
||||||
|
#[inline]
|
||||||
fn null() -> Value {
|
fn null() -> Value {
|
||||||
SeaOrmRepr::null()
|
SeaOrmRepr::null()
|
||||||
}
|
}
|
||||||
@@ -109,10 +126,9 @@ mod tests {
|
|||||||
|
|
||||||
use super::super::IpAddress;
|
use super::super::IpAddress;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "ipaddress")]
|
#[sea_orm(table_name = "ipaddress")]
|
||||||
pub struct Model {
|
struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
id: u8,
|
id: u8,
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
@@ -120,11 +136,11 @@ mod tests {
|
|||||||
nullable: Option<IpAddress>,
|
nullable: Option<IpAddress>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {}
|
enum Relation {}
|
||||||
}
|
}
|
||||||
mod ipv4address {
|
mod ipv4address {
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
@@ -134,10 +150,9 @@ mod tests {
|
|||||||
|
|
||||||
use super::super::Ipv4Address;
|
use super::super::Ipv4Address;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "ipv4address")]
|
#[sea_orm(table_name = "ipv4address")]
|
||||||
pub struct Model {
|
struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
id: u8,
|
id: u8,
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
@@ -145,11 +160,11 @@ mod tests {
|
|||||||
nullable: Option<Ipv4Address>,
|
nullable: Option<Ipv4Address>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {}
|
enum Relation {}
|
||||||
}
|
}
|
||||||
mod ipv6address {
|
mod ipv6address {
|
||||||
use sea_orm::{
|
use sea_orm::{
|
||||||
@@ -159,10 +174,9 @@ mod tests {
|
|||||||
|
|
||||||
use super::super::Ipv6Address;
|
use super::super::Ipv6Address;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "ipv6address")]
|
#[sea_orm(table_name = "ipv6address")]
|
||||||
pub struct Model {
|
struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
id: u8,
|
id: u8,
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
@@ -170,10 +184,10 @@ mod tests {
|
|||||||
nullable: Option<Ipv6Address>,
|
nullable: Option<Ipv6Address>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {}
|
enum Relation {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//! [Path] and [PathBuf] utilities
|
//! [Path] and [`PathBuf`] utilities.
|
||||||
|
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@@ -11,34 +11,42 @@ compile_error!("PathBytes is not supported on Windows");
|
|||||||
use sea_orm::sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr};
|
use sea_orm::sea_query::{ArrayType, Nullable, ValueType, ValueTypeErr};
|
||||||
use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, TryGetable, Value};
|
use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryFromU64, TryGetError, TryGetable, Value};
|
||||||
|
|
||||||
|
/// The internal representation used by the database.
|
||||||
type SeaOrmRepr = Vec<u8>;
|
type SeaOrmRepr = Vec<u8>;
|
||||||
|
|
||||||
/// Wrapper around [PathBuf] to store paths as bytes in SeaORM
|
/// Wrapper around [`PathBuf`] to store paths as bytes in `SeaORM`.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||||
#[cfg_attr(feature = "serde", serde(transparent))]
|
#[cfg_attr(feature = "serde", serde(transparent))]
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
|
#[expect(clippy::exhaustive_structs, reason = "unlikely to change")]
|
||||||
|
#[expect(clippy::module_name_repetitions, reason = "Bytes wouldn't make sense")]
|
||||||
pub struct PathBytes(pub PathBuf);
|
pub struct PathBytes(pub PathBuf);
|
||||||
|
|
||||||
impl From<PathBuf> for PathBytes {
|
impl From<PathBuf> for PathBytes {
|
||||||
|
#[inline]
|
||||||
fn from(value: PathBuf) -> Self {
|
fn from(value: PathBuf) -> Self {
|
||||||
Self(value)
|
Self(value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PathBytes> for PathBuf {
|
impl From<PathBytes> for PathBuf {
|
||||||
|
#[inline]
|
||||||
fn from(value: PathBytes) -> Self {
|
fn from(value: PathBytes) -> Self {
|
||||||
value.0
|
value.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsRef<Path> for PathBytes {
|
impl AsRef<Path> for PathBytes {
|
||||||
|
#[inline]
|
||||||
fn as_ref(&self) -> &Path {
|
fn as_ref(&self) -> &Path {
|
||||||
&self.0
|
&self.0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ValueType for PathBytes {
|
impl ValueType for PathBytes {
|
||||||
|
#[inline]
|
||||||
fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
|
fn try_from(v: Value) -> Result<Self, ValueTypeErr> {
|
||||||
<SeaOrmRepr as ValueType>::try_from(v)
|
<SeaOrmRepr as ValueType>::try_from(v)
|
||||||
.map(OsString::from_vec)
|
.map(OsString::from_vec)
|
||||||
@@ -46,26 +54,32 @@ impl ValueType for PathBytes {
|
|||||||
.map(Self)
|
.map(Self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn type_name() -> String {
|
fn type_name() -> String {
|
||||||
core::any::type_name::<Self>().to_string()
|
core::any::type_name::<Self>().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn array_type() -> ArrayType {
|
fn array_type() -> ArrayType {
|
||||||
SeaOrmRepr::array_type()
|
SeaOrmRepr::array_type()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
fn column_type() -> ColumnType {
|
fn column_type() -> ColumnType {
|
||||||
SeaOrmRepr::column_type()
|
SeaOrmRepr::column_type()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<PathBytes> for Value {
|
impl From<PathBytes> for Value {
|
||||||
|
#[inline]
|
||||||
fn from(value: PathBytes) -> Self {
|
fn from(value: PathBytes) -> Self {
|
||||||
value.0.into_os_string().into_vec().into()
|
value.0.into_os_string().into_vec().into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl TryGetable for PathBytes {
|
impl TryGetable for PathBytes {
|
||||||
|
#[inline]
|
||||||
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
|
fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
|
||||||
SeaOrmRepr::try_get_by(res, index)
|
SeaOrmRepr::try_get_by(res, index)
|
||||||
.map(OsString::from_vec)
|
.map(OsString::from_vec)
|
||||||
@@ -75,6 +89,7 @@ impl TryGetable for PathBytes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TryFromU64 for PathBytes {
|
impl TryFromU64 for PathBytes {
|
||||||
|
#[inline]
|
||||||
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
fn try_from_u64(n: u64) -> Result<Self, DbErr> {
|
||||||
SeaOrmRepr::try_from_u64(n)
|
SeaOrmRepr::try_from_u64(n)
|
||||||
.map(OsString::from_vec)
|
.map(OsString::from_vec)
|
||||||
@@ -84,6 +99,7 @@ impl TryFromU64 for PathBytes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Nullable for PathBytes {
|
impl Nullable for PathBytes {
|
||||||
|
#[inline]
|
||||||
fn null() -> Value {
|
fn null() -> Value {
|
||||||
SeaOrmRepr::null()
|
SeaOrmRepr::null()
|
||||||
}
|
}
|
||||||
@@ -98,10 +114,9 @@ mod tests {
|
|||||||
|
|
||||||
use super::PathBytes;
|
use super::PathBytes;
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
#[derive(Debug, Clone, PartialEq, Eq, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "paths")]
|
#[sea_orm(table_name = "paths")]
|
||||||
pub struct Model {
|
struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
id: u8,
|
id: u8,
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
@@ -109,9 +124,9 @@ mod tests {
|
|||||||
nullable: Option<PathBytes>,
|
nullable: Option<PathBytes>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
#[derive(Debug, EnumIter, DeriveRelation)]
|
#[derive(Debug, EnumIter, DeriveRelation)]
|
||||||
pub enum Relation {}
|
enum Relation {}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Helpers for working with SeaORM
|
//! Helpers for working with `SeaORM`.
|
||||||
|
|
||||||
mod upsert;
|
mod upsert;
|
||||||
pub use upsert::UpsertTrait;
|
pub use upsert::UpsertTrait;
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
//! Provides [`UpsertTrait`] for performing an upsert into a database.
|
||||||
|
|
||||||
|
use sea_orm::sea_query::{IntoColumnRef as _, OnConflict};
|
||||||
|
use sea_orm::{ActiveModelTrait, EntityTrait, Insert, Iterable as _};
|
||||||
|
|
||||||
|
/// This trait add a method on [Insert] to allow for upsert behavior.
|
||||||
|
pub trait UpsertTrait: private::Sealed {
|
||||||
|
/// Set ON CONFLICT on primary key to update all other columns.
|
||||||
|
#[must_use]
|
||||||
|
fn on_conflict_upsert(self) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an [Iterator] over the `PrimaryKey` columns of an [`ActiveModelTrait`].
|
||||||
|
fn primary_key_iter<A: ActiveModelTrait>()
|
||||||
|
-> impl Iterator<Item = <A::Entity as EntityTrait>::PrimaryKey> {
|
||||||
|
<A::Entity as EntityTrait>::PrimaryKey::iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns an [Iterator] over the [`EntityTrait`] columns of an [`ActiveModelTrait`].
|
||||||
|
fn column_iter<A: ActiveModelTrait>() -> impl Iterator<Item = <A::Entity as EntityTrait>::Column> {
|
||||||
|
<<A as ActiveModelTrait>::Entity as EntityTrait>::Column::iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<A: ActiveModelTrait> private::Sealed for Insert<A> {}
|
||||||
|
impl<A: ActiveModelTrait> UpsertTrait for Insert<A> {
|
||||||
|
#[inline]
|
||||||
|
fn on_conflict_upsert(self) -> Self {
|
||||||
|
self.on_conflict(
|
||||||
|
OnConflict::columns(primary_key_iter::<A>())
|
||||||
|
.update_columns(column_iter::<A>().filter(|col| {
|
||||||
|
!primary_key_iter::<A>().any(|pk| pk.into_column_ref() == col.into_column_ref())
|
||||||
|
}))
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Private module.
|
||||||
|
mod private {
|
||||||
|
/// Sealed trait to prevent implementations of `UpsertTrait`.
|
||||||
|
#[expect(unnameable_types, reason = "sealed trait")]
|
||||||
|
pub trait Sealed {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::{UpsertTrait, private::Sealed};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn seal() {
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
impl Sealed for S {}
|
||||||
|
impl UpsertTrait for S {
|
||||||
|
fn on_conflict_upsert(self) -> Self {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_sealed<T: Sealed>() {}
|
||||||
|
fn is_upsert<T: UpsertTrait>() {}
|
||||||
|
|
||||||
|
is_sealed::<S>();
|
||||||
|
is_upsert::<S>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Helpers for defining SeaORM schemas
|
//! Helpers for defining `SeaORM` schemas.
|
||||||
|
|
||||||
#[cfg(feature = "sqlite")]
|
#[cfg(feature = "sqlite")]
|
||||||
mod sqlite;
|
mod sqlite;
|
||||||
@@ -1,16 +1,18 @@
|
|||||||
|
//! Helper functions for declaring `SQLite` schemas.
|
||||||
|
|
||||||
use sea_orm_migration::schema::{integer_null, string};
|
use sea_orm_migration::schema::{integer_null, string};
|
||||||
use sea_orm_migration::sea_query::{ColumnDef, IntoIden};
|
use sea_orm_migration::sea_query::{ColumnDef, IntoIden};
|
||||||
|
|
||||||
/// Sets the column to be an alias for SQLite's rowid.
|
/// Sets the column to be an alias for SQLite's rowid.
|
||||||
///
|
///
|
||||||
/// Required conditions:
|
/// Required conditions:
|
||||||
/// - This column must *not* be auto_increment
|
/// - This column must *not* be `auto_increment`
|
||||||
/// - There cannot be other primary_key columns in the table
|
/// - There cannot be other `primary_key` columns in the table
|
||||||
///
|
///
|
||||||
/// When using `DeriveEntityModel`, the type must be `i64` (or equivalent)
|
/// When using `DeriveEntityModel`, the type must be `i64` (or equivalent)
|
||||||
/// and should be tagged with:
|
/// and should be tagged with:
|
||||||
///
|
///
|
||||||
/// `#[sea_orm(primary_key, auto_increment = false)]`
|
/// > `#[sea_orm(primary_key, auto_increment = false)]`.
|
||||||
///
|
///
|
||||||
/// When using the new entity format:
|
/// When using the new entity format:
|
||||||
///
|
///
|
||||||
@@ -25,11 +27,12 @@ use sea_orm_migration::sea_query::{ColumnDef, IntoIden};
|
|||||||
/// id: Id<Model>,
|
/// id: Id<Model>,
|
||||||
/// }
|
/// }
|
||||||
/// impl ActiveModelBehavior for ActiveModel {}
|
/// impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
#[inline]
|
||||||
pub fn sqlite_rowid_alias<T: IntoIden>(name: T) -> ColumnDef {
|
pub fn sqlite_rowid_alias<T: IntoIden>(name: T) -> ColumnDef {
|
||||||
integer_null(name).primary_key().take()
|
integer_null(name).primary_key().take()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the column to be a case insensitive string
|
/// Set the column to be a case insensitive string.
|
||||||
///
|
///
|
||||||
/// When using the new entity format:
|
/// When using the new entity format:
|
||||||
///
|
///
|
||||||
@@ -46,6 +49,7 @@ pub fn sqlite_rowid_alias<T: IntoIden>(name: T) -> ColumnDef {
|
|||||||
/// nocase: String,
|
/// nocase: String,
|
||||||
/// }
|
/// }
|
||||||
/// impl ActiveModelBehavior for ActiveModel {}
|
/// impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
#[inline]
|
||||||
pub fn sqlite_case_insensitive_string<T: IntoIden>(name: T) -> ColumnDef {
|
pub fn sqlite_case_insensitive_string<T: IntoIden>(name: T) -> ColumnDef {
|
||||||
string(name).extra("COLLATE NOCASE").take()
|
string(name).extra("COLLATE NOCASE").take()
|
||||||
}
|
}
|
||||||
@@ -54,7 +58,12 @@ pub fn sqlite_case_insensitive_string<T: IntoIden>(name: T) -> ColumnDef {
|
|||||||
mod entity_tests {
|
mod entity_tests {
|
||||||
use sea_orm::{ConnectOptions, Database, DatabaseConnection};
|
use sea_orm::{ConnectOptions, Database, DatabaseConnection};
|
||||||
|
|
||||||
pub async fn new_initialized_memory_db() -> DatabaseConnection {
|
/// Create and initialize a new in-memory SQLite database.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
/// If the database driver fails to connect, or if the schema registry fails
|
||||||
|
/// to sync the database schema.
|
||||||
|
async fn new_initialized_memory_db() -> DatabaseConnection {
|
||||||
let options = ConnectOptions::new("sqlite::memory:");
|
let options = ConnectOptions::new("sqlite::memory:");
|
||||||
|
|
||||||
let db = Database::connect(options)
|
let db = Database::connect(options)
|
||||||
@@ -67,6 +76,22 @@ mod entity_tests {
|
|||||||
db
|
db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(
|
||||||
|
clippy::unused_async_trait_impl,
|
||||||
|
reason = "sea_orm::model generates unpolled futures"
|
||||||
|
)]
|
||||||
|
#[expect(
|
||||||
|
clippy::same_name_method,
|
||||||
|
reason = "sea_orm::model generates the same method names"
|
||||||
|
)]
|
||||||
|
#[expect(
|
||||||
|
clippy::allow_attributes,
|
||||||
|
reason = "under different conditions sea_orm::model generates different code"
|
||||||
|
)]
|
||||||
|
#[allow(
|
||||||
|
clippy::future_not_send,
|
||||||
|
reason = "sea_orm::model generates un-send futures"
|
||||||
|
)]
|
||||||
mod rowid_test {
|
mod rowid_test {
|
||||||
use sea_orm::ActiveValue::{NotSet, Set};
|
use sea_orm::ActiveValue::{NotSet, Set};
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
@@ -76,14 +101,18 @@ mod entity_tests {
|
|||||||
#[sea_orm::model]
|
#[sea_orm::model]
|
||||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "rowid_test")]
|
#[sea_orm(table_name = "rowid_test")]
|
||||||
pub struct Model {
|
struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
id: Id<Model>,
|
id: Id<Model>,
|
||||||
}
|
}
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
|
#[cfg(not(miri))]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_sqlite_rowid_alias() {
|
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||||
|
#[expect(clippy::default_numeric_fallback, reason = "numbers are not ambiguous")]
|
||||||
|
async fn sqlite_rowid_alias() {
|
||||||
let db = super::new_initialized_memory_db().await;
|
let db = super::new_initialized_memory_db().await;
|
||||||
|
|
||||||
// Starts at 1 and increments
|
// Starts at 1 and increments
|
||||||
@@ -98,7 +127,7 @@ mod entity_tests {
|
|||||||
let model = ActiveModel {
|
let model = ActiveModel {
|
||||||
id: Set(Id::from_raw(i)),
|
id: Set(Id::from_raw(i)),
|
||||||
};
|
};
|
||||||
model.delete(&db).await.expect("delete");
|
_ = model.delete(&db).await.expect("delete");
|
||||||
let model = ActiveModel { id: NotSet };
|
let model = ActiveModel { id: NotSet };
|
||||||
let model = model.insert(&db).await.expect("insert");
|
let model = model.insert(&db).await.expect("insert");
|
||||||
assert_eq!(model.id.into_raw(), i);
|
assert_eq!(model.id.into_raw(), i);
|
||||||
@@ -156,6 +185,22 @@ mod entity_tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[expect(
|
||||||
|
clippy::unused_async_trait_impl,
|
||||||
|
reason = "sea_orm::model generates unpolled futures"
|
||||||
|
)]
|
||||||
|
#[expect(
|
||||||
|
clippy::same_name_method,
|
||||||
|
reason = "sea_orm::model generates the same method names"
|
||||||
|
)]
|
||||||
|
#[expect(
|
||||||
|
clippy::allow_attributes,
|
||||||
|
reason = "under different conditions sea_orm::model generates different code"
|
||||||
|
)]
|
||||||
|
#[allow(
|
||||||
|
clippy::future_not_send,
|
||||||
|
reason = "sea_orm::model generates un-send futures"
|
||||||
|
)]
|
||||||
mod nocase_test {
|
mod nocase_test {
|
||||||
use sea_orm::ActiveValue::Set;
|
use sea_orm::ActiveValue::Set;
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
@@ -163,17 +208,20 @@ mod entity_tests {
|
|||||||
#[sea_orm::model]
|
#[sea_orm::model]
|
||||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "nocase_test")]
|
#[sea_orm(table_name = "nocase_test")]
|
||||||
pub struct Model {
|
struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
id: i64,
|
id: i64,
|
||||||
case: String,
|
case: String,
|
||||||
#[sea_orm(extra = "COLLATE NOCASE")]
|
#[sea_orm(extra = "COLLATE NOCASE")]
|
||||||
nocase: String,
|
nocase: String,
|
||||||
}
|
}
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
|
#[cfg(not(miri))]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_sqlite_case_insensitive_string() {
|
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||||
|
async fn sqlite_case_insensitive_string() {
|
||||||
let db = super::new_initialized_memory_db().await;
|
let db = super::new_initialized_memory_db().await;
|
||||||
|
|
||||||
let i = 50;
|
let i = 50;
|
||||||
@@ -185,7 +233,7 @@ mod entity_tests {
|
|||||||
case: Set("abcd".to_owned()),
|
case: Set("abcd".to_owned()),
|
||||||
nocase: Set("abcd".to_owned()),
|
nocase: Set("abcd".to_owned()),
|
||||||
};
|
};
|
||||||
model.insert(&db).await.expect("insert");
|
drop(model.insert(&db).await.expect("insert"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query by uppercase string
|
// Query by uppercase string
|
||||||
@@ -216,7 +264,7 @@ mod entity_tests {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use sea_orm::ActiveValue::{NotSet, Set};
|
use sea_orm::ActiveValue::{NotSet, Set};
|
||||||
use sea_orm::entity::prelude::*;
|
use sea_orm::entity::prelude::*;
|
||||||
use sea_orm::{ConnectOptions, Database, DatabaseConnection};
|
use sea_orm::{ConnectOptions, Database};
|
||||||
use sea_orm_migration::async_trait::async_trait;
|
use sea_orm_migration::async_trait::async_trait;
|
||||||
use sea_orm_migration::prelude::*;
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
@@ -224,14 +272,21 @@ mod tests {
|
|||||||
|
|
||||||
use super::{sqlite_case_insensitive_string, sqlite_rowid_alias};
|
use super::{sqlite_case_insensitive_string, sqlite_rowid_alias};
|
||||||
|
|
||||||
|
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||||
async fn new_memory_db() -> DatabaseConnection {
|
async fn new_memory_db() -> DatabaseConnection {
|
||||||
let options = ConnectOptions::new("sqlite::memory:");
|
let options = ConnectOptions::new("sqlite::memory:");
|
||||||
Database::connect(options).await.expect("Database::connect")
|
Database::connect(options).await.expect("Database::connect")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(miri))]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
#[expect(unused_qualifications, reason = "derive macro qualifies everything")]
|
||||||
|
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||||
|
#[expect(clippy::default_numeric_fallback, reason = "numbers are not ambiguous")]
|
||||||
|
#[expect(clippy::redundant_test_prefix, reason = "name collision")]
|
||||||
async fn test_sqlite_rowid_alias() {
|
async fn test_sqlite_rowid_alias() {
|
||||||
struct Migrator;
|
struct Migrator;
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl MigratorTrait for Migrator {
|
impl MigratorTrait for Migrator {
|
||||||
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
||||||
vec![Box::new(Migration)]
|
vec![Box::new(Migration)]
|
||||||
@@ -246,7 +301,12 @@ mod tests {
|
|||||||
#[derive(DeriveMigrationName)]
|
#[derive(DeriveMigrationName)]
|
||||||
struct Migration;
|
struct Migration;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl MigrationTrait for Migration {
|
impl MigrationTrait for Migration {
|
||||||
|
#[expect(
|
||||||
|
elided_lifetimes_in_paths,
|
||||||
|
reason = "async_trait causes lifetimes to be strange"
|
||||||
|
)]
|
||||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
manager
|
manager
|
||||||
.create_table(
|
.create_table(
|
||||||
@@ -263,17 +323,21 @@ mod tests {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "test_table")]
|
#[sea_orm(table_name = "test_table")]
|
||||||
pub struct Model {
|
#[expect(clippy::use_self, reason = "derive macros cause Self to be invalid")]
|
||||||
|
struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
id: Id<Model>,
|
id: Id<Model>,
|
||||||
}
|
}
|
||||||
#[derive(Debug, EnumIter)]
|
#[derive(Debug, EnumIter)]
|
||||||
pub enum Relation {}
|
enum Relation {}
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl RelationTrait for Relation {
|
impl RelationTrait for Relation {
|
||||||
|
#[expect(clippy::panic, reason = "no relation")]
|
||||||
fn def(&self) -> RelationDef {
|
fn def(&self) -> RelationDef {
|
||||||
panic!("No RelationDef")
|
panic!("No RelationDef")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
let db = new_memory_db().await;
|
let db = new_memory_db().await;
|
||||||
@@ -291,7 +355,7 @@ mod tests {
|
|||||||
let model = ActiveModel {
|
let model = ActiveModel {
|
||||||
id: Set(Id::from_raw(i)),
|
id: Set(Id::from_raw(i)),
|
||||||
};
|
};
|
||||||
model.delete(&db).await.expect("delete");
|
_ = model.delete(&db).await.expect("delete");
|
||||||
let model = ActiveModel { id: NotSet };
|
let model = ActiveModel { id: NotSet };
|
||||||
let model = model.insert(&db).await.expect("insert");
|
let model = model.insert(&db).await.expect("insert");
|
||||||
assert_eq!(model.id.into_raw(), i);
|
assert_eq!(model.id.into_raw(), i);
|
||||||
@@ -348,9 +412,14 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(miri))]
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
#[expect(unused_qualifications, reason = "derive macro qualifies everything")]
|
||||||
|
#[expect(clippy::missing_panics_doc, reason = "unit test")]
|
||||||
|
#[expect(clippy::redundant_test_prefix, reason = "name collision")]
|
||||||
async fn test_sqlite_case_insensitive_string() {
|
async fn test_sqlite_case_insensitive_string() {
|
||||||
struct Migrator;
|
struct Migrator;
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl MigratorTrait for Migrator {
|
impl MigratorTrait for Migrator {
|
||||||
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
|
||||||
vec![Box::new(Migration)]
|
vec![Box::new(Migration)]
|
||||||
@@ -366,7 +435,12 @@ mod tests {
|
|||||||
#[derive(DeriveMigrationName)]
|
#[derive(DeriveMigrationName)]
|
||||||
struct Migration;
|
struct Migration;
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl MigrationTrait for Migration {
|
impl MigrationTrait for Migration {
|
||||||
|
#[expect(
|
||||||
|
elided_lifetimes_in_paths,
|
||||||
|
reason = "async_trait causes lifetimes to be strange"
|
||||||
|
)]
|
||||||
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
|
||||||
manager
|
manager
|
||||||
.create_table(
|
.create_table(
|
||||||
@@ -384,18 +458,21 @@ mod tests {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, DeriveEntityModel)]
|
#[derive(Debug, Clone, DeriveEntityModel)]
|
||||||
#[sea_orm(table_name = "test_table")]
|
#[sea_orm(table_name = "test_table")]
|
||||||
pub struct Model {
|
struct Model {
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
#[sea_orm(primary_key, auto_increment = false)]
|
||||||
id: i64,
|
id: i64,
|
||||||
ci_str: String,
|
ci_str: String,
|
||||||
}
|
}
|
||||||
#[derive(Debug, EnumIter)]
|
#[derive(Debug, EnumIter)]
|
||||||
pub enum Relation {}
|
enum Relation {}
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl RelationTrait for Relation {
|
impl RelationTrait for Relation {
|
||||||
|
#[expect(clippy::panic, reason = "no relation")]
|
||||||
fn def(&self) -> RelationDef {
|
fn def(&self) -> RelationDef {
|
||||||
panic!("No RelationDef")
|
panic!("No RelationDef")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[expect(clippy::missing_trait_methods, reason = "accept default methods")]
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
let db = new_memory_db().await;
|
let db = new_memory_db().await;
|
||||||
@@ -409,7 +486,7 @@ mod tests {
|
|||||||
id: Set(i),
|
id: Set(i),
|
||||||
ci_str: Set("abcd".to_owned()),
|
ci_str: Set("abcd".to_owned()),
|
||||||
};
|
};
|
||||||
model.insert(&db).await.expect("insert");
|
drop(model.insert(&db).await.expect("insert"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Query by uppercase string
|
// Query by uppercase string
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
set minimum-version := '1.55.0'
|
||||||
|
set default-list
|
||||||
|
|
||||||
|
alias update := update-min
|
||||||
|
|
||||||
|
all:
|
||||||
|
just all-min
|
||||||
|
just all-max
|
||||||
|
all-min: update-min build lint test example doc format
|
||||||
|
all-max: update-max build lint test example doc format
|
||||||
|
|
||||||
|
update-min:
|
||||||
|
cargo +nightly update -Z direct-minimal-versions
|
||||||
|
|
||||||
|
update-max:
|
||||||
|
cargo update
|
||||||
|
|
||||||
|
build:
|
||||||
|
cargo hack --feature-powerset build
|
||||||
|
|
||||||
|
lint:
|
||||||
|
cargo hack --feature-powerset clippy -- -D warnings
|
||||||
|
cargo hack --feature-powerset clippy --tests -- -D warnings
|
||||||
|
|
||||||
|
test:
|
||||||
|
cargo hack --feature-powerset test
|
||||||
|
cargo +1.94 hack --feature-powerset test
|
||||||
|
cargo +nightly hack --feature-powerset miri test
|
||||||
|
|
||||||
|
example:
|
||||||
|
cargo run --example=migrations --features=sqlite
|
||||||
|
|
||||||
|
doc *args:
|
||||||
|
RUSTDOCFLAGS="--cfg docsrs -D warnings" cargo +nightly doc \
|
||||||
|
--workspace --all-features --document-private-items \
|
||||||
|
--lib --bins --no-deps --locked \
|
||||||
|
{{ args }}
|
||||||
|
|
||||||
|
format:
|
||||||
|
cargo fmt --check
|
||||||
|
|
||||||
|
install:
|
||||||
|
cargo install --path crates/lint-policy-cli
|
||||||
+1
-1
@@ -1,2 +1,2 @@
|
|||||||
[toolchain]
|
[toolchain]
|
||||||
channel = "stable"
|
channel = "1.98"
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
//! SeaORM column types for enforcing data consistency
|
|
||||||
|
|
||||||
pub mod duration;
|
|
||||||
pub mod id;
|
|
||||||
pub mod ip;
|
|
||||||
pub mod path;
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
use sea_orm::sea_query::{IntoColumnRef, OnConflict};
|
|
||||||
use sea_orm::{ActiveModelTrait, EntityTrait, Insert, Iterable};
|
|
||||||
|
|
||||||
/// This trait add a method on [Insert] to allow for upsert behavior
|
|
||||||
pub trait UpsertTrait: private::Sealed {
|
|
||||||
/// Set ON CONFLICT on primary key to update all other columns
|
|
||||||
fn on_conflict_upsert(self) -> Self;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn primary_key_iter<A: ActiveModelTrait>()
|
|
||||||
-> impl Iterator<Item = <A::Entity as EntityTrait>::PrimaryKey> {
|
|
||||||
<A::Entity as EntityTrait>::PrimaryKey::iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn column_iter<A: ActiveModelTrait>() -> impl Iterator<Item = <A::Entity as EntityTrait>::Column> {
|
|
||||||
<<A as ActiveModelTrait>::Entity as EntityTrait>::Column::iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<A: ActiveModelTrait> UpsertTrait for Insert<A> {
|
|
||||||
fn on_conflict_upsert(self) -> Self {
|
|
||||||
self.on_conflict(
|
|
||||||
OnConflict::columns(primary_key_iter::<A>())
|
|
||||||
.update_columns(column_iter::<A>().filter(|col| {
|
|
||||||
!primary_key_iter::<A>().any(|pk| pk.into_column_ref() == col.into_column_ref())
|
|
||||||
}))
|
|
||||||
.to_owned(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod private {
|
|
||||||
pub trait Sealed {}
|
|
||||||
impl<A: ::sea_orm::ActiveModelTrait> Sealed for ::sea_orm::Insert<A> {}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
toml-version = "v1.0.0"
|
|
||||||
|
|
||||||
[format.rules]
|
|
||||||
indent-style = "tab"
|
|
||||||
indent-width = 4
|
|
||||||
Reference in New Issue
Block a user