Compare commits
10 Commits
3ed3e079f2
...
64e0b4e536
| Author | SHA1 | Date | |
|---|---|---|---|
| 64e0b4e536 | |||
| a94f8e2bfd | |||
| 2b668ba89f | |||
| 1a745ff17f | |||
| ed640ad20d | |||
| 8f509b1e2c | |||
| 3c58ffbb36 | |||
| 68d77a23e4 | |||
| fe423a199b | |||
| 8fd390fe18 |
326
Cargo.lock
generated
326
Cargo.lock
generated
@@ -108,15 +108,6 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atomic"
|
|
||||||
version = "0.6.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8d818003e740b63afc82337e3160717f4f63078720a810b7b903e70a5d1d2994"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -180,12 +171,6 @@ version = "3.17.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bytemuck"
|
|
||||||
version = "1.22.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -200,9 +185,9 @@ checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.2.18"
|
version = "1.2.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "525046617d8376e3db1deffb079e91cef90a89fc3ca5c185bbf8c9ecdd15cd5c"
|
checksum = "8e3a13707ac958681c13b39b458c073d0d9bc8a22cb1b2f4c8e55eb72c13f362"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"shlex",
|
"shlex",
|
||||||
]
|
]
|
||||||
@@ -215,9 +200,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "clap"
|
||||||
version = "4.5.35"
|
version = "4.5.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944"
|
checksum = "2df961d8c8a0d08aa9945718ccf584145eee3f3aa06cddbeac12933781102e04"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap_builder",
|
"clap_builder",
|
||||||
"clap_derive",
|
"clap_derive",
|
||||||
@@ -225,9 +210,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap_builder"
|
name = "clap_builder"
|
||||||
version = "4.5.35"
|
version = "4.5.36"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9"
|
checksum = "132dbda40fb6753878316a489d5a1242a8ef2f0d9e47ba01c951ea8aa7d013a5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anstream",
|
"anstream",
|
||||||
"anstyle",
|
"anstyle",
|
||||||
@@ -428,7 +413,6 @@ dependencies = [
|
|||||||
"ident_case",
|
"ident_case",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -454,37 +438,6 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_builder"
|
|
||||||
version = "0.20.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
|
||||||
dependencies = [
|
|
||||||
"derive_builder_macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_builder_core"
|
|
||||||
version = "0.20.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
|
||||||
dependencies = [
|
|
||||||
"darling",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "derive_builder_macro"
|
|
||||||
version = "0.20.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
|
||||||
dependencies = [
|
|
||||||
"derive_builder_core",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "detect-lang"
|
name = "detect-lang"
|
||||||
version = "0.1.5"
|
version = "0.1.5"
|
||||||
@@ -595,19 +548,6 @@ version = "2.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "figment"
|
|
||||||
version = "0.10.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8cb01cd46b0cf372153850f4c6c272d9cbea2da513e07538405148f95bd789f3"
|
|
||||||
dependencies = [
|
|
||||||
"atomic",
|
|
||||||
"serde",
|
|
||||||
"serde_yaml",
|
|
||||||
"uncased",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "flume"
|
name = "flume"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
@@ -783,22 +723,6 @@ version = "0.28.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "handlebars"
|
|
||||||
version = "6.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098"
|
|
||||||
dependencies = [
|
|
||||||
"derive_builder",
|
|
||||||
"log",
|
|
||||||
"num-order",
|
|
||||||
"pest",
|
|
||||||
"pest_derive",
|
|
||||||
"serde",
|
|
||||||
"serde_json",
|
|
||||||
"thiserror",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.2"
|
version = "0.15.2"
|
||||||
@@ -1147,9 +1071,9 @@ checksum = "29fc123b2f6600099ca18248f69e3ee02b09c4188c0d98e9a690d90dec3e408a"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linux-raw-sys"
|
name = "linux-raw-sys"
|
||||||
version = "0.9.3"
|
version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413"
|
checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "litemap"
|
name = "litemap"
|
||||||
@@ -1198,6 +1122,23 @@ version = "2.7.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memo-map"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minijinja"
|
||||||
|
version = "2.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "98642a6dfca91122779a307b77cd07a4aa951fbe32232aaf5bad9febc66be754"
|
||||||
|
dependencies = [
|
||||||
|
"memo-map",
|
||||||
|
"self_cell",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "miniz_oxide"
|
name = "miniz_oxide"
|
||||||
version = "0.7.4"
|
version = "0.7.4"
|
||||||
@@ -1286,21 +1227,6 @@ dependencies = [
|
|||||||
"num-traits",
|
"num-traits",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-modular"
|
|
||||||
version = "0.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-order"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6"
|
|
||||||
dependencies = [
|
|
||||||
"num-modular",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -1379,6 +1305,12 @@ version = "1.0.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef"
|
checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pathdiff"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pem-rfc7468"
|
name = "pem-rfc7468"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@@ -1394,51 +1326,6 @@ version = "2.3.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pest"
|
|
||||||
version = "2.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "198db74531d58c70a361c42201efde7e2591e976d518caf7662a47dc5720e7b6"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
"thiserror",
|
|
||||||
"ucd-trie",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pest_derive"
|
|
||||||
version = "2.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d725d9cfd79e87dccc9341a2ef39d1b6f6353d68c4b33c177febbe1a402c97c5"
|
|
||||||
dependencies = [
|
|
||||||
"pest",
|
|
||||||
"pest_generator",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pest_generator"
|
|
||||||
version = "2.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db7d01726be8ab66ab32f9df467ae8b1148906685bbe75c82d1e65d7f5b3f841"
|
|
||||||
dependencies = [
|
|
||||||
"pest",
|
|
||||||
"pest_meta",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pest_meta"
|
|
||||||
version = "2.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f9f832470494906d1fca5329f8ab5791cc60beb230c74815dff541cbd2b5ca0"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
"pest",
|
|
||||||
"sha2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-project-lite"
|
name = "pin-project-lite"
|
||||||
version = "0.2.16"
|
version = "0.2.16"
|
||||||
@@ -1653,9 +1540,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sea-orm-codegen"
|
name = "sea-orm-codegen"
|
||||||
version = "1.1.8"
|
version = "1.1.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "85fc7ff538b0fef5061e52f8a67ddf41f866d12e64115df6fda753b334d6d88e"
|
checksum = "cf05ea0808639cc78769a8459b475dcb6ae642366b5df84612914efa089be3a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heck 0.4.1",
|
"heck 0.4.1",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
@@ -1675,23 +1562,22 @@ dependencies = [
|
|||||||
"color-eyre",
|
"color-eyre",
|
||||||
"comfy-table",
|
"comfy-table",
|
||||||
"comment-parser",
|
"comment-parser",
|
||||||
"figment",
|
|
||||||
"handlebars",
|
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"include_dir",
|
"include_dir",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"inquire",
|
"inquire",
|
||||||
|
"minijinja",
|
||||||
"path-clean",
|
"path-clean",
|
||||||
|
"pathdiff",
|
||||||
"quote",
|
"quote",
|
||||||
"sea-orm-codegen",
|
"sea-orm-codegen",
|
||||||
"sea-schema",
|
"sea-schema",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-inline-default",
|
"serde_merge",
|
||||||
"serde_json",
|
|
||||||
"serde_yaml",
|
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"syn",
|
"syn",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
"toml",
|
||||||
"toml_edit",
|
"toml_edit",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
@@ -1729,7 +1615,7 @@ dependencies = [
|
|||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
"thiserror",
|
"thiserror 2.0.12",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1757,6 +1643,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "self_cell"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f7d95a54511e0c7be3f51e8867aa8cf35148d7b9445d44de2f943e2b206e749"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
@@ -1766,17 +1658,6 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde-inline-default"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "59fb1bedd774187d304179493b0d3c41fbe97b04b14305363f68d2bdf5e47cb9"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.219"
|
version = "1.0.219"
|
||||||
@@ -1800,6 +1681,17 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_merge"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "606e91878516232ac3b16c12e063d4468d762f16d77e7aef14a1f2326c5f409b"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_spanned"
|
name = "serde_spanned"
|
||||||
version = "0.6.8"
|
version = "0.6.8"
|
||||||
@@ -1821,19 +1713,6 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_yaml"
|
|
||||||
version = "0.9.34+deprecated"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap",
|
|
||||||
"itoa",
|
|
||||||
"ryu",
|
|
||||||
"serde",
|
|
||||||
"unsafe-libyaml",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -1960,9 +1839,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx"
|
name = "sqlx"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f"
|
checksum = "14e22987355fbf8cfb813a0cf8cd97b1b4ec834b94dbd759a9e8679d41fabe83"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-macros",
|
"sqlx-macros",
|
||||||
@@ -1973,10 +1852,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-core"
|
name = "sqlx-core"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0"
|
checksum = "55c4720d7d4cd3d5b00f61d03751c685ad09c33ae8290c8a2c11335e0604300b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
"crc",
|
"crc",
|
||||||
"crossbeam-queue",
|
"crossbeam-queue",
|
||||||
@@ -1997,7 +1877,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"thiserror",
|
"thiserror 2.0.12",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -2006,9 +1886,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-macros"
|
name = "sqlx-macros"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310"
|
checksum = "175147fcb75f353ac7675509bc58abb2cb291caf0fd24a3623b8f7e3eb0a754b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -2019,9 +1899,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-macros-core"
|
name = "sqlx-macros-core"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad"
|
checksum = "1cde983058e53bfa75998e1982086c5efe3c370f3250bf0357e344fa3352e32b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"dotenvy",
|
"dotenvy",
|
||||||
"either",
|
"either",
|
||||||
@@ -2045,9 +1925,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-mysql"
|
name = "sqlx-mysql"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233"
|
checksum = "847d2e5393a4f39e47e4f36cab419709bc2b83cbe4223c60e86e1471655be333"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64",
|
"base64",
|
||||||
@@ -2080,16 +1960,16 @@ dependencies = [
|
|||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror",
|
"thiserror 2.0.12",
|
||||||
"tracing",
|
"tracing",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-postgres"
|
name = "sqlx-postgres"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613"
|
checksum = "cc35947a541b9e0a2e3d85da444f1c4137c13040267141b208395a0d0ca4659f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"base64",
|
"base64",
|
||||||
@@ -2117,16 +1997,16 @@ dependencies = [
|
|||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"stringprep",
|
"stringprep",
|
||||||
"thiserror",
|
"thiserror 2.0.12",
|
||||||
"tracing",
|
"tracing",
|
||||||
"whoami",
|
"whoami",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlx-sqlite"
|
name = "sqlx-sqlite"
|
||||||
version = "0.8.3"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540"
|
checksum = "6c48291dac4e5ed32da0927a0b981788be65674aeb62666d19873ab4289febde"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atoi",
|
"atoi",
|
||||||
"flume",
|
"flume",
|
||||||
@@ -2141,6 +2021,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
|
"thiserror 2.0.12",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
@@ -2209,13 +2090,33 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||||
|
dependencies = [
|
||||||
|
"thiserror-impl 1.0.69",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.12"
|
version = "2.0.12"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl",
|
"thiserror-impl 2.0.12",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "thiserror-impl"
|
||||||
|
version = "1.0.69"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2304,6 +2205,18 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.8.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.8"
|
version = "0.6.8"
|
||||||
@@ -2404,21 +2317,6 @@ version = "1.18.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ucd-trie"
|
|
||||||
version = "0.1.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "uncased"
|
|
||||||
version = "0.9.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e1b88fcfe09e89d3866a5c11019378088af2d24c3fbd4f0543f96b479ec90697"
|
|
||||||
dependencies = [
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-bidi"
|
name = "unicode-bidi"
|
||||||
version = "0.3.18"
|
version = "0.3.18"
|
||||||
@@ -2464,12 +2362,6 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unsafe-libyaml"
|
|
||||||
version = "0.2.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
version = "2.5.4"
|
version = "2.5.4"
|
||||||
@@ -2787,9 +2679,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "winnow"
|
||||||
version = "0.7.4"
|
version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36"
|
checksum = "63d3fcd9bba44b03821e7d699eeee959f3126dcc4aa8e4ae18ec617c2a5cea10"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|||||||
10
Cargo.toml
10
Cargo.toml
@@ -1,3 +1,4 @@
|
|||||||
|
[workspace]
|
||||||
[package]
|
[package]
|
||||||
name = "sea-orm-generator"
|
name = "sea-orm-generator"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -10,23 +11,22 @@ clap = { version = "4.5.32", features = ["derive", "env"] }
|
|||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
comfy-table = { version = "7.1.4", default-features = false }
|
comfy-table = { version = "7.1.4", default-features = false }
|
||||||
comment-parser = "0.1.0"
|
comment-parser = "0.1.0"
|
||||||
figment = { version = "0.10.19", features = ["yaml"] }
|
|
||||||
handlebars = "6.3.2"
|
|
||||||
heck = "0.5.0"
|
heck = "0.5.0"
|
||||||
include_dir = "0.7.4"
|
include_dir = "0.7.4"
|
||||||
indicatif = "0.17.11"
|
indicatif = "0.17.11"
|
||||||
inquire = "0.7.5"
|
inquire = "0.7.5"
|
||||||
|
minijinja = { version = "2.9.0", features = ["loader"] }
|
||||||
path-clean = "1.0.1"
|
path-clean = "1.0.1"
|
||||||
|
pathdiff = "0.2.3"
|
||||||
quote = "1.0.40"
|
quote = "1.0.40"
|
||||||
sea-orm-codegen = "1.1.8"
|
sea-orm-codegen = "1.1.8"
|
||||||
sea-schema = { version = "0.16.1", features = ["sqlx-all"] }
|
sea-schema = { version = "0.16.1", features = ["sqlx-all"] }
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
serde-inline-default = "0.2.3"
|
serde_merge = "0.1.3"
|
||||||
serde_json = "1.0.140"
|
|
||||||
serde_yaml = "0.9.34"
|
|
||||||
sqlx = { version = "0.8.3", features = ["mysql", "postgres", "sqlite", "runtime-tokio"] }
|
sqlx = { version = "0.8.3", features = ["mysql", "postgres", "sqlite", "runtime-tokio"] }
|
||||||
syn = { version = "2.0.100", features = ["extra-traits", "full"] }
|
syn = { version = "2.0.100", features = ["extra-traits", "full"] }
|
||||||
tokio = { version = "1.44.1", features = ["full"] }
|
tokio = { version = "1.44.1", features = ["full"] }
|
||||||
|
toml = "0.8.20"
|
||||||
toml_edit = { version = "0.22.24", features = ["serde"] }
|
toml_edit = { version = "0.22.24", features = ["serde"] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
inherit (pkgs) lib;
|
inherit (pkgs) lib;
|
||||||
craneLib = (crane.mkLib pkgs).overrideToolchain (
|
craneLib = (crane.mkLib pkgs).overrideToolchain (
|
||||||
p:
|
p:
|
||||||
p.rust-bin.stable.latest.default.override {
|
p.rust-bin.nightly.latest.default.override {
|
||||||
extensions = [ "llvm-tools-preview" ];
|
extensions = [ "llvm-tools-preview" ];
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -150,6 +150,7 @@
|
|||||||
cargo-llvm-cov
|
cargo-llvm-cov
|
||||||
cargo-audit
|
cargo-audit
|
||||||
cargo-tarpaulin
|
cargo-tarpaulin
|
||||||
|
cargo-udeps
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,19 @@
|
|||||||
# This file is used to configure the SeaORM generator.
|
# This file is used to configure the SeaORM generator.
|
||||||
[modules.discovery]
|
[modules.discovery]
|
||||||
enable = true
|
enable = true
|
||||||
[modules.discovery.filter]
|
|
||||||
include_hidden = false
|
|
||||||
skip_seaql_migrations = true
|
|
||||||
|
|
||||||
[modules.sea_orm]
|
[modules.sea_orm]
|
||||||
enable = true
|
enable = true
|
||||||
path = "./tests/src/models/_entities"
|
path = "./tests/src/models/_entities"
|
||||||
|
|
||||||
|
[modules.template]
|
||||||
|
enable = true
|
||||||
|
[modules.template.tables]
|
||||||
|
|
||||||
|
[modules.model]
|
||||||
|
enable = true
|
||||||
|
prelude = true
|
||||||
|
path = "./tests/src/models"
|
||||||
|
|
||||||
|
[modules.annotate]
|
||||||
|
enable = true
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
processes = {
|
processes = {
|
||||||
frontend = {
|
frontend = {
|
||||||
command = ''
|
command = ''
|
||||||
RUST_LOG=debug,sqlx=warn ${pkgs.cargo-watch}/bin/cargo-watch -x 'run'
|
RUST_LOG=debug,sqlx=warn ${pkgs.cargo-watch}/bin/cargo-watch -i tests/src -x 'run && cat tests/src/models/user.rs'
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct DbConfig {
|
|
||||||
pub database_schema: Option<String>,
|
|
||||||
pub max_connections: u32,
|
|
||||||
pub acquire_timeout: u64,
|
|
||||||
}
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
pub mod db;
|
|
||||||
pub mod output;
|
|
||||||
pub mod sea_orm_config;
|
|
||||||
pub mod template;
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use db::DbConfig;
|
|
||||||
use output::{OutputCommentConfig, OutputConfig, OutputModelConfig};
|
|
||||||
use sea_orm_config::{
|
|
||||||
DateTimeCrate, EntityFormat, Prelude, SeaOrmConfig, SeaOrmEntityConfig,
|
|
||||||
SeaOrmExtraAttributesConfig, SeaOrmExtraDerivesConfig, SeaOrmSerdeConfig, SeaOrmTableConfig,
|
|
||||||
SerdeEnable,
|
|
||||||
};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use serde_yaml::Mapping;
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct Config {
|
|
||||||
pub db: DbConfig,
|
|
||||||
pub sea_orm: SeaOrmConfig,
|
|
||||||
pub output: OutputConfig,
|
|
||||||
pub templates: Option<Mapping>,
|
|
||||||
pub templates_dir: Option<PathBuf>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Config {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
db: DbConfig {
|
|
||||||
database_schema: None,
|
|
||||||
max_connections: 10,
|
|
||||||
acquire_timeout: 5,
|
|
||||||
},
|
|
||||||
sea_orm: SeaOrmConfig {
|
|
||||||
prelude: Prelude::Enabled,
|
|
||||||
serde: SeaOrmSerdeConfig {
|
|
||||||
enable: SerdeEnable::None,
|
|
||||||
skip_deserializing_primary_key: false,
|
|
||||||
skip_hidden_column: false,
|
|
||||||
},
|
|
||||||
entity: SeaOrmEntityConfig {
|
|
||||||
format: EntityFormat::Compact,
|
|
||||||
tables: SeaOrmTableConfig {
|
|
||||||
include_hidden: false,
|
|
||||||
skip_seaql_migrations: true,
|
|
||||||
table_config: None,
|
|
||||||
},
|
|
||||||
extra_derives: SeaOrmExtraDerivesConfig {
|
|
||||||
model: Vec::new(),
|
|
||||||
eenum: Vec::new(),
|
|
||||||
},
|
|
||||||
extra_attributes: SeaOrmExtraAttributesConfig {
|
|
||||||
model: Vec::new(),
|
|
||||||
eenum: Vec::new(),
|
|
||||||
},
|
|
||||||
date_time_crate: DateTimeCrate::Chrono,
|
|
||||||
with_copy_enums: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
output: OutputConfig {
|
|
||||||
path: PathBuf::from("./src/"),
|
|
||||||
models: OutputModelConfig {
|
|
||||||
comment: OutputCommentConfig {
|
|
||||||
max_width: None,
|
|
||||||
table_name: true,
|
|
||||||
column_name: true,
|
|
||||||
column_db_type: true,
|
|
||||||
column_rust_type: true,
|
|
||||||
column_attributes: true,
|
|
||||||
column_exclude_attributes: Vec::new(),
|
|
||||||
enable: true,
|
|
||||||
column_info: true,
|
|
||||||
ignore_errors: false,
|
|
||||||
},
|
|
||||||
enable: true,
|
|
||||||
entities: String::from("_entities"),
|
|
||||||
prelude: true,
|
|
||||||
path: PathBuf::from("./models"),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
templates: None,
|
|
||||||
templates_dir: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct OutputConfig {
|
|
||||||
pub path: PathBuf,
|
|
||||||
pub models: OutputModelConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct OutputModelConfig {
|
|
||||||
pub prelude: bool,
|
|
||||||
pub enable: bool,
|
|
||||||
pub path: PathBuf,
|
|
||||||
pub comment: OutputCommentConfig,
|
|
||||||
pub entities: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct OutputCommentConfig {
|
|
||||||
pub enable: bool,
|
|
||||||
pub max_width: Option<u16>,
|
|
||||||
pub table_name: bool,
|
|
||||||
pub column_info: bool,
|
|
||||||
pub column_name: bool,
|
|
||||||
pub column_db_type: bool,
|
|
||||||
pub column_rust_type: bool,
|
|
||||||
pub column_attributes: bool,
|
|
||||||
pub column_exclude_attributes: Vec<String>,
|
|
||||||
pub ignore_errors: bool,
|
|
||||||
}
|
|
||||||
@@ -1,233 +0,0 @@
|
|||||||
use serde::{Deserialize, Deserializer, Serialize};
|
|
||||||
use serde_yaml::Value;
|
|
||||||
|
|
||||||
use sea_orm_codegen::{
|
|
||||||
DateTimeCrate as CodegenDateTimeCrate, EntityWriterContext, WithPrelude, WithSerde,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::Config;
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum EntityFormat {
|
|
||||||
Expanded,
|
|
||||||
Compact,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
#[serde(rename_all = "snake_case")]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum TableConfig {
|
|
||||||
Specific { specific: Vec<String> },
|
|
||||||
Exclude { exclude: Vec<String> },
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum SerdeEnable {
|
|
||||||
Both,
|
|
||||||
Serialize,
|
|
||||||
Deserialize,
|
|
||||||
None,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum Prelude {
|
|
||||||
Enabled,
|
|
||||||
Disabled,
|
|
||||||
AllowUnusedImports,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for SerdeEnable {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<SerdeEnable, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value = Value::deserialize(deserializer)?;
|
|
||||||
|
|
||||||
match value {
|
|
||||||
Value::String(s) if s == "serialize" => Ok(SerdeEnable::Serialize),
|
|
||||||
Value::String(s) if s == "deserialize" => Ok(SerdeEnable::Deserialize),
|
|
||||||
Value::Bool(true) => Ok(SerdeEnable::Both),
|
|
||||||
Value::Bool(false) => Ok(SerdeEnable::None),
|
|
||||||
_ => Err(serde::de::Error::custom(
|
|
||||||
"expected 'serialize', 'deserialize', 'true' or 'false'",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Serialize for SerdeEnable {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
match self {
|
|
||||||
SerdeEnable::Both => serializer.serialize_bool(true),
|
|
||||||
SerdeEnable::Serialize => serializer.serialize_str("serialize"),
|
|
||||||
SerdeEnable::Deserialize => serializer.serialize_str("deserialize"),
|
|
||||||
SerdeEnable::None => serializer.serialize_bool(false),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl<'de> Deserialize<'de> for Prelude {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Prelude, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let value = Value::deserialize(deserializer)?;
|
|
||||||
|
|
||||||
match value {
|
|
||||||
Value::Bool(true) => Ok(Prelude::Enabled),
|
|
||||||
Value::Bool(false) => Ok(Prelude::Disabled),
|
|
||||||
Value::String(s) if s == "allow_unused_imports" => Ok(Prelude::AllowUnusedImports),
|
|
||||||
_ => Err(serde::de::Error::custom(
|
|
||||||
"expected 'true', 'false', or 'allow_unused_imports'",
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Serialize for Prelude {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: serde::Serializer,
|
|
||||||
{
|
|
||||||
match self {
|
|
||||||
Prelude::Enabled => serializer.serialize_bool(true),
|
|
||||||
Prelude::Disabled => serializer.serialize_bool(false),
|
|
||||||
Prelude::AllowUnusedImports => serializer.serialize_str("allow_unused_imports"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct SeaOrmConfig {
|
|
||||||
pub prelude: Prelude,
|
|
||||||
pub serde: SeaOrmSerdeConfig,
|
|
||||||
pub entity: SeaOrmEntityConfig,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct SeaOrmSerdeConfig {
|
|
||||||
pub enable: SerdeEnable,
|
|
||||||
pub skip_deserializing_primary_key: bool,
|
|
||||||
pub skip_hidden_column: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct SeaOrmEntityConfig {
|
|
||||||
pub format: EntityFormat,
|
|
||||||
pub tables: SeaOrmTableConfig,
|
|
||||||
pub extra_derives: SeaOrmExtraDerivesConfig,
|
|
||||||
pub extra_attributes: SeaOrmExtraAttributesConfig,
|
|
||||||
pub date_time_crate: DateTimeCrate,
|
|
||||||
pub with_copy_enums: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct SeaOrmTableConfig {
|
|
||||||
pub include_hidden: bool,
|
|
||||||
pub skip_seaql_migrations: bool,
|
|
||||||
#[serde(flatten)]
|
|
||||||
pub table_config: Option<TableConfig>,
|
|
||||||
}
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct SeaOrmExtraDerivesConfig {
|
|
||||||
pub model: Vec<String>,
|
|
||||||
#[serde(rename = "enum")]
|
|
||||||
pub eenum: Vec<String>,
|
|
||||||
}
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
pub struct SeaOrmExtraAttributesConfig {
|
|
||||||
pub model: Vec<String>,
|
|
||||||
#[serde(rename = "enum")]
|
|
||||||
pub eenum: Vec<String>,
|
|
||||||
}
|
|
||||||
#[derive(Deserialize, Serialize, Debug, Clone)]
|
|
||||||
#[serde(rename_all = "lowercase")]
|
|
||||||
pub enum DateTimeCrate {
|
|
||||||
Time,
|
|
||||||
Chrono,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DateTimeCrate> for CodegenDateTimeCrate {
|
|
||||||
fn from(date_time_crate: DateTimeCrate) -> CodegenDateTimeCrate {
|
|
||||||
match date_time_crate {
|
|
||||||
DateTimeCrate::Chrono => CodegenDateTimeCrate::Chrono,
|
|
||||||
DateTimeCrate::Time => CodegenDateTimeCrate::Time,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SeaOrmTableConfig {
|
|
||||||
pub fn get_filter(&self) -> Box<dyn Fn(&String) -> bool> {
|
|
||||||
let include_hidden = self.include_hidden;
|
|
||||||
if let Some(table) = &self.table_config {
|
|
||||||
match table {
|
|
||||||
TableConfig::Specific { specific } => {
|
|
||||||
let specific = specific.clone();
|
|
||||||
Box::new(move |table: &String| {
|
|
||||||
(include_hidden || !table.starts_with('_')) && specific.contains(table)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
TableConfig::Exclude { exclude } => {
|
|
||||||
let exclude = exclude.clone();
|
|
||||||
Box::new(move |table: &String| {
|
|
||||||
(include_hidden || !table.starts_with('_')) && !exclude.contains(table)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if self.skip_seaql_migrations {
|
|
||||||
Box::new(move |table: &String| {
|
|
||||||
(include_hidden || !table.starts_with('_'))
|
|
||||||
&& !table.starts_with("seaql_migrations")
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
Box::new(move |table: &String| (include_hidden || !table.starts_with('_')))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EntityFormat {
|
|
||||||
pub fn is_expanded(&self) -> bool {
|
|
||||||
matches!(self, EntityFormat::Expanded)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<Prelude> for WithPrelude {
|
|
||||||
fn from(val: Prelude) -> Self {
|
|
||||||
match val {
|
|
||||||
Prelude::Enabled => WithPrelude::All,
|
|
||||||
|
|
||||||
Prelude::Disabled => WithPrelude::None,
|
|
||||||
Prelude::AllowUnusedImports => WithPrelude::AllAllowUnusedImports,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<SerdeEnable> for WithSerde {
|
|
||||||
fn from(val: SerdeEnable) -> Self {
|
|
||||||
match val {
|
|
||||||
SerdeEnable::Both => WithSerde::Both,
|
|
||||||
SerdeEnable::Serialize => WithSerde::Serialize,
|
|
||||||
SerdeEnable::Deserialize => WithSerde::Deserialize,
|
|
||||||
SerdeEnable::None => WithSerde::None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Config> for EntityWriterContext {
|
|
||||||
fn from(val: Config) -> Self {
|
|
||||||
EntityWriterContext::new(
|
|
||||||
val.sea_orm.entity.format.is_expanded(),
|
|
||||||
val.sea_orm.prelude.into(),
|
|
||||||
val.sea_orm.serde.enable.into(),
|
|
||||||
val.sea_orm.entity.with_copy_enums,
|
|
||||||
val.sea_orm.entity.date_time_crate.into(),
|
|
||||||
val.db.database_schema,
|
|
||||||
false,
|
|
||||||
val.sea_orm.serde.skip_deserializing_primary_key,
|
|
||||||
val.sea_orm.serde.skip_hidden_column,
|
|
||||||
val.sea_orm.entity.extra_derives.model,
|
|
||||||
val.sea_orm.entity.extra_attributes.model,
|
|
||||||
val.sea_orm.entity.extra_derives.eenum,
|
|
||||||
val.sea_orm.entity.extra_attributes.eenum,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
use core::time;
|
|
||||||
|
|
||||||
use color_eyre::eyre::{eyre, ContextCompat, Report, Result};
|
|
||||||
use sea_schema::sea_query::TableCreateStatement;
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
use crate::config::db::DbConfig;
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub enum DbType {
|
|
||||||
MySql,
|
|
||||||
Postgres,
|
|
||||||
Sqlite,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn get_tables(
|
|
||||||
database_url: String,
|
|
||||||
filter: Box<dyn Fn(&String) -> bool>,
|
|
||||||
database_config: &DbConfig,
|
|
||||||
) -> Result<(Vec<TableCreateStatement>, DbType)> {
|
|
||||||
let url = Url::parse(&database_url)?;
|
|
||||||
|
|
||||||
tracing::trace!(?url);
|
|
||||||
|
|
||||||
let is_sqlite = url.scheme() == "sqlite";
|
|
||||||
|
|
||||||
let database_name: &str = (if !is_sqlite {
|
|
||||||
let database_name = url
|
|
||||||
.path_segments()
|
|
||||||
.context("No database name as part of path")?
|
|
||||||
.next()
|
|
||||||
.context("No database name as part of path")?;
|
|
||||||
|
|
||||||
if database_name.is_empty() {
|
|
||||||
return Err(eyre!("Database path name is empty"));
|
|
||||||
}
|
|
||||||
Ok::<&str, Report>(database_name)
|
|
||||||
} else {
|
|
||||||
Ok(Default::default())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let (table_stmts, db_type) = match url.scheme() {
|
|
||||||
"mysql" => {
|
|
||||||
use sea_schema::mysql::discovery::SchemaDiscovery;
|
|
||||||
use sqlx::MySql;
|
|
||||||
|
|
||||||
tracing::info!("Connecting to MySQL");
|
|
||||||
let connection = sqlx_connect::<MySql>(
|
|
||||||
database_config.max_connections,
|
|
||||||
database_config.acquire_timeout,
|
|
||||||
url.as_str(),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
tracing::info!("Discovering schema");
|
|
||||||
let schema_discovery = SchemaDiscovery::new(connection, database_name);
|
|
||||||
let schema = schema_discovery.discover().await?;
|
|
||||||
let table_stmts = schema
|
|
||||||
.tables
|
|
||||||
.into_iter()
|
|
||||||
.filter(|schema| filter(&schema.info.name))
|
|
||||||
.map(|schema| schema.write())
|
|
||||||
.collect();
|
|
||||||
(table_stmts, DbType::MySql)
|
|
||||||
}
|
|
||||||
"sqlite" => {
|
|
||||||
use sea_schema::sqlite::discovery::SchemaDiscovery;
|
|
||||||
use sqlx::Sqlite;
|
|
||||||
|
|
||||||
tracing::info!("Connecting to SQLite");
|
|
||||||
let connection = sqlx_connect::<Sqlite>(
|
|
||||||
database_config.max_connections,
|
|
||||||
database_config.acquire_timeout,
|
|
||||||
url.as_str(),
|
|
||||||
None,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
tracing::info!("Discovering schema");
|
|
||||||
let schema_discovery = SchemaDiscovery::new(connection);
|
|
||||||
let schema = schema_discovery
|
|
||||||
.discover()
|
|
||||||
.await?
|
|
||||||
.merge_indexes_into_table();
|
|
||||||
let table_stmts = schema
|
|
||||||
.tables
|
|
||||||
.into_iter()
|
|
||||||
.filter(|schema| filter(&schema.name))
|
|
||||||
.map(|schema| schema.write())
|
|
||||||
.collect();
|
|
||||||
(table_stmts, DbType::Sqlite)
|
|
||||||
}
|
|
||||||
"postgres" | "potgresql" => {
|
|
||||||
use sea_schema::postgres::discovery::SchemaDiscovery;
|
|
||||||
use sqlx::Postgres;
|
|
||||||
|
|
||||||
tracing::info!("Connecting to Postgres");
|
|
||||||
let schema = &database_config
|
|
||||||
.database_schema
|
|
||||||
.as_deref()
|
|
||||||
.unwrap_or("public");
|
|
||||||
let connection = sqlx_connect::<Postgres>(
|
|
||||||
database_config.max_connections,
|
|
||||||
database_config.acquire_timeout,
|
|
||||||
url.as_str(),
|
|
||||||
Some(schema),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
tracing::info!("Discovering schema");
|
|
||||||
let schema_discovery = SchemaDiscovery::new(connection, schema);
|
|
||||||
let schema = schema_discovery.discover().await?;
|
|
||||||
tracing::info!(?schema);
|
|
||||||
let table_stmts = schema
|
|
||||||
.tables
|
|
||||||
.into_iter()
|
|
||||||
.filter(|schema| filter(&schema.info.name))
|
|
||||||
.map(|schema| schema.write())
|
|
||||||
.collect();
|
|
||||||
(table_stmts, DbType::Postgres)
|
|
||||||
}
|
|
||||||
_ => unimplemented!("{} is not supported", url.scheme()),
|
|
||||||
};
|
|
||||||
tracing::info!("Schema discovered");
|
|
||||||
|
|
||||||
Ok((table_stmts, db_type))
|
|
||||||
}
|
|
||||||
async fn sqlx_connect<DB>(
|
|
||||||
max_connections: u32,
|
|
||||||
acquire_timeout: u64,
|
|
||||||
url: &str,
|
|
||||||
schema: Option<&str>,
|
|
||||||
) -> Result<sqlx::Pool<DB>>
|
|
||||||
where
|
|
||||||
DB: sqlx::Database,
|
|
||||||
for<'a> &'a mut <DB as sqlx::Database>::Connection: sqlx::Executor<'a>,
|
|
||||||
{
|
|
||||||
let mut pool_options = sqlx::pool::PoolOptions::<DB>::new()
|
|
||||||
.max_connections(max_connections)
|
|
||||||
.acquire_timeout(time::Duration::from_secs(acquire_timeout));
|
|
||||||
// Set search_path for Postgres, E.g. Some("public") by default
|
|
||||||
// MySQL & SQLite connection initialize with schema `None`
|
|
||||||
if let Some(schema) = schema {
|
|
||||||
let sql = format!("SET search_path = '{schema}'");
|
|
||||||
pool_options = pool_options.after_connect(move |conn, _| {
|
|
||||||
let sql = sql.clone();
|
|
||||||
Box::pin(async move {
|
|
||||||
sqlx::Executor::execute(conn, sql.as_str())
|
|
||||||
.await
|
|
||||||
.map(|_| ())
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
pool_options.connect(url).await.map_err(Into::into)
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use path_clean::PathClean;
|
use path_clean::PathClean;
|
||||||
use std::{collections::HashMap, path::PathBuf};
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
use tokio::{fs::File, io::AsyncWriteExt};
|
||||||
|
|
||||||
pub fn pathbuf_to_rust_path(path: PathBuf) -> String {
|
pub fn pathbuf_to_rust_path(path: PathBuf) -> String {
|
||||||
let clean_path = path.clean();
|
let clean_path = path.clean();
|
||||||
@@ -27,8 +28,10 @@ pub fn pathbuf_to_rust_path(path: PathBuf) -> String {
|
|||||||
path
|
path
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
#[allow(unused)]
|
||||||
pub enum InsertPoint {
|
pub enum InsertPoint {
|
||||||
Start,
|
Start,
|
||||||
|
Replace(String),
|
||||||
End,
|
End,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -47,20 +50,54 @@ impl FileManager {
|
|||||||
files: HashMap::new(),
|
files: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn insert_file(
|
pub fn insert(
|
||||||
&mut self,
|
&mut self,
|
||||||
file: PathBuf,
|
file: &PathBuf,
|
||||||
content: String,
|
content: &str,
|
||||||
insert_point: Option<InsertPoint>,
|
insert_point: Option<InsertPoint>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if let Some(file) = self.files.get_mut(&file) {
|
if let Some(file) = self.files.get_mut(file) {
|
||||||
match insert_point {
|
match insert_point {
|
||||||
Some(InsertPoint::Start) => file.content.insert_str(0, &content),
|
Some(InsertPoint::Start) => file.content.insert_str(0, content),
|
||||||
Some(InsertPoint::End) => file.content.push_str(&content),
|
Some(InsertPoint::End) => file.content.push_str(content),
|
||||||
None => file.content.push_str(&content),
|
None => file.content.push_str(content),
|
||||||
|
Some(InsertPoint::Replace(replace)) => {
|
||||||
|
let content = file.content.replace(&replace, content);
|
||||||
|
file.content = content;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.files.insert(file.clone(), FileContent { content });
|
self.files.insert(
|
||||||
|
file.clone(),
|
||||||
|
FileContent {
|
||||||
|
content: content.to_string(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn get(&self, file: &PathBuf) -> Option<&FileContent> {
|
||||||
|
self.files.get(file)
|
||||||
|
}
|
||||||
|
pub async fn write_files(&self) -> Result<()> {
|
||||||
|
for (file, content) in &self.files {
|
||||||
|
tracing::info!(?file, "Writing file");
|
||||||
|
let parent = file.parent().unwrap();
|
||||||
|
if !parent.exists() {
|
||||||
|
tokio::fs::create_dir_all(parent).await?;
|
||||||
|
}
|
||||||
|
let mut opened_file = File::create(file).await?;
|
||||||
|
opened_file.write_all(content.content.as_bytes()).await?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub async fn format_files(&self) -> Result<()> {
|
||||||
|
for file in self.files.keys() {
|
||||||
|
tracing::info!(?file, "Formatting file");
|
||||||
|
tokio::process::Command::new("rustfmt")
|
||||||
|
.arg(file)
|
||||||
|
.output()
|
||||||
|
.await?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -68,7 +105,7 @@ impl FileManager {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::generator::file::pathbuf_to_rust_path;
|
use crate::generator::file::{pathbuf_to_rust_path, FileManager, InsertPoint};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pathbuf_to_rust_path() {
|
fn test_pathbuf_to_rust_path() {
|
||||||
@@ -90,4 +127,49 @@ mod test {
|
|||||||
let rust_path = pathbuf_to_rust_path(path);
|
let rust_path = pathbuf_to_rust_path(path);
|
||||||
assert_eq!(rust_path, "");
|
assert_eq!(rust_path, "");
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_fildmanager_insert() {
|
||||||
|
let mut file_manager = FileManager::new();
|
||||||
|
let file_path = PathBuf::from("test.rs");
|
||||||
|
file_manager.insert(&file_path, "test", None).unwrap();
|
||||||
|
file_manager.insert(&file_path, "test1", None).unwrap();
|
||||||
|
assert_eq!(file_manager.get(&file_path).unwrap().content, "testtest1");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_fildmanager_insert_start() {
|
||||||
|
let mut file_manager = FileManager::new();
|
||||||
|
let file_path = PathBuf::from("test.rs");
|
||||||
|
file_manager.insert(&file_path, "test", None).unwrap();
|
||||||
|
file_manager
|
||||||
|
.insert(&file_path, "teststart", Some(InsertPoint::Start))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
file_manager.get(&file_path).unwrap().content,
|
||||||
|
"teststarttest"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_fildmanager_insert_end() {
|
||||||
|
let mut file_manager = FileManager::new();
|
||||||
|
let file_path = PathBuf::from("test.rs");
|
||||||
|
file_manager.insert(&file_path, "test", None).unwrap();
|
||||||
|
file_manager
|
||||||
|
.insert(&file_path, "testend", Some(InsertPoint::End))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(file_manager.get(&file_path).unwrap().content, "testtestend");
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_fildmanager_insert_replace() {
|
||||||
|
let mut file_manager = FileManager::new();
|
||||||
|
let file_path = PathBuf::from("test.rs");
|
||||||
|
file_manager.insert(&file_path, "test", None).unwrap();
|
||||||
|
file_manager
|
||||||
|
.insert(
|
||||||
|
&file_path,
|
||||||
|
"testreplace",
|
||||||
|
Some(InsertPoint::Replace("test".to_string())),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(file_manager.get(&file_path).unwrap().content, "testreplace");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,30 +1,32 @@
|
|||||||
pub mod discover;
|
|
||||||
pub mod file;
|
pub mod file;
|
||||||
pub mod modules;
|
pub mod modules;
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use handlebars::Handlebars;
|
|
||||||
use toml_edit::DocumentMut;
|
use toml_edit::DocumentMut;
|
||||||
|
|
||||||
|
use crate::Args;
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct DatabaseUrl(String);
|
pub struct DatabaseUrl(String);
|
||||||
|
|
||||||
pub async fn generate(database_url: &str, root_config: DocumentMut) -> Result<()> {
|
pub async fn generate(args: Args, root_config: DocumentMut) -> Result<()> {
|
||||||
let mut module_manager = modules::ModuleManager::new(root_config);
|
let mut module_manager = modules::ModuleManager::new(root_config);
|
||||||
module_manager.init()?;
|
module_manager.init()?;
|
||||||
let ctx = module_manager.get_context_mut();
|
let ctx = module_manager.get_context_mut();
|
||||||
ctx.get_anymap_mut()
|
ctx.get_anymap_mut()
|
||||||
.insert(DatabaseUrl(database_url.to_owned()));
|
.insert(DatabaseUrl(args.database_url.to_owned()));
|
||||||
module_manager.validate().await?;
|
module_manager.validate().await?;
|
||||||
module_manager.execute().await?;
|
module_manager.execute().await?;
|
||||||
|
module_manager
|
||||||
|
.get_context_mut()
|
||||||
|
.get_file_manager()
|
||||||
|
.write_files()
|
||||||
|
.await?;
|
||||||
|
if args.rustfmt {
|
||||||
|
module_manager
|
||||||
|
.get_context_mut()
|
||||||
|
.get_file_manager()
|
||||||
|
.format_files()
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
// let db_filter = config.sea_orm.entity.tables.get_filter();
|
|
||||||
// let (table_stmts, db_type) =
|
|
||||||
// discover::get_tables(database_url.to_owned(), db_filter, &config.db).await?;
|
|
||||||
// let tables = table_stmts
|
|
||||||
// .into_iter()
|
|
||||||
// .map(Table::new)
|
|
||||||
// .collect::<Result<Vec<Table>>>()?;
|
|
||||||
//
|
|
||||||
// let model_outputs = modules::models::generate_models(database_url, config, handlebars).await?;
|
|
||||||
// files.extend(model_outputs);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
121
src/generator/modules/annotate/comment.rs
Normal file
121
src/generator/modules/annotate/comment.rs
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
use color_eyre::Result;
|
||||||
|
use minijinja::Environment;
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::generator::modules::{
|
||||||
|
discovery::{db::DbType, table::Table},
|
||||||
|
sea_orm::config::DateTimeCrate,
|
||||||
|
};
|
||||||
|
use comfy_table::{Cell, ContentArrangement, Table as CTable};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
AnnotateCommentConfig, COMMENTBODY, COMMENTHEAD, COMMENTTAIL, HEADER, SETTINGSDELIMITER,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct CommentContext<'a> {
|
||||||
|
pub table_name: &'a str,
|
||||||
|
pub config: &'a AnnotateCommentConfig,
|
||||||
|
pub column_info_table: String,
|
||||||
|
pub comment_config: Option<String>,
|
||||||
|
pub config_delimiter: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn generate_comment(
|
||||||
|
table: &Table,
|
||||||
|
config: &AnnotateCommentConfig,
|
||||||
|
environment: &Environment<'static>,
|
||||||
|
db_type: &DbType,
|
||||||
|
date_time_crate: &DateTimeCrate,
|
||||||
|
comment_config: Option<AnnotateCommentConfig>,
|
||||||
|
) -> Result<String> {
|
||||||
|
let mut column_info_table = CTable::new();
|
||||||
|
let mut header = Vec::new();
|
||||||
|
if config.column_name.unwrap() {
|
||||||
|
header.push("Name");
|
||||||
|
}
|
||||||
|
if config.column_db_type.unwrap() {
|
||||||
|
header.push("DbType");
|
||||||
|
}
|
||||||
|
if config.column_rust_type.unwrap() {
|
||||||
|
header.push("RsType");
|
||||||
|
}
|
||||||
|
if config.column_attributes.unwrap() {
|
||||||
|
header.push("Attrs");
|
||||||
|
}
|
||||||
|
column_info_table
|
||||||
|
.load_preset("|| -+=++ + ++")
|
||||||
|
.set_content_arrangement(ContentArrangement::Dynamic)
|
||||||
|
.set_header(header);
|
||||||
|
if let Some(width) = config.max_wdith {
|
||||||
|
column_info_table.set_width(width);
|
||||||
|
}
|
||||||
|
for column in &table.columns {
|
||||||
|
let mut row = Vec::new();
|
||||||
|
if config.column_name.unwrap() {
|
||||||
|
row.push(Cell::new(column.name.clone()))
|
||||||
|
}
|
||||||
|
if config.column_db_type.unwrap() {
|
||||||
|
let column_type = column.get_db_type(db_type);
|
||||||
|
row.push(Cell::new(column_type));
|
||||||
|
}
|
||||||
|
if config.column_rust_type.unwrap() {
|
||||||
|
let column_type = column.get_rust_type(date_time_crate);
|
||||||
|
row.push(Cell::new(column_type));
|
||||||
|
}
|
||||||
|
if config.column_attributes.unwrap() {
|
||||||
|
let exclude = config.column_exclude_attributes.clone().unwrap();
|
||||||
|
let filter: Box<dyn Fn(&String) -> bool> = Box::new(move |f| {
|
||||||
|
let exclude = exclude.clone();
|
||||||
|
!exclude.contains(f)
|
||||||
|
});
|
||||||
|
let attrs_string = column.attrs_to_string(Some(filter));
|
||||||
|
|
||||||
|
row.push(Cell::new(attrs_string));
|
||||||
|
}
|
||||||
|
column_info_table.add_row(row);
|
||||||
|
}
|
||||||
|
let context = CommentContext {
|
||||||
|
table_name: &table.name,
|
||||||
|
config,
|
||||||
|
column_info_table: column_info_table.to_string(),
|
||||||
|
comment_config: comment_config
|
||||||
|
.and_then(|f| toml::to_string_pretty(&f).ok())
|
||||||
|
.map(|s| {
|
||||||
|
s.lines()
|
||||||
|
.map(|line| format!(" {}", line))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
}),
|
||||||
|
config_delimiter: SETTINGSDELIMITER,
|
||||||
|
};
|
||||||
|
let template = environment.get_template("annotate.comment")?;
|
||||||
|
let rendered_data = template.render(&context)?;
|
||||||
|
|
||||||
|
Ok(pad_comment(&rendered_data))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pad_comment(s: &str) -> String {
|
||||||
|
let parts = s.split('\n').collect::<Vec<_>>();
|
||||||
|
let mut padded = String::new();
|
||||||
|
for (index, part) in parts.iter().enumerate() {
|
||||||
|
let first = index == 0;
|
||||||
|
let comment = match first {
|
||||||
|
true => format!("{} {}\n{}", COMMENTHEAD, HEADER, COMMENTBODY),
|
||||||
|
false => COMMENTBODY.to_string(),
|
||||||
|
};
|
||||||
|
let padded_part = format!("{} {}\n", comment, part);
|
||||||
|
padded.push_str(&padded_part);
|
||||||
|
}
|
||||||
|
padded.push_str(COMMENTTAIL);
|
||||||
|
padded
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_settings_block(file_content: &str) -> Option<String> {
|
||||||
|
let delimiter_length = SETTINGSDELIMITER.len();
|
||||||
|
let start_pos = file_content.find(SETTINGSDELIMITER)?;
|
||||||
|
let end_pos = file_content[start_pos + delimiter_length..].find(SETTINGSDELIMITER)?;
|
||||||
|
let content = &file_content[start_pos + delimiter_length..start_pos + end_pos];
|
||||||
|
let content = content.replace(&format!("\n{COMMENTBODY}"), "\n");
|
||||||
|
Some(content)
|
||||||
|
}
|
||||||
174
src/generator/modules/annotate/mod.rs
Normal file
174
src/generator/modules/annotate/mod.rs
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
pub mod comment;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use crate::generator::file::InsertPoint;
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
discovery::DiscoveredSchema, models::ModelsConfig, sea_orm::SeaOrmConfig, Module,
|
||||||
|
ModulesContext,
|
||||||
|
};
|
||||||
|
use color_eyre::Result;
|
||||||
|
use comment_parser::{CommentParser, Event};
|
||||||
|
use minijinja::Environment;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::fs;
|
||||||
|
const HEADER: &str = r#"== Schema Information"#;
|
||||||
|
const COMMENTHEAD: &str = r#"/*"#;
|
||||||
|
const COMMENTBODY: &str = r#" *"#;
|
||||||
|
const COMMENTTAIL: &str = r#"*/"#;
|
||||||
|
const SETTINGSDELIMITER: &str = r#"```"#;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AnnotateConfig {
|
||||||
|
pub enable: bool,
|
||||||
|
pub comment: AnnotateCommentConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
pub struct AnnotateCommentConfig {
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub enable: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub max_wdith: Option<u16>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub table_name: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub column_name: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub column_db_type: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub column_rust_type: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub column_attributes: Option<bool>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub column_exclude_attributes: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for AnnotateCommentConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enable: Some(true),
|
||||||
|
max_wdith: Some(80),
|
||||||
|
table_name: Some(true),
|
||||||
|
column_name: Some(true),
|
||||||
|
column_db_type: Some(true),
|
||||||
|
column_rust_type: Some(true),
|
||||||
|
column_attributes: Some(true),
|
||||||
|
column_exclude_attributes: Some(vec![]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct AnnotateModule;
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl Module for AnnotateModule {
|
||||||
|
fn init(&mut self, ctx: &mut ModulesContext) -> Result<()> {
|
||||||
|
ctx.get_config_auto::<AnnotateConfig>("modules.annotate")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn validate(&mut self, ctx: &mut ModulesContext) -> Result<bool> {
|
||||||
|
let map = ctx.get_anymap();
|
||||||
|
|
||||||
|
if let Some(config) = map.get::<AnnotateConfig>() {
|
||||||
|
Ok(config.enable)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async fn execute(&mut self, ctx: &mut ModulesContext) -> Result<()> {
|
||||||
|
let map = ctx.get_anymap();
|
||||||
|
let file_manager = ctx.get_file_manager();
|
||||||
|
let mut file_chunks: Vec<(PathBuf, String, Option<InsertPoint>)> = Vec::new();
|
||||||
|
if let (Some(config), Some(environment), Some(schema)) = (
|
||||||
|
map.get::<AnnotateConfig>(),
|
||||||
|
map.get::<Environment<'static>>(),
|
||||||
|
map.get::<DiscoveredSchema>(),
|
||||||
|
) {
|
||||||
|
if let (Some(models_config), Some(sea_orm_config)) =
|
||||||
|
(map.get::<ModelsConfig>(), map.get::<SeaOrmConfig>())
|
||||||
|
{
|
||||||
|
if models_config.enable {
|
||||||
|
let path = models_config.path.clone().unwrap();
|
||||||
|
for table in &schema.tables {
|
||||||
|
let path = path.join(format!("{}.rs", table.name));
|
||||||
|
let content = file_manager.get(&path);
|
||||||
|
if content.is_some() {
|
||||||
|
// generate default comment and insert
|
||||||
|
let comment = comment::generate_comment(
|
||||||
|
table,
|
||||||
|
&config.comment,
|
||||||
|
environment,
|
||||||
|
&schema.database_type,
|
||||||
|
&sea_orm_config.entity.date_time_crate,
|
||||||
|
None,
|
||||||
|
)?;
|
||||||
|
file_chunks.push((path, comment, Some(InsertPoint::Start)))
|
||||||
|
} else {
|
||||||
|
let content = fs::read_to_string(&path).await?;
|
||||||
|
let rules = comment_parser::get_syntax("rust").unwrap();
|
||||||
|
let parser = CommentParser::new(&content, rules);
|
||||||
|
for comment in parser {
|
||||||
|
if let Event::BlockComment(body, _) = comment {
|
||||||
|
if body.contains(HEADER) {
|
||||||
|
tracing::debug!("Found header");
|
||||||
|
let mut comment_config = config.comment.clone();
|
||||||
|
let mut file_comment_config = None;
|
||||||
|
if let Some(parsed_settings) =
|
||||||
|
comment::find_settings_block(&content)
|
||||||
|
{
|
||||||
|
match toml::from_str::<AnnotateCommentConfig>(
|
||||||
|
&parsed_settings,
|
||||||
|
) {
|
||||||
|
Ok(s) => {
|
||||||
|
file_comment_config = Some(s.clone());
|
||||||
|
comment_config =
|
||||||
|
serde_merge::omerge(&config.comment, s)?;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return Err(e.into());
|
||||||
|
// if !settings.ignore_errors {
|
||||||
|
// return Err(e.into());
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tracing::info!(?comment_config, ?file_comment_config);
|
||||||
|
if comment_config.enable.unwrap() {
|
||||||
|
let comment = comment::generate_comment(
|
||||||
|
table,
|
||||||
|
&comment_config,
|
||||||
|
environment,
|
||||||
|
&schema.database_type,
|
||||||
|
&sea_orm_config.entity.date_time_crate,
|
||||||
|
file_comment_config,
|
||||||
|
)?;
|
||||||
|
file_chunks.push((path.clone(), content.clone(), None));
|
||||||
|
file_chunks.push((
|
||||||
|
path,
|
||||||
|
comment,
|
||||||
|
Some(InsertPoint::Replace(body.to_string())),
|
||||||
|
));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let file_manager = ctx.get_file_manager_mut();
|
||||||
|
for file in file_chunks {
|
||||||
|
file_manager.insert(&file.0, &file.1, file.2)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,17 @@
|
|||||||
use color_eyre::{eyre::ContextCompat, Result};
|
use color_eyre::{eyre::ContextCompat, Result};
|
||||||
use heck::ToUpperCamelCase;
|
use heck::ToUpperCamelCase;
|
||||||
use sea_schema::sea_query::{ColumnDef, ColumnSpec, ColumnType, IndexCreateStatement};
|
use sea_schema::sea_query::{ColumnDef, ColumnSpec, ColumnType, IndexCreateStatement};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
use crate::config::sea_orm_config::DateTimeCrate;
|
use crate::generator::modules::sea_orm::config::DateTimeCrate;
|
||||||
|
|
||||||
use super::db::DbType;
|
use super::db::DbType;
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub struct Column {
|
pub struct Column {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
pub col_type: ColumnType,
|
pub col_type: ColumnType,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
pub attrs: Vec<ColumnSpec>,
|
pub attrs: Vec<ColumnSpec>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,30 +37,18 @@ impl Column {
|
|||||||
attrs: attrs.to_vec(),
|
attrs: attrs.to_vec(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// pub fn get_info_row(&self, config: &ModelConfig) -> Result<Vec<Cell>> {
|
#[allow(clippy::type_complexity)]
|
||||||
// let column_type_rust = self.get_rs_type(&config.comment.date_time_crate);
|
pub fn attrs_to_string(&self, filter: Option<Box<dyn Fn(&String) -> bool>>) -> String {
|
||||||
// let column_type = self.get_db_type(&config.db_type);
|
|
||||||
// let attrs = self.attrs_to_string();
|
|
||||||
// let mut cols = Vec::new();
|
|
||||||
// if config.comment.column_name {
|
|
||||||
// cols.push(Cell::new(self.name.clone()))
|
|
||||||
// }
|
|
||||||
// if config.comment.column_name {
|
|
||||||
// cols.push(Cell::new(column_type.clone()))
|
|
||||||
// }
|
|
||||||
// if config.comment.column_rust_type {
|
|
||||||
// cols.push(Cell::new(column_type_rust.clone()))
|
|
||||||
// }
|
|
||||||
// if config.comment.column_attributes {
|
|
||||||
// cols.push(Cell::new(attrs.clone()));
|
|
||||||
// }
|
|
||||||
// Ok(cols)
|
|
||||||
// }
|
|
||||||
pub fn attrs_to_string(&self) -> String {
|
|
||||||
self.attrs
|
self.attrs
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(Self::get_addr_type)
|
.filter_map(Self::get_addr_type)
|
||||||
.map(|s| s.to_string())
|
.map(|s| s.to_string())
|
||||||
|
.filter(|s| {
|
||||||
|
if let Some(filter) = &filter {
|
||||||
|
return filter(s);
|
||||||
|
};
|
||||||
|
true
|
||||||
|
})
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
}
|
}
|
||||||
@@ -171,7 +162,7 @@ impl Column {
|
|||||||
}
|
}
|
||||||
write_db_type(&self.col_type, db_type)
|
write_db_type(&self.col_type, db_type)
|
||||||
}
|
}
|
||||||
pub fn get_rs_type(&self, date_time_crate: &DateTimeCrate) -> String {
|
pub fn get_rust_type(&self, date_time_crate: &DateTimeCrate) -> String {
|
||||||
fn write_rs_type(col_type: &ColumnType, date_time_crate: &DateTimeCrate) -> String {
|
fn write_rs_type(col_type: &ColumnType, date_time_crate: &DateTimeCrate) -> String {
|
||||||
#[allow(unreachable_patterns)]
|
#[allow(unreachable_patterns)]
|
||||||
match col_type {
|
match col_type {
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ use color_eyre::eyre::{eyre, ContextCompat, Report, Result};
|
|||||||
use sea_schema::sea_query::TableCreateStatement;
|
use sea_schema::sea_query::TableCreateStatement;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
use crate::config::db::DbConfig;
|
|
||||||
|
|
||||||
use super::DiscoveryConfig;
|
use super::DiscoveryConfig;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum DbType {
|
pub enum DbType {
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ use color_eyre::Result;
|
|||||||
use db::DbType;
|
use db::DbType;
|
||||||
use sea_schema::sea_query::TableCreateStatement;
|
use sea_schema::sea_query::TableCreateStatement;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_inline_default::serde_inline_default;
|
|
||||||
use table::Table;
|
use table::Table;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
@@ -113,7 +112,6 @@ impl Module for DiscoveryModule {
|
|||||||
if let (Some(config), Some(_)) = (map.get::<DiscoveryConfig>(), map.get::<DatabaseUrl>()) {
|
if let (Some(config), Some(_)) = (map.get::<DiscoveryConfig>(), map.get::<DatabaseUrl>()) {
|
||||||
Ok(config.enable)
|
Ok(config.enable)
|
||||||
} else {
|
} else {
|
||||||
// One or both keys are missing
|
|
||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
use super::column::Column;
|
use super::column::Column;
|
||||||
use color_eyre::{eyre::eyre, Result};
|
use color_eyre::{eyre::eyre, Result};
|
||||||
use sea_schema::sea_query::{self, TableCreateStatement};
|
use sea_schema::sea_query::{self, TableCreateStatement};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize)]
|
||||||
pub struct Table {
|
pub struct Table {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub columns: Vec<Column>,
|
pub columns: Vec<Column>,
|
||||||
|
|||||||
@@ -1,25 +1,18 @@
|
|||||||
use std::{
|
use std::fmt::Debug;
|
||||||
fmt::Debug,
|
|
||||||
sync::{Arc, MutexGuard},
|
|
||||||
};
|
|
||||||
|
|
||||||
use anymap::{
|
use annotate::AnnotateModule;
|
||||||
any::{Any, CloneAny},
|
use anymap::{any::CloneAny, Map};
|
||||||
Map,
|
|
||||||
};
|
|
||||||
use color_eyre::{eyre::eyre, Result};
|
use color_eyre::{eyre::eyre, Result};
|
||||||
use discovery::DiscoveryModule;
|
use discovery::DiscoveryModule;
|
||||||
use sea_orm::{SeaOrmConfig, SeaOrmModule};
|
use models::ModelsModule;
|
||||||
// use models::ModelsModule;
|
use sea_orm::SeaOrmModule;
|
||||||
use serde::{de::IntoDeserializer, Deserialize};
|
use serde::{de::IntoDeserializer, Deserialize};
|
||||||
use std::sync::Mutex;
|
|
||||||
use templates::TemplateModule;
|
use templates::TemplateModule;
|
||||||
use toml_edit::{de::ValueDeserializer, DocumentMut, Item, Value};
|
use toml_edit::{DocumentMut, Item};
|
||||||
// use models::table::Table;
|
|
||||||
//
|
|
||||||
// use super::discover::DbType;
|
|
||||||
|
|
||||||
|
use super::file::FileManager;
|
||||||
type AnyCloneMap = Map<dyn CloneAny + Send>;
|
type AnyCloneMap = Map<dyn CloneAny + Send>;
|
||||||
|
pub mod annotate;
|
||||||
pub mod discovery;
|
pub mod discovery;
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod sea_orm;
|
pub mod sea_orm;
|
||||||
@@ -28,12 +21,14 @@ pub mod templates;
|
|||||||
pub struct ModulesContext {
|
pub struct ModulesContext {
|
||||||
pub anymap: AnyCloneMap,
|
pub anymap: AnyCloneMap,
|
||||||
pub root_config: DocumentMut,
|
pub root_config: DocumentMut,
|
||||||
|
pub file_manager: FileManager,
|
||||||
}
|
}
|
||||||
impl ModulesContext {
|
impl ModulesContext {
|
||||||
pub fn new(root_config: DocumentMut) -> Self {
|
pub fn new(root_config: DocumentMut) -> Self {
|
||||||
Self {
|
Self {
|
||||||
anymap: AnyCloneMap::new(),
|
anymap: AnyCloneMap::new(),
|
||||||
root_config,
|
root_config,
|
||||||
|
file_manager: FileManager::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_config_raw(&self, path: &str) -> Result<&Item> {
|
pub fn get_config_raw(&self, path: &str) -> Result<&Item> {
|
||||||
@@ -44,12 +39,12 @@ impl ModulesContext {
|
|||||||
if let Some(v) = item.get(i) {
|
if let Some(v) = item.get(i) {
|
||||||
*item = v;
|
*item = v;
|
||||||
} else {
|
} else {
|
||||||
return Err(eyre!("Config not found"));
|
return Err(eyre!("Config not found \"{i}\""));
|
||||||
}
|
}
|
||||||
} else if let Some(v) = self.root_config.get(i) {
|
} else if let Some(v) = self.root_config.get(i) {
|
||||||
item = Some(v);
|
item = Some(v);
|
||||||
} else {
|
} else {
|
||||||
return Err(eyre!("Config not found"));
|
return Err(eyre!("Config not found \"{i}\""));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(v) = item {
|
if let Some(v) = item {
|
||||||
@@ -58,8 +53,10 @@ impl ModulesContext {
|
|||||||
Err(eyre!("Config not found"))
|
Err(eyre!("Config not found"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn get_config<'a, V: Deserialize<'a> + Debug>(&self, path: &str) -> Result<V> {
|
pub fn get_config<'a, V: Deserialize<'a> + Debug>(&self, path: &str) -> Result<Option<V>> {
|
||||||
let item = self.get_config_raw(path)?;
|
let Ok(item) = self.get_config_raw(path) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
let value = item
|
let value = item
|
||||||
.clone()
|
.clone()
|
||||||
.into_value()
|
.into_value()
|
||||||
@@ -67,26 +64,31 @@ impl ModulesContext {
|
|||||||
let deserializer = value.into_deserializer();
|
let deserializer = value.into_deserializer();
|
||||||
let config = V::deserialize(deserializer)?;
|
let config = V::deserialize(deserializer)?;
|
||||||
tracing::debug!(?config, "{}", path);
|
tracing::debug!(?config, "{}", path);
|
||||||
Ok(config)
|
Ok(Some(config))
|
||||||
}
|
}
|
||||||
pub fn get_config_auto<'a, V: Deserialize<'a> + Clone + Send + Debug + 'static>(
|
pub fn get_config_auto<'a, V: Deserialize<'a> + Clone + Send + Debug + Default + 'static>(
|
||||||
&mut self,
|
&mut self,
|
||||||
path: &str,
|
path: &str,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let value: V = self.get_config::<V>(path)?;
|
let value: Option<V> = self.get_config::<V>(path)?;
|
||||||
self.get_anymap_mut().insert(value);
|
if value.is_none() {
|
||||||
|
tracing::warn!(?path, "Config not found, using default");
|
||||||
|
}
|
||||||
|
self.get_anymap_mut().insert(value.unwrap_or_default());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
// pub fn get_anymap(&self) -> MutexGuard<Map> {
|
|
||||||
// let v = self.anymap.lock().unwrap();
|
|
||||||
// v
|
|
||||||
// }
|
|
||||||
pub fn get_anymap(&self) -> &AnyCloneMap {
|
pub fn get_anymap(&self) -> &AnyCloneMap {
|
||||||
&self.anymap
|
&self.anymap
|
||||||
}
|
}
|
||||||
pub fn get_anymap_mut(&mut self) -> &mut AnyCloneMap {
|
pub fn get_anymap_mut(&mut self) -> &mut AnyCloneMap {
|
||||||
&mut self.anymap
|
&mut self.anymap
|
||||||
}
|
}
|
||||||
|
pub fn get_file_manager(&self) -> &FileManager {
|
||||||
|
&self.file_manager
|
||||||
|
}
|
||||||
|
pub fn get_file_manager_mut(&mut self) -> &mut FileManager {
|
||||||
|
&mut self.file_manager
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait Module: Debug {
|
pub trait Module: Debug {
|
||||||
@@ -105,7 +107,9 @@ impl ModuleManager {
|
|||||||
let modules: Vec<Box<dyn Module>> = vec![
|
let modules: Vec<Box<dyn Module>> = vec![
|
||||||
Box::new(TemplateModule),
|
Box::new(TemplateModule),
|
||||||
Box::new(DiscoveryModule),
|
Box::new(DiscoveryModule),
|
||||||
Box::new(SeaOrmModule), //Box::new(ModelsModule)
|
Box::new(SeaOrmModule),
|
||||||
|
Box::new(ModelsModule),
|
||||||
|
Box::new(AnnotateModule),
|
||||||
];
|
];
|
||||||
Self {
|
Self {
|
||||||
modules,
|
modules,
|
||||||
|
|||||||
@@ -1,236 +0,0 @@
|
|||||||
use color_eyre::{eyre::ContextCompat, Result};
|
|
||||||
use comfy_table::Cell;
|
|
||||||
use heck::ToUpperCamelCase;
|
|
||||||
use sea_schema::sea_query::{ColumnDef, ColumnSpec, ColumnType, IndexCreateStatement};
|
|
||||||
|
|
||||||
use super::{discover::DbType, ModelConfig};
|
|
||||||
use crate::config::sea_orm_config::DateTimeCrate;
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Column {
|
|
||||||
pub name: String,
|
|
||||||
pub col_type: ColumnType,
|
|
||||||
pub attrs: Vec<ColumnSpec>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Column {
|
|
||||||
pub fn new(column: ColumnDef, index: Option<IndexCreateStatement>) -> Result<Self> {
|
|
||||||
let name = column.get_column_name();
|
|
||||||
let col_type = column
|
|
||||||
.get_column_type()
|
|
||||||
.context("Unable to get column type")?
|
|
||||||
.clone();
|
|
||||||
let mut attrs = column.get_column_spec().clone();
|
|
||||||
if let Some(index) = index {
|
|
||||||
if index.is_unique_key() {
|
|
||||||
attrs.push(ColumnSpec::UniqueKey)
|
|
||||||
}
|
|
||||||
if index.is_primary_key() {
|
|
||||||
attrs.push(ColumnSpec::PrimaryKey);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(Column {
|
|
||||||
name: name.to_string(),
|
|
||||||
col_type,
|
|
||||||
attrs: attrs.to_vec(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
pub fn get_info_row(&self, config: &ModelConfig) -> Result<Vec<Cell>> {
|
|
||||||
let column_type_rust = self.get_rs_type(&config.comment.date_time_crate);
|
|
||||||
let column_type = self.get_db_type(&config.db_type);
|
|
||||||
let attrs = self.attrs_to_string();
|
|
||||||
let mut cols = Vec::new();
|
|
||||||
if config.comment.column_name {
|
|
||||||
cols.push(Cell::new(self.name.clone()))
|
|
||||||
}
|
|
||||||
if config.comment.column_name {
|
|
||||||
cols.push(Cell::new(column_type.clone()))
|
|
||||||
}
|
|
||||||
if config.comment.column_rust_type {
|
|
||||||
cols.push(Cell::new(column_type_rust.clone()))
|
|
||||||
}
|
|
||||||
if config.comment.column_attributes {
|
|
||||||
cols.push(Cell::new(attrs.clone()));
|
|
||||||
}
|
|
||||||
Ok(cols)
|
|
||||||
}
|
|
||||||
pub fn attrs_to_string(&self) -> String {
|
|
||||||
self.attrs
|
|
||||||
.iter()
|
|
||||||
.filter_map(Self::get_addr_type)
|
|
||||||
.map(|s| s.to_string())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join(", ")
|
|
||||||
}
|
|
||||||
pub fn get_addr_type(attr: &ColumnSpec) -> Option<String> {
|
|
||||||
match attr {
|
|
||||||
ColumnSpec::PrimaryKey => Some("primary key".to_owned()),
|
|
||||||
ColumnSpec::Null => unimplemented!(),
|
|
||||||
ColumnSpec::NotNull => Some("not null".to_owned()),
|
|
||||||
ColumnSpec::Default(_) => unimplemented!(),
|
|
||||||
ColumnSpec::AutoIncrement => Some("autoincrement".to_owned()),
|
|
||||||
ColumnSpec::UniqueKey => Some("unique key".to_owned()),
|
|
||||||
ColumnSpec::Check(_) => unimplemented!(),
|
|
||||||
ColumnSpec::Generated { .. } => unimplemented!(),
|
|
||||||
ColumnSpec::Extra(_) => unimplemented!(),
|
|
||||||
ColumnSpec::Comment(_) => unimplemented!(),
|
|
||||||
ColumnSpec::Using(_) => unimplemented!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get_db_type(&self, db_type: &DbType) -> String {
|
|
||||||
fn write_db_type(col_type: &ColumnType, db_type: &DbType) -> String {
|
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
match (col_type, db_type) {
|
|
||||||
(ColumnType::Char(_), _) => "char".to_owned(),
|
|
||||||
(ColumnType::String(_), _) => "varchar".to_owned(),
|
|
||||||
(ColumnType::Text, _) => "text".to_owned(),
|
|
||||||
(ColumnType::TinyInteger, DbType::MySql | DbType::Sqlite) => "tinyint".to_owned(),
|
|
||||||
(ColumnType::TinyInteger, DbType::Postgres) => "smallint".to_owned(),
|
|
||||||
(ColumnType::SmallInteger, _) => "smallint".to_owned(),
|
|
||||||
(ColumnType::Integer, DbType::MySql) => "int".to_owned(),
|
|
||||||
(ColumnType::Integer, _) => "integer".to_owned(),
|
|
||||||
(ColumnType::BigInteger, DbType::MySql | DbType::Postgres) => "bigint".to_owned(),
|
|
||||||
(ColumnType::BigInteger, DbType::Sqlite) => "integer".to_owned(),
|
|
||||||
(ColumnType::TinyUnsigned, DbType::MySql) => "tinyint unsigned".to_owned(),
|
|
||||||
(ColumnType::TinyUnsigned, DbType::Postgres) => "smallint".to_owned(),
|
|
||||||
(ColumnType::TinyUnsigned, DbType::Sqlite) => "tinyint".to_owned(),
|
|
||||||
(ColumnType::SmallUnsigned, DbType::MySql) => "smallint unsigned".to_owned(),
|
|
||||||
(ColumnType::SmallUnsigned, DbType::Postgres | DbType::Sqlite) => {
|
|
||||||
"smallint".to_owned()
|
|
||||||
}
|
|
||||||
(ColumnType::Unsigned, DbType::MySql) => "int unsigned".to_owned(),
|
|
||||||
(ColumnType::Unsigned, DbType::Postgres | DbType::Sqlite) => "integer".to_owned(),
|
|
||||||
(ColumnType::BigUnsigned, DbType::MySql) => "bigint unsigned".to_owned(),
|
|
||||||
(ColumnType::BigUnsigned, DbType::Postgres) => "bigint".to_owned(),
|
|
||||||
(ColumnType::BigUnsigned, DbType::Sqlite) => "integer".to_owned(),
|
|
||||||
(ColumnType::Float, DbType::MySql | DbType::Sqlite) => "float".to_owned(),
|
|
||||||
(ColumnType::Float, DbType::Postgres) => "real".to_owned(),
|
|
||||||
(ColumnType::Double, DbType::MySql | DbType::Sqlite) => "double".to_owned(),
|
|
||||||
(ColumnType::Double, DbType::Postgres) => "double precision".to_owned(),
|
|
||||||
(ColumnType::Decimal(_), DbType::MySql | DbType::Postgres) => "decimal".to_owned(),
|
|
||||||
(ColumnType::Decimal(_), DbType::Sqlite) => "real".to_owned(),
|
|
||||||
(ColumnType::DateTime, DbType::MySql) => "datetime".to_owned(),
|
|
||||||
(ColumnType::DateTime, DbType::Postgres) => "timestamp w/o tz".to_owned(),
|
|
||||||
(ColumnType::DateTime, DbType::Sqlite) => "datetime_text".to_owned(),
|
|
||||||
(ColumnType::Timestamp, DbType::MySql | DbType::Postgres) => "timestamp".to_owned(),
|
|
||||||
(ColumnType::Timestamp, DbType::Sqlite) => "timestamp_text".to_owned(),
|
|
||||||
(ColumnType::TimestampWithTimeZone, DbType::MySql) => "timestamp".to_owned(),
|
|
||||||
(ColumnType::TimestampWithTimeZone, DbType::Postgres) => {
|
|
||||||
"timestamp w tz".to_owned()
|
|
||||||
}
|
|
||||||
(ColumnType::TimestampWithTimeZone, DbType::Sqlite) => {
|
|
||||||
"timestamp_with_timezone_text".to_owned()
|
|
||||||
}
|
|
||||||
(ColumnType::Time, DbType::MySql | DbType::Postgres) => "time".to_owned(),
|
|
||||||
(ColumnType::Time, DbType::Sqlite) => "time_text".to_owned(),
|
|
||||||
(ColumnType::Date, DbType::MySql | DbType::Postgres) => "date".to_owned(),
|
|
||||||
(ColumnType::Date, DbType::Sqlite) => "date_text".to_owned(),
|
|
||||||
(ColumnType::Year, DbType::MySql) => "year".to_owned(),
|
|
||||||
(ColumnType::Interval(_, _), DbType::Postgres) => "interval".to_owned(),
|
|
||||||
(ColumnType::Blob, DbType::MySql | DbType::Sqlite) => "blob".to_owned(),
|
|
||||||
(ColumnType::Blob, DbType::Postgres) => "bytea".to_owned(),
|
|
||||||
(ColumnType::Binary(_), DbType::MySql) => "binary".to_owned(),
|
|
||||||
(ColumnType::Binary(_), DbType::Postgres) => "bytea".to_owned(),
|
|
||||||
(ColumnType::Binary(_), DbType::Sqlite) => "blob".to_owned(),
|
|
||||||
(ColumnType::VarBinary(_), DbType::MySql) => "varbinary".to_owned(),
|
|
||||||
(ColumnType::VarBinary(_), DbType::Postgres) => "bytea".to_owned(),
|
|
||||||
(ColumnType::VarBinary(_), DbType::Sqlite) => "varbinary_blob".to_owned(),
|
|
||||||
(ColumnType::Bit(_), DbType::MySql | DbType::Postgres) => "bit".to_owned(),
|
|
||||||
(ColumnType::VarBit(_), DbType::MySql) => "bit".to_owned(),
|
|
||||||
(ColumnType::VarBit(_), DbType::Postgres) => "varbit".to_owned(),
|
|
||||||
(ColumnType::Boolean, DbType::MySql | DbType::Postgres) => "bool".to_owned(),
|
|
||||||
(ColumnType::Boolean, DbType::Sqlite) => "boolean".to_owned(),
|
|
||||||
(ColumnType::Money(_), DbType::MySql) => "decimal".to_owned(),
|
|
||||||
(ColumnType::Money(_), DbType::Postgres) => "money".to_owned(),
|
|
||||||
(ColumnType::Money(_), DbType::Sqlite) => "real_money".to_owned(),
|
|
||||||
(ColumnType::Json, DbType::MySql | DbType::Postgres) => "json".to_owned(),
|
|
||||||
(ColumnType::Json, DbType::Sqlite) => "json_text".to_owned(),
|
|
||||||
(ColumnType::JsonBinary, DbType::MySql) => "json".to_owned(),
|
|
||||||
(ColumnType::JsonBinary, DbType::Postgres) => "jsonb".to_owned(),
|
|
||||||
(ColumnType::JsonBinary, DbType::Sqlite) => "jsonb_text".to_owned(),
|
|
||||||
(ColumnType::Uuid, DbType::MySql) => "binary(16)".to_owned(),
|
|
||||||
(ColumnType::Uuid, DbType::Postgres) => "uuid".to_owned(),
|
|
||||||
(ColumnType::Uuid, DbType::Sqlite) => "uuid_text".to_owned(),
|
|
||||||
(ColumnType::Enum { name, .. }, DbType::MySql) => {
|
|
||||||
format!("ENUM({})", name.to_string().to_upper_camel_case())
|
|
||||||
}
|
|
||||||
(ColumnType::Enum { name, .. }, DbType::Postgres) => {
|
|
||||||
name.to_string().to_uppercase()
|
|
||||||
}
|
|
||||||
(ColumnType::Enum { .. }, DbType::Sqlite) => "enum_text".to_owned(),
|
|
||||||
(ColumnType::Array(column_type), DbType::Postgres) => {
|
|
||||||
format!("{}[]", write_db_type(column_type, db_type)).to_uppercase()
|
|
||||||
}
|
|
||||||
(ColumnType::Vector(_), DbType::Postgres) => "vector".to_owned(),
|
|
||||||
(ColumnType::Cidr, DbType::Postgres) => "cidr".to_owned(),
|
|
||||||
(ColumnType::Inet, DbType::Postgres) => "inet".to_owned(),
|
|
||||||
(ColumnType::MacAddr, DbType::Postgres) => "macaddr".to_owned(),
|
|
||||||
(ColumnType::LTree, DbType::Postgres) => "ltree".to_owned(),
|
|
||||||
|
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write_db_type(&self.col_type, db_type)
|
|
||||||
}
|
|
||||||
pub fn get_rs_type(&self, date_time_crate: &DateTimeCrate) -> String {
|
|
||||||
fn write_rs_type(col_type: &ColumnType, date_time_crate: &DateTimeCrate) -> String {
|
|
||||||
#[allow(unreachable_patterns)]
|
|
||||||
match col_type {
|
|
||||||
ColumnType::Char(_)
|
|
||||||
| ColumnType::String(_)
|
|
||||||
| ColumnType::Text
|
|
||||||
| ColumnType::Custom(_) => "String".to_owned(),
|
|
||||||
ColumnType::TinyInteger => "i8".to_owned(),
|
|
||||||
ColumnType::SmallInteger => "i16".to_owned(),
|
|
||||||
ColumnType::Integer => "i32".to_owned(),
|
|
||||||
ColumnType::BigInteger => "i64".to_owned(),
|
|
||||||
ColumnType::TinyUnsigned => "u8".to_owned(),
|
|
||||||
ColumnType::SmallUnsigned => "u16".to_owned(),
|
|
||||||
ColumnType::Unsigned => "u32".to_owned(),
|
|
||||||
ColumnType::BigUnsigned => "u64".to_owned(),
|
|
||||||
ColumnType::Float => "f32".to_owned(),
|
|
||||||
ColumnType::Double => "f64".to_owned(),
|
|
||||||
ColumnType::Json | ColumnType::JsonBinary => "Json".to_owned(),
|
|
||||||
ColumnType::Date => match date_time_crate {
|
|
||||||
DateTimeCrate::Chrono => "Date".to_owned(),
|
|
||||||
DateTimeCrate::Time => "TimeDate".to_owned(),
|
|
||||||
},
|
|
||||||
ColumnType::Time => match date_time_crate {
|
|
||||||
DateTimeCrate::Chrono => "Time".to_owned(),
|
|
||||||
DateTimeCrate::Time => "TimeTime".to_owned(),
|
|
||||||
},
|
|
||||||
ColumnType::DateTime => match date_time_crate {
|
|
||||||
DateTimeCrate::Chrono => "DateTime".to_owned(),
|
|
||||||
DateTimeCrate::Time => "TimeDateTime".to_owned(),
|
|
||||||
},
|
|
||||||
ColumnType::Timestamp => match date_time_crate {
|
|
||||||
DateTimeCrate::Chrono => "DateTimeUtc".to_owned(),
|
|
||||||
DateTimeCrate::Time => "TimeDateTime".to_owned(),
|
|
||||||
},
|
|
||||||
ColumnType::TimestampWithTimeZone => match date_time_crate {
|
|
||||||
DateTimeCrate::Chrono => "DateTimeWithTimeZone".to_owned(),
|
|
||||||
DateTimeCrate::Time => "TimeDateTimeWithTimeZone".to_owned(),
|
|
||||||
},
|
|
||||||
ColumnType::Decimal(_) | ColumnType::Money(_) => "Decimal".to_owned(),
|
|
||||||
ColumnType::Uuid => "Uuid".to_owned(),
|
|
||||||
ColumnType::Binary(_) | ColumnType::VarBinary(_) | ColumnType::Blob => {
|
|
||||||
"Vec<u8>".to_owned()
|
|
||||||
}
|
|
||||||
ColumnType::Boolean => "bool".to_owned(),
|
|
||||||
ColumnType::Enum { name, .. } => name.to_string().to_upper_camel_case(),
|
|
||||||
ColumnType::Array(column_type) => {
|
|
||||||
format!("Vec<{}>", write_rs_type(column_type, date_time_crate))
|
|
||||||
}
|
|
||||||
ColumnType::Vector(_) => "::pgvector::Vector".to_owned(),
|
|
||||||
ColumnType::Bit(None | Some(1)) => "bool".to_owned(),
|
|
||||||
ColumnType::Bit(_) | ColumnType::VarBit(_) => "Vec<u8>".to_owned(),
|
|
||||||
ColumnType::Year => "i32".to_owned(),
|
|
||||||
ColumnType::Cidr | ColumnType::Inet => "IpNetwork".to_owned(),
|
|
||||||
ColumnType::Interval(_, _) | ColumnType::MacAddr | ColumnType::LTree => {
|
|
||||||
"String".to_owned()
|
|
||||||
}
|
|
||||||
_ => unimplemented!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
write_rs_type(&self.col_type, date_time_crate)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,147 +0,0 @@
|
|||||||
use super::{table::Table, CommentConfig, CommentConfigSerde, ModelConfig};
|
|
||||||
use color_eyre::Result;
|
|
||||||
use comfy_table::{ContentArrangement, Table as CTable};
|
|
||||||
use comment_parser::{CommentParser, Event};
|
|
||||||
|
|
||||||
const HEADER: &str = r#"== Schema Information"#;
|
|
||||||
const COMMENTHEAD: &str = r#"/*"#;
|
|
||||||
const COMMENTBODY: &str = r#" *"#;
|
|
||||||
const COMMENTTAIL: &str = r#"*/"#;
|
|
||||||
const SETTINGSDELIMITER: &str = r#"```"#;
|
|
||||||
|
|
||||||
pub struct ModelCommentGenerator {}
|
|
||||||
|
|
||||||
impl ModelCommentGenerator {
|
|
||||||
pub fn find_settings_block(file_content: &str) -> Option<String> {
|
|
||||||
let delimiter_length = SETTINGSDELIMITER.len();
|
|
||||||
let start_pos = file_content.find(SETTINGSDELIMITER)?;
|
|
||||||
let end_pos = file_content[start_pos + delimiter_length..].find(SETTINGSDELIMITER)?;
|
|
||||||
let content = &file_content[start_pos + delimiter_length..start_pos + end_pos];
|
|
||||||
let content = content.replace(&format!("\n{COMMENTBODY}"), "\n");
|
|
||||||
Some(content)
|
|
||||||
}
|
|
||||||
pub fn generate_comment(
|
|
||||||
table: Table,
|
|
||||||
file_content: &str,
|
|
||||||
config: &ModelConfig,
|
|
||||||
) -> Result<String> {
|
|
||||||
let rules = comment_parser::get_syntax("rust").unwrap();
|
|
||||||
let parser = CommentParser::new(file_content, rules);
|
|
||||||
for comment in parser {
|
|
||||||
if let Event::BlockComment(body, _) = comment {
|
|
||||||
if body.contains(HEADER) {
|
|
||||||
tracing::debug!("Found header");
|
|
||||||
let mut settings = config.comment.clone();
|
|
||||||
let mut new_settings = None;
|
|
||||||
if let Some(parsed_settings) = Self::find_settings_block(file_content) {
|
|
||||||
tracing::info!(?new_settings);
|
|
||||||
match serde_yaml::from_str::<CommentConfigSerde>(&parsed_settings) {
|
|
||||||
Ok(s) => {
|
|
||||||
new_settings = Some(s.clone());
|
|
||||||
settings = s.merge(&settings);
|
|
||||||
tracing::info!(?settings);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
if !settings.ignore_errors {
|
|
||||||
return Err(e.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tracing::debug!(?table.name, ?settings);
|
|
||||||
if settings.enable {
|
|
||||||
let comment =
|
|
||||||
Self::generate_comment_content(table, config, &settings, new_settings)?;
|
|
||||||
return Ok(file_content.replace(body, &comment));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let comment = Self::generate_comment_content(table, config, &config.comment, None)?;
|
|
||||||
Ok(format!("{}\n{}", comment, file_content))
|
|
||||||
}
|
|
||||||
pub fn generate_comment_content(
|
|
||||||
table: Table,
|
|
||||||
model_config: &ModelConfig,
|
|
||||||
config: &CommentConfig,
|
|
||||||
parsed_settings: Option<CommentConfigSerde>,
|
|
||||||
) -> Result<String> {
|
|
||||||
let mut model_config = model_config.clone();
|
|
||||||
model_config.comment = config.clone();
|
|
||||||
let column_info_table = if config.column_info {
|
|
||||||
let mut column_info_table = CTable::new();
|
|
||||||
let mut header = Vec::new();
|
|
||||||
if config.column_name {
|
|
||||||
header.push("Name");
|
|
||||||
}
|
|
||||||
if config.column_db_type {
|
|
||||||
header.push("DbType");
|
|
||||||
}
|
|
||||||
if config.column_rust_type {
|
|
||||||
header.push("RsType");
|
|
||||||
}
|
|
||||||
if config.column_attributes {
|
|
||||||
header.push("Attrs");
|
|
||||||
}
|
|
||||||
column_info_table
|
|
||||||
.load_preset(" -+=++ + ++")
|
|
||||||
.set_content_arrangement(ContentArrangement::Dynamic)
|
|
||||||
.set_header(header);
|
|
||||||
if let Some(width) = config.max_width {
|
|
||||||
column_info_table.set_width(width);
|
|
||||||
}
|
|
||||||
for column in &table.columns {
|
|
||||||
column_info_table.add_row(column.get_info_row(&model_config)?);
|
|
||||||
}
|
|
||||||
column_info_table.to_string()
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
let config_part = match parsed_settings {
|
|
||||||
Some(settings) => {
|
|
||||||
let settings_str = serde_yaml::to_string(&settings)?;
|
|
||||||
let settings_str = settings_str
|
|
||||||
.lines()
|
|
||||||
.map(|line| format!(" {}", line))
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
.join("\n");
|
|
||||||
format!(
|
|
||||||
"{SETTINGSDELIMITER}\n{}\n{SETTINGSDELIMITER}\n\n",
|
|
||||||
settings_str
|
|
||||||
)
|
|
||||||
}
|
|
||||||
None => String::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let table_name = &table.name;
|
|
||||||
let table_name_str = if config.table_name {
|
|
||||||
format!("Table: {}\n", table_name)
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
let string = format!("{HEADER}\n{config_part}{table_name_str}\n{column_info_table}");
|
|
||||||
|
|
||||||
let padded_string = Self::pad_comment(&string);
|
|
||||||
Ok(padded_string)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pad_comment(s: &str) -> String {
|
|
||||||
let parts = s.split('\n').collect::<Vec<_>>();
|
|
||||||
let mut padded = String::new();
|
|
||||||
for (index, part) in parts.iter().enumerate() {
|
|
||||||
let first = index == 0;
|
|
||||||
let comment = match first {
|
|
||||||
true => COMMENTHEAD.to_string(),
|
|
||||||
false => COMMENTBODY.to_string(),
|
|
||||||
};
|
|
||||||
let padded_part = format!("{} {}\n", comment, part);
|
|
||||||
padded.push_str(&padded_part);
|
|
||||||
}
|
|
||||||
padded.push_str(COMMENTTAIL);
|
|
||||||
padded
|
|
||||||
}
|
|
||||||
// pub async fn generate_header(&self, config: &Config, db_type: &DbType) -> Result<String> {
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
use crate::generator::file::GeneratedFileChunk;
|
|
||||||
|
|
||||||
use super::{comment::ModelCommentGenerator, table::Table, ModelConfig};
|
|
||||||
use color_eyre::Result;
|
|
||||||
use handlebars::Handlebars;
|
|
||||||
use heck::ToPascalCase;
|
|
||||||
use serde::Serialize;
|
|
||||||
use tokio::fs;
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct FileGenerator;
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
|
||||||
pub struct ModelContext {
|
|
||||||
entities_path: String,
|
|
||||||
model_path: String,
|
|
||||||
model_name: String,
|
|
||||||
active_model_name: String,
|
|
||||||
prelude_path: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileGenerator {
|
|
||||||
pub async fn generate_file<'a>(
|
|
||||||
table: Table,
|
|
||||||
config: &ModelConfig,
|
|
||||||
handlebars: &'a Handlebars<'a>,
|
|
||||||
) -> Result<Vec<GeneratedFileChunk>> {
|
|
||||||
let mut file_chunks = Vec::new();
|
|
||||||
file_chunks.push(GeneratedFileChunk {
|
|
||||||
path: config.models_path.join("mod.rs"),
|
|
||||||
content: format!("pub mod {};", table.name),
|
|
||||||
priority: 0,
|
|
||||||
});
|
|
||||||
let filepath = config.models_path.join(format!("{}.rs", table.name));
|
|
||||||
tracing::debug!(?filepath, "Generating file");
|
|
||||||
if filepath.exists() {
|
|
||||||
file_chunks
|
|
||||||
.extend(Self::handle_existing_file(table, &filepath, config, handlebars).await?);
|
|
||||||
} else {
|
|
||||||
let model_name = format!("{}Model", table.name).to_pascal_case();
|
|
||||||
let active_model_name = format!("{}ActiveModel", table.name).to_pascal_case();
|
|
||||||
let context = ModelContext {
|
|
||||||
entities_path: format!("super::{}", config.relative_entities_path),
|
|
||||||
model_path: table.name.clone(),
|
|
||||||
model_name,
|
|
||||||
active_model_name,
|
|
||||||
prelude_path: Some("super::prelude".to_string()),
|
|
||||||
};
|
|
||||||
tracing::info!(?context, "Generating new file",);
|
|
||||||
|
|
||||||
let content = if config.prelude {
|
|
||||||
file_chunks.push(GeneratedFileChunk {
|
|
||||||
path: config.models_path.join("prelude.rs"),
|
|
||||||
content: format!(
|
|
||||||
"pub use super::{}::{}::{{Model as {},ActiveModel as {},Entity as {}}};",
|
|
||||||
config.relative_entities_path,
|
|
||||||
table.name,
|
|
||||||
context.model_name,
|
|
||||||
context.active_model_name,
|
|
||||||
table.name.clone().to_pascal_case()
|
|
||||||
),
|
|
||||||
priority: 0,
|
|
||||||
});
|
|
||||||
handlebars.render("modelprelude", &context)?
|
|
||||||
} else {
|
|
||||||
handlebars.render("model", &context)?
|
|
||||||
};
|
|
||||||
|
|
||||||
file_chunks.push(GeneratedFileChunk {
|
|
||||||
path: filepath,
|
|
||||||
content,
|
|
||||||
priority: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Ok(file_chunks)
|
|
||||||
}
|
|
||||||
async fn handle_existing_file<'a>(
|
|
||||||
table: Table,
|
|
||||||
filepath: &PathBuf,
|
|
||||||
config: &ModelConfig,
|
|
||||||
_handlebars: &'a Handlebars<'a>,
|
|
||||||
) -> Result<Vec<GeneratedFileChunk>> {
|
|
||||||
let mut file_chunks = Vec::new();
|
|
||||||
let mut file_content = fs::read_to_string(filepath).await?;
|
|
||||||
if config.comment.enable {
|
|
||||||
file_content = ModelCommentGenerator::generate_comment(table, &file_content, config)?;
|
|
||||||
}
|
|
||||||
file_chunks.push(GeneratedFileChunk {
|
|
||||||
path: filepath.clone(),
|
|
||||||
content: file_content,
|
|
||||||
priority: 0,
|
|
||||||
});
|
|
||||||
Ok(file_chunks)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,189 +1,176 @@
|
|||||||
use super::{Module, ModulesContext};
|
use std::path::PathBuf;
|
||||||
use color_eyre::Result;
|
|
||||||
use serde::Deserialize;
|
use crate::generator::file::pathbuf_to_rust_path;
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
|
||||||
|
use super::{
|
||||||
|
discovery::{table::Table, DiscoveredSchema},
|
||||||
|
sea_orm::SeaOrmConfig,
|
||||||
|
templates::TemplateConfig,
|
||||||
|
Module, ModulesContext,
|
||||||
|
};
|
||||||
|
use color_eyre::{
|
||||||
|
eyre::{eyre, Context, ContextCompat},
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
use minijinja::Environment;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||||
|
#[serde(default)]
|
||||||
pub struct ModelsConfig {
|
pub struct ModelsConfig {
|
||||||
pub enable: bool,
|
pub enable: bool,
|
||||||
pub database_schema: String,
|
pub path: Option<PathBuf>,
|
||||||
pub max_connections: u32,
|
pub prelude: bool,
|
||||||
pub acquire_timeout: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[derive(Debug)]
|
impl Default for ModelsConfig {
|
||||||
// pub struct ModelsModule;
|
fn default() -> Self {
|
||||||
//
|
Self {
|
||||||
// #[async_trait::async_trait]
|
enable: false,
|
||||||
// impl Module for ModelsModule {
|
path: None,
|
||||||
// fn init(&self, ctx: &mut ModulesContext) -> Result<()> {
|
prelude: true,
|
||||||
// Ok(())
|
}
|
||||||
// }
|
}
|
||||||
//
|
}
|
||||||
// async fn validate(&self, ctx: &mut ModulesContext) -> Result<bool> {
|
|
||||||
// Ok(false)
|
#[derive(Debug, Clone, Serialize)]
|
||||||
// }
|
pub struct ModelTemplateContext {
|
||||||
// }
|
entities_path: Option<String>,
|
||||||
//
|
tables: Option<Vec<Table>>,
|
||||||
//
|
prelude_path: Option<String>,
|
||||||
// use crate::{
|
table_name: Option<String>,
|
||||||
// config::{sea_orm_config::DateTimeCrate, Config},
|
config: ModelsConfig,
|
||||||
// generator::{
|
}
|
||||||
// discover::{self, DbType},
|
|
||||||
// file::GeneratedFileChunk,
|
impl ModelTemplateContext {
|
||||||
// },
|
pub fn new(
|
||||||
// };
|
entities_path: Option<String>,
|
||||||
// use color_eyre::Result;
|
prelude_path: Option<String>,
|
||||||
// use file::FileGenerator;
|
tables: Option<Vec<Table>>,
|
||||||
// use handlebars::Handlebars;
|
table_name: Option<String>,
|
||||||
// use sea_orm_codegen::{EntityTransformer, EntityWriterContext, OutputFile};
|
config: ModelsConfig,
|
||||||
// use sea_schema::sea_query::TableCreateStatement;
|
) -> Self {
|
||||||
// use serde::{Deserialize, Serialize};
|
Self {
|
||||||
// use std::path::PathBuf;
|
entities_path,
|
||||||
// use table::Table;
|
tables,
|
||||||
//
|
prelude_path,
|
||||||
// pub mod column;
|
table_name,
|
||||||
// pub mod comment;
|
config,
|
||||||
// pub mod file;
|
}
|
||||||
// pub mod table;
|
}
|
||||||
// #[derive(Debug, Clone)]
|
}
|
||||||
// pub struct ModelConfig {
|
|
||||||
// pub models_path: PathBuf,
|
#[derive(Debug)]
|
||||||
// pub prelude: bool,
|
pub struct ModelsModule;
|
||||||
// pub entities_path: PathBuf,
|
|
||||||
// pub relative_entities_path: String,
|
#[async_trait::async_trait]
|
||||||
// pub enable: bool,
|
impl Module for ModelsModule {
|
||||||
// pub comment: CommentConfig,
|
fn init(&mut self, ctx: &mut ModulesContext) -> Result<()> {
|
||||||
// pub db_type: DbType,
|
ctx.get_config_auto::<ModelsConfig>("modules.model")?;
|
||||||
// }
|
Ok(())
|
||||||
// #[derive(Debug, Clone)]
|
}
|
||||||
// pub struct CommentConfig {
|
|
||||||
// pub max_width: Option<u16>,
|
async fn validate(&mut self, ctx: &mut ModulesContext) -> Result<bool> {
|
||||||
// pub enable: bool,
|
let map = ctx.get_anymap();
|
||||||
// pub table_name: bool,
|
|
||||||
// pub column_info: bool,
|
if let (Some(config), Some(template_config), Some(sea_orm_config)) = (
|
||||||
// pub column_name: bool,
|
map.get::<ModelsConfig>(),
|
||||||
// pub column_rust_type: bool,
|
map.get::<TemplateConfig>(),
|
||||||
// pub column_db_type: bool,
|
map.get::<SeaOrmConfig>(),
|
||||||
// pub column_attributes: bool,
|
) {
|
||||||
// pub ignore_errors: bool,
|
if config.enable && !template_config.enable {
|
||||||
// pub date_time_crate: DateTimeCrate,
|
return Err(eyre!(
|
||||||
// }
|
"\"modules.template.enable\" must be enabled to use \"modules.model.enable\""
|
||||||
// #[derive(Debug, Clone, Serialize, Deserialize)]
|
));
|
||||||
// pub struct CommentConfigSerde {
|
}
|
||||||
// #[serde(skip_serializing_if = "Option::is_none")]
|
if config.enable && !sea_orm_config.enable {
|
||||||
// pub max_width: Option<u16>,
|
return Err(eyre!(
|
||||||
// #[serde(skip_serializing_if = "Option::is_none")]
|
"\"modules.sea_orm.enable\" must be enabled to use \"modules.model.enable\""
|
||||||
// pub enable: Option<bool>,
|
));
|
||||||
// #[serde(skip_serializing_if = "Option::is_none")]
|
}
|
||||||
// pub table_name: Option<bool>,
|
if config.enable && config.path.is_none() {
|
||||||
// #[serde(skip_serializing_if = "Option::is_none")]
|
return Err(eyre!(
|
||||||
// pub name: Option<bool>,
|
"\"modules.model.path\" must be set to use \"modules.model.enable\""
|
||||||
// #[serde(skip_serializing_if = "Option::is_none")]
|
));
|
||||||
// pub info: Option<bool>,
|
}
|
||||||
// #[serde(skip_serializing_if = "Option::is_none")]
|
Ok(config.enable && template_config.enable)
|
||||||
// pub rust_type: Option<bool>,
|
} else {
|
||||||
// #[serde(skip_serializing_if = "Option::is_none")]
|
// One or both keys are missing
|
||||||
// pub db_type: Option<bool>,
|
Ok(false)
|
||||||
// #[serde(skip_serializing_if = "Option::is_none")]
|
}
|
||||||
// pub attributes: Option<bool>,
|
}
|
||||||
// }
|
async fn execute(&mut self, ctx: &mut ModulesContext) -> Result<()> {
|
||||||
// impl CommentConfigSerde {
|
let mut files: Vec<(PathBuf, String)> = Vec::new();
|
||||||
// pub fn merge(&self, config: &CommentConfig) -> CommentConfig {
|
let map = ctx.get_anymap();
|
||||||
// CommentConfig {
|
|
||||||
// max_width: self.max_width.or(config.max_width),
|
if let (Some(config), Some(templates), Some(sea_orm_config), Some(schema)) = (
|
||||||
// table_name: self.table_name.unwrap_or(config.table_name),
|
map.get::<ModelsConfig>(),
|
||||||
// column_name: self.name.unwrap_or(config.column_name),
|
map.get::<Environment<'static>>(),
|
||||||
// column_info: self.info.unwrap_or(config.column_info),
|
map.get::<SeaOrmConfig>(),
|
||||||
// column_rust_type: self.rust_type.unwrap_or(config.column_rust_type),
|
map.get::<DiscoveredSchema>(),
|
||||||
// column_db_type: self.db_type.unwrap_or(config.column_db_type),
|
) {
|
||||||
// column_attributes: self.attributes.unwrap_or(config.column_attributes),
|
let models_path = config.path.clone().unwrap();
|
||||||
// ignore_errors: config.ignore_errors,
|
tracing::info!(?models_path, "Models path");
|
||||||
// enable: self.enable.unwrap_or(config.enable),
|
let entities_path = sea_orm_config.path.clone().unwrap();
|
||||||
// date_time_crate: config.date_time_crate.clone(),
|
let mod_path = models_path.join("mod.rs");
|
||||||
// }
|
|
||||||
// }
|
let relative_entities_path = pathdiff::diff_paths(&entities_path, &mod_path)
|
||||||
// }
|
.context("Failed to calculate relative path")?;
|
||||||
//
|
let relative_entities_rust_path = pathbuf_to_rust_path(relative_entities_path);
|
||||||
// impl ModelConfig {
|
let context = ModelTemplateContext::new(
|
||||||
// pub fn new(config: Config, db_type: DbType) -> Self {
|
Some(relative_entities_rust_path.clone()),
|
||||||
// let models_path = config.output.path.join(&config.output.models.path);
|
None,
|
||||||
// let entities_path = models_path.join(&config.output.models.entities);
|
Some(schema.tables.clone()),
|
||||||
// ModelConfig {
|
None,
|
||||||
// db_type,
|
config.clone(),
|
||||||
// prelude: config.output.models.prelude,
|
);
|
||||||
// entities_path,
|
if config.prelude {
|
||||||
// models_path,
|
let prelude = templates
|
||||||
// relative_entities_path: config.output.models.entities.clone(),
|
.get_template("model_prelude")?
|
||||||
// enable: config.output.models.enable,
|
.render(&context)
|
||||||
// comment: CommentConfig {
|
.context("Failed to render model prelude part")?;
|
||||||
// max_width: config.output.models.comment.max_width,
|
files.push((models_path.join("prelude.rs"), prelude));
|
||||||
// enable: config.output.models.comment.enable,
|
}
|
||||||
// table_name: config.output.models.comment.table_name,
|
|
||||||
// column_name: config.output.models.comment.column_name,
|
let mod_content = templates
|
||||||
// column_info: config.output.models.comment.column_info,
|
.get_template("model_mod")?
|
||||||
// column_rust_type: config.output.models.comment.column_rust_type,
|
.render(&context)
|
||||||
// column_db_type: config.output.models.comment.column_db_type,
|
.context("Failed to render model mod")?;
|
||||||
// column_attributes: config.output.models.comment.column_attributes,
|
files.push((mod_path.clone(), mod_content));
|
||||||
// ignore_errors: config.output.models.comment.ignore_errors,
|
for table in &schema.tables {
|
||||||
// date_time_crate: config.sea_orm.entity.date_time_crate,
|
tracing::debug!(?table, "Generating model for table");
|
||||||
// },
|
let path = models_path.join(format!("{}.rs", table.name));
|
||||||
// }
|
|
||||||
// }
|
let context = ModelTemplateContext::new(
|
||||||
// }
|
Some(relative_entities_rust_path.clone()),
|
||||||
//
|
if config.prelude {
|
||||||
// pub async fn generate_models<'a>(
|
Some("super::prelude".to_string())
|
||||||
// database_url: &str,
|
} else {
|
||||||
// config: &Config,
|
None
|
||||||
// handlebars: &'a Handlebars<'a>,
|
},
|
||||||
// ) -> Result<Vec<GeneratedFileChunk>> {
|
None,
|
||||||
// let mut files = Vec::new();
|
Some(table.name.clone()),
|
||||||
// let db_filter = config.sea_orm.entity.tables.get_filter();
|
config.clone(),
|
||||||
// let (table_stmts, db_type) =
|
);
|
||||||
// discover::get_tables(database_url.to_owned(), db_filter, &config.db).await?;
|
|
||||||
// let model_config = ModelConfig::new(config.clone(), db_type);
|
if path.exists() {
|
||||||
//
|
tracing::debug!(?path, "Model file already exists");
|
||||||
// let writer_context = config.clone().into();
|
continue;
|
||||||
// files.extend(
|
}
|
||||||
// generate_entities(table_stmts.clone(), model_config.clone(), writer_context).await?,
|
|
||||||
// );
|
let content = templates
|
||||||
//
|
.get_template("model")?
|
||||||
// files.push(GeneratedFileChunk {
|
.render(&context)
|
||||||
// path: model_config.models_path.join("mod.rs"),
|
.context("Failed to render model")?;
|
||||||
// content: format!("pub mod {};", model_config.relative_entities_path),
|
files.push((path.clone(), content.clone()));
|
||||||
// priority: 0,
|
}
|
||||||
// });
|
} else {
|
||||||
// let tables = table_stmts
|
// One or both keys are missing
|
||||||
// .into_iter()
|
}
|
||||||
// .map(Table::new)
|
tracing::info!(?files, "Generated model files");
|
||||||
// .collect::<Result<Vec<Table>>>()?;
|
let file_manager = ctx.get_file_manager_mut();
|
||||||
//
|
for (output_path, content) in files {
|
||||||
// if model_config.enable {
|
file_manager.insert(&output_path, &content, None)?;
|
||||||
// for table in tables {
|
}
|
||||||
// files.extend(FileGenerator::generate_file(table, &model_config, handlebars).await?);
|
Ok(())
|
||||||
// }
|
}
|
||||||
// if model_config.prelude {
|
}
|
||||||
// files.push(GeneratedFileChunk {
|
|
||||||
// path: model_config.models_path.join("mod.rs"),
|
|
||||||
// content: String::from("pub mod prelude;"),
|
|
||||||
// priority: 0,
|
|
||||||
// })
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// Ok(files)
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// pub async fn generate_entities(
|
|
||||||
// table_statements: Vec<TableCreateStatement>,
|
|
||||||
// config: ModelConfig,
|
|
||||||
// writer_context: EntityWriterContext,
|
|
||||||
// ) -> Result<Vec<GeneratedFileChunk>> {
|
|
||||||
// let output = EntityTransformer::transform(table_statements)?.generate(&writer_context);
|
|
||||||
// Ok(output
|
|
||||||
// .files
|
|
||||||
// .into_iter()
|
|
||||||
// .map(|OutputFile { name, content }| GeneratedFileChunk {
|
|
||||||
// path: config.entities_path.join(name),
|
|
||||||
// content,
|
|
||||||
// priority: 0,
|
|
||||||
// })
|
|
||||||
// .collect::<Vec<_>>())
|
|
||||||
// }
|
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
use super::column::Column;
|
|
||||||
use color_eyre::{eyre::eyre, Result};
|
|
||||||
use sea_schema::sea_query::{self, TableCreateStatement};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Table {
|
|
||||||
pub name: String,
|
|
||||||
pub columns: Vec<Column>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Table {
|
|
||||||
pub fn new(statement: TableCreateStatement) -> Result<Table> {
|
|
||||||
let table_name = match statement.get_table_name() {
|
|
||||||
Some(table_ref) => match table_ref {
|
|
||||||
sea_query::TableRef::Table(t)
|
|
||||||
| sea_query::TableRef::SchemaTable(_, t)
|
|
||||||
| sea_query::TableRef::DatabaseSchemaTable(_, _, t)
|
|
||||||
| sea_query::TableRef::TableAlias(t, _)
|
|
||||||
| sea_query::TableRef::SchemaTableAlias(_, t, _)
|
|
||||||
| sea_query::TableRef::DatabaseSchemaTableAlias(_, _, t, _) => t.to_string(),
|
|
||||||
_ => unimplemented!(),
|
|
||||||
},
|
|
||||||
None => return Err(eyre!("Table name not found")),
|
|
||||||
};
|
|
||||||
tracing::debug!(?table_name);
|
|
||||||
let columns_raw = statement.get_columns();
|
|
||||||
let indexes = statement.get_indexes();
|
|
||||||
for column in columns_raw {
|
|
||||||
tracing::debug!(?column);
|
|
||||||
}
|
|
||||||
for index in indexes {
|
|
||||||
tracing::debug!(?index);
|
|
||||||
}
|
|
||||||
let columns = columns_raw
|
|
||||||
.iter()
|
|
||||||
.map(|column| {
|
|
||||||
let name = column.get_column_name();
|
|
||||||
let index = indexes
|
|
||||||
.iter()
|
|
||||||
.find(|index| index.get_index_spec().get_column_names().contains(&name));
|
|
||||||
Column::new(column.clone(), index.cloned())
|
|
||||||
})
|
|
||||||
.collect::<Result<Vec<Column>>>()?;
|
|
||||||
tracing::debug!(?columns);
|
|
||||||
Ok(Table {
|
|
||||||
columns,
|
|
||||||
name: table_name,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use serde::{Deserialize, Deserializer, Serialize};
|
use serde::{Deserialize, Deserializer, Serialize};
|
||||||
use serde_yaml::Value;
|
use toml::Value;
|
||||||
|
|
||||||
use sea_orm_codegen::{DateTimeCrate as CodegenDateTimeCrate, WithPrelude, WithSerde};
|
use sea_orm_codegen::{DateTimeCrate as CodegenDateTimeCrate, WithPrelude, WithSerde};
|
||||||
|
|
||||||
@@ -43,8 +43,8 @@ impl<'de> Deserialize<'de> for SerdeEnable {
|
|||||||
match value {
|
match value {
|
||||||
Value::String(s) if s == "serialize" => Ok(SerdeEnable::Serialize),
|
Value::String(s) if s == "serialize" => Ok(SerdeEnable::Serialize),
|
||||||
Value::String(s) if s == "deserialize" => Ok(SerdeEnable::Deserialize),
|
Value::String(s) if s == "deserialize" => Ok(SerdeEnable::Deserialize),
|
||||||
Value::Bool(true) => Ok(SerdeEnable::Both),
|
Value::Boolean(true) => Ok(SerdeEnable::Both),
|
||||||
Value::Bool(false) => Ok(SerdeEnable::None),
|
Value::Boolean(false) => Ok(SerdeEnable::None),
|
||||||
_ => Err(serde::de::Error::custom(
|
_ => Err(serde::de::Error::custom(
|
||||||
"expected 'serialize', 'deserialize', 'true' or 'false'",
|
"expected 'serialize', 'deserialize', 'true' or 'false'",
|
||||||
)),
|
)),
|
||||||
@@ -72,8 +72,8 @@ impl<'de> Deserialize<'de> for Prelude {
|
|||||||
let value = Value::deserialize(deserializer)?;
|
let value = Value::deserialize(deserializer)?;
|
||||||
|
|
||||||
match value {
|
match value {
|
||||||
Value::Bool(true) => Ok(Prelude::Enabled),
|
Value::Boolean(true) => Ok(Prelude::Enabled),
|
||||||
Value::Bool(false) => Ok(Prelude::Disabled),
|
Value::Boolean(false) => Ok(Prelude::Disabled),
|
||||||
Value::String(s) if s == "allow_unused_imports" => Ok(Prelude::AllowUnusedImports),
|
Value::String(s) if s == "allow_unused_imports" => Ok(Prelude::AllowUnusedImports),
|
||||||
_ => Err(serde::de::Error::custom(
|
_ => Err(serde::de::Error::custom(
|
||||||
"expected 'true', 'false', or 'allow_unused_imports'",
|
"expected 'true', 'false', or 'allow_unused_imports'",
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ impl Module for SeaOrmModule {
|
|||||||
}
|
}
|
||||||
async fn execute(&mut self, ctx: &mut ModulesContext) -> Result<()> {
|
async fn execute(&mut self, ctx: &mut ModulesContext) -> Result<()> {
|
||||||
let map = ctx.get_anymap();
|
let map = ctx.get_anymap();
|
||||||
|
let mut outputs = vec![];
|
||||||
if let (Some(statements), Some(config), Some(discovery_config)) = (
|
if let (Some(statements), Some(config), Some(discovery_config)) = (
|
||||||
map.get::<RawDiscoveredStatements>(),
|
map.get::<RawDiscoveredStatements>(),
|
||||||
map.get::<SeaOrmConfig>(),
|
map.get::<SeaOrmConfig>(),
|
||||||
@@ -86,18 +87,20 @@ impl Module for SeaOrmModule {
|
|||||||
config.entity.extra_derives.eenum.clone(),
|
config.entity.extra_derives.eenum.clone(),
|
||||||
config.entity.extra_attributes.eenum.clone(),
|
config.entity.extra_attributes.eenum.clone(),
|
||||||
false,
|
false,
|
||||||
true,
|
false,
|
||||||
);
|
);
|
||||||
let output = EntityTransformer::transform(statements.statements.clone())?
|
let output = EntityTransformer::transform(statements.statements.clone())?
|
||||||
.generate(&writer_context);
|
.generate(&writer_context);
|
||||||
for file in output.files {
|
outputs.extend(output.files.into_iter().map(|file| {
|
||||||
let file_path = config.path.clone().unwrap_or_default();
|
let file_path = config.path.clone().unwrap_or_default();
|
||||||
let file_path = file_path.join(file.name);
|
let file_path = file_path.join(file.name);
|
||||||
tracing::info!(?file_path, "Generating file");
|
(file_path, file.content)
|
||||||
|
}));
|
||||||
// let mut file_generator = crate::generator::file::FileGenerator::new(file_path);
|
|
||||||
// file_generator.write(file.content)?;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let file_manager = ctx.get_file_manager_mut();
|
||||||
|
for (output_path, content) in outputs {
|
||||||
|
file_manager.insert(&output_path, &content, None)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -1,16 +1,84 @@
|
|||||||
use crate::generator::DatabaseUrl;
|
|
||||||
|
|
||||||
use super::{Module, ModulesContext};
|
use super::{Module, ModulesContext};
|
||||||
use color_eyre::Result;
|
use color_eyre::{
|
||||||
use handlebars::Handlebars;
|
eyre::{eyre, ContextCompat},
|
||||||
|
Result,
|
||||||
|
};
|
||||||
|
use heck::ToPascalCase;
|
||||||
|
use minijinja::Environment;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_inline_default::serde_inline_default;
|
use std::{collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
|
use include_dir::{include_dir, Dir, DirEntry};
|
||||||
|
use tokio::fs;
|
||||||
|
|
||||||
|
static TEMPLATE_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/templates");
|
||||||
|
|
||||||
#[serde_inline_default]
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
|
#[serde(default)]
|
||||||
pub struct TemplateConfig {
|
pub struct TemplateConfig {
|
||||||
#[serde_inline_default(false)]
|
|
||||||
pub enable: bool,
|
pub enable: bool,
|
||||||
|
pub path: Option<PathBuf>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub tables: HashMap<String, MapString>,
|
||||||
|
}
|
||||||
|
impl TemplateConfig {
|
||||||
|
pub fn to_paths(&self) -> Vec<(String, PathBuf)> {
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
for (key, value) in &self.tables {
|
||||||
|
let map_string = value.clone();
|
||||||
|
paths.extend(map_string.into_paths(key.clone()));
|
||||||
|
}
|
||||||
|
let root = self.path.clone().unwrap_or_default();
|
||||||
|
|
||||||
|
paths
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, path)| {
|
||||||
|
let new_path = root.clone().join(path);
|
||||||
|
(key, new_path)
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug, Clone, Deserialize, Eq, PartialEq)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
pub enum MapString {
|
||||||
|
Map(HashMap<String, MapString>),
|
||||||
|
PathBuf(PathBuf),
|
||||||
|
}
|
||||||
|
impl MapString {
|
||||||
|
pub fn into_paths(self, prefix: String) -> Vec<(String, PathBuf)> {
|
||||||
|
fn write_path(prefix: String, string: MapString) -> Vec<(String, PathBuf)> {
|
||||||
|
let mut strings = Vec::new();
|
||||||
|
match string {
|
||||||
|
MapString::Map(inner) => {
|
||||||
|
for (key, value) in inner {
|
||||||
|
let new_prefix = if prefix.is_empty() {
|
||||||
|
key
|
||||||
|
} else {
|
||||||
|
format!("{}.{}", prefix, key)
|
||||||
|
};
|
||||||
|
strings.extend(write_path(new_prefix, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MapString::PathBuf(pathbuf) => {
|
||||||
|
strings.push((prefix, pathbuf));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
strings.sort();
|
||||||
|
strings
|
||||||
|
}
|
||||||
|
write_path(prefix, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TemplateConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
enable: true,
|
||||||
|
tables: HashMap::new(),
|
||||||
|
path: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TemplateModule;
|
pub struct TemplateModule;
|
||||||
@@ -18,22 +86,79 @@ pub struct TemplateModule;
|
|||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl Module for TemplateModule {
|
impl Module for TemplateModule {
|
||||||
fn init(&mut self, ctx: &mut ModulesContext) -> Result<()> {
|
fn init(&mut self, ctx: &mut ModulesContext) -> Result<()> {
|
||||||
let registry: Handlebars<'static> = Handlebars::new();
|
ctx.get_config_auto::<TemplateConfig>("modules.template")?;
|
||||||
ctx.get_anymap_mut().insert(registry);
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn validate(&mut self, ctx: &mut ModulesContext) -> Result<bool> {
|
async fn validate(&mut self, ctx: &mut ModulesContext) -> Result<bool> {
|
||||||
// let map = ctx.get_anymap();
|
let map = ctx.get_anymap_mut();
|
||||||
//
|
//
|
||||||
// if let (Some(config), Some(_)) = (map.get::<DiscoveryConfig>(), map.get::<DatabaseUrl>()) {
|
if let Some(config) = map.get::<TemplateConfig>() {
|
||||||
// Ok(config.enable)
|
if config.enable {
|
||||||
// } else {
|
for templates in config.to_paths() {
|
||||||
// // One or both keys are missing
|
let path = templates.1;
|
||||||
// Ok(false)
|
if !path.exists() {
|
||||||
// }
|
return Err(eyre!("Template path does not exist: {}", path.display()));
|
||||||
Ok(true)
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(config.enable)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
async fn execute(&mut self, ctx: &mut ModulesContext) -> Result<()> {
|
async fn execute(&mut self, ctx: &mut ModulesContext) -> Result<()> {
|
||||||
|
let mut env: Environment<'static> = Environment::new();
|
||||||
|
env.add_function("pascalCase", |f: String| f.to_pascal_case());
|
||||||
|
// registry.set_strict_mode(true);
|
||||||
|
if let Some(config) = ctx.get_anymap().get::<TemplateConfig>() {
|
||||||
|
for (template, path) in config.to_paths() {
|
||||||
|
tracing::debug!(?template, ?path, "Registering template");
|
||||||
|
let content = fs::read_to_string(path).await?;
|
||||||
|
env.add_template_owned(template, content)?;
|
||||||
|
}
|
||||||
|
Self::register_default_templates(TEMPLATE_DIR.entries(), &mut env).await?;
|
||||||
|
}
|
||||||
|
ctx.get_anymap_mut().insert(env);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TemplateModule {
|
||||||
|
async fn register_default_templates<'a>(
|
||||||
|
entries: &[DirEntry<'a>],
|
||||||
|
env: &mut Environment<'a>,
|
||||||
|
) -> Result<()> {
|
||||||
|
for entry in entries.iter().filter(|file| {
|
||||||
|
file.path()
|
||||||
|
.extension()
|
||||||
|
.is_some_and(|f| f.to_str().is_some_and(|f| f == "jinja"))
|
||||||
|
|| file.as_dir().is_some()
|
||||||
|
}) {
|
||||||
|
match entry {
|
||||||
|
DirEntry::File(file) => {
|
||||||
|
let path = file.path().with_extension("");
|
||||||
|
let name = path
|
||||||
|
.to_str()
|
||||||
|
.context("Failed to convert path to str")?
|
||||||
|
.replace("/", ".");
|
||||||
|
let content = file
|
||||||
|
.contents_utf8()
|
||||||
|
.context(format!("Template {} failed to parse", name))?
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
|
tracing::debug!(?name, "Registering template");
|
||||||
|
if env.get_template(name.as_str()).is_err() {
|
||||||
|
env.add_template_owned(name, content)?;
|
||||||
|
} else {
|
||||||
|
tracing::debug!(?name, "Template already registered, skipping");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DirEntry::Dir(dir) => {
|
||||||
|
Box::pin(Self::register_default_templates(dir.entries(), env)).await?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
60
src/main.rs
60
src/main.rs
@@ -1,25 +1,22 @@
|
|||||||
mod config;
|
// mod config;
|
||||||
mod generator;
|
mod generator;
|
||||||
mod templates;
|
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use color_eyre::{eyre::eyre, Result};
|
use color_eyre::Result;
|
||||||
use config::Config;
|
use tokio::fs;
|
||||||
use figment::{
|
|
||||||
providers::{Format, Serialized, Yaml},
|
|
||||||
Figment,
|
|
||||||
};
|
|
||||||
use handlebars::Handlebars;
|
|
||||||
use tokio::{fs, io::AsyncWriteExt, process::Command};
|
|
||||||
use toml_edit::DocumentMut;
|
use toml_edit::DocumentMut;
|
||||||
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
use tracing_subscriber::{fmt, layer::SubscriberExt, util::SubscriberInitExt, EnvFilter};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
struct Args {
|
pub struct Args {
|
||||||
#[clap(short, long, default_value = "generator.toml")]
|
#[clap(short, long, default_value = "generator.toml")]
|
||||||
config: String,
|
config: String,
|
||||||
#[clap(short, long, env = "DATABASE_URL")]
|
#[clap(short, long, env = "DATABASE_URL")]
|
||||||
database_url: String,
|
database_url: String,
|
||||||
|
#[clap(short, long)]
|
||||||
|
workdir: Option<String>,
|
||||||
|
#[clap(short, long, default_value = "true")]
|
||||||
|
rustfmt: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
@@ -32,43 +29,14 @@ async fn main() -> Result<()> {
|
|||||||
.init();
|
.init();
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
// let config: Config = Figment::new()
|
|
||||||
// .merge(Serialized::defaults(Config::default()))
|
|
||||||
// .merge(Yaml::file(&args.config))
|
|
||||||
// .extract()?;
|
|
||||||
// tracing::info!(?config);
|
|
||||||
tracing::info!(?args);
|
tracing::info!(?args);
|
||||||
|
// change workdir
|
||||||
// let mut handlebars: Registry = ;
|
if let Some(workdir) = &args.workdir {
|
||||||
// templates::register_templates(&mut handlebars, &config).await?;
|
std::env::set_current_dir(workdir)?;
|
||||||
let config = fs::read_to_string(args.config).await?;
|
}
|
||||||
|
let config = fs::read_to_string(&args.config).await?;
|
||||||
let root_config = config.parse::<DocumentMut>()?;
|
let root_config = config.parse::<DocumentMut>()?;
|
||||||
|
|
||||||
let outputs = generator::generate(&args.database_url, root_config).await?;
|
generator::generate(args, root_config).await?;
|
||||||
//
|
|
||||||
// // tracing::info!(?outputs, "Generated files");
|
|
||||||
// for output in outputs.iter() {
|
|
||||||
// tracing::info!(?output, "Generated chunk");
|
|
||||||
// // let mut file = fs::File::create(&output.path).await?;
|
|
||||||
// // file.write_all(output.content.as_bytes()).await?;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// let merged_outputs = generator::file::combine_chunks(outputs)?;
|
|
||||||
// for output in merged_outputs.iter() {
|
|
||||||
// tracing::info!(?output.path, "Merged file");
|
|
||||||
// let parent = output.path.parent().unwrap();
|
|
||||||
// if !parent.exists() {
|
|
||||||
// fs::create_dir_all(parent).await?;
|
|
||||||
// }
|
|
||||||
// let mut file = fs::File::create(&output.path).await?;
|
|
||||||
// file.write_all(output.content.as_bytes()).await?;
|
|
||||||
// }
|
|
||||||
// for output in merged_outputs.iter() {
|
|
||||||
// tracing::info!(?output.path, "Running rustfmt");
|
|
||||||
// let exit_status = Command::new("rustfmt").arg(&output.path).status().await?;
|
|
||||||
// if !exit_status.success() {
|
|
||||||
// return Err(eyre!("Failed to run rustfmt"));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
use crate::config::Config;
|
|
||||||
use color_eyre::eyre::{ContextCompat, Result};
|
|
||||||
use handlebars::Handlebars;
|
|
||||||
use include_dir::{include_dir, Dir, DirEntry};
|
|
||||||
use serde_yaml::Value;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use tokio::fs;
|
|
||||||
|
|
||||||
static TEMPLATE_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/templates");
|
|
||||||
|
|
||||||
async fn handle_direntries<'a>(
|
|
||||||
entries: &[DirEntry<'a>],
|
|
||||||
handlebars: &mut Handlebars<'a>,
|
|
||||||
) -> Result<()> {
|
|
||||||
for entry in entries.iter().filter(|file| {
|
|
||||||
file.path()
|
|
||||||
.extension()
|
|
||||||
.is_some_and(|f| f.to_str().is_some_and(|f| f == "hbs"))
|
|
||||||
|| file.as_dir().is_some()
|
|
||||||
}) {
|
|
||||||
match entry {
|
|
||||||
DirEntry::File(file) => {
|
|
||||||
let path = file.path().with_extension("");
|
|
||||||
let name = path
|
|
||||||
.to_str()
|
|
||||||
.context("Failed to convert path to str")?
|
|
||||||
.replace("/", ".");
|
|
||||||
let content = file
|
|
||||||
.contents_utf8()
|
|
||||||
.context(format!("Template {} failed to parse", name))?;
|
|
||||||
|
|
||||||
tracing::debug!(?name, "Registering template");
|
|
||||||
if !handlebars.has_template(&name) {
|
|
||||||
handlebars.register_template_string(&name, content)?;
|
|
||||||
} else {
|
|
||||||
tracing::debug!(?name, "Template already registered, skipping");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DirEntry::Dir(dir) => {
|
|
||||||
Box::pin(handle_direntries(dir.entries(), handlebars)).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
pub async fn register_templates(handlebars: &mut Handlebars<'_>, config: &Config) -> Result<()> {
|
|
||||||
if let Some(templates) = &config.templates {
|
|
||||||
for (name, value) in templates.iter() {
|
|
||||||
let Value::String(name) = name else {
|
|
||||||
return Err(color_eyre::eyre::eyre!("Invalid template name"));
|
|
||||||
};
|
|
||||||
let Value::String(path) = value else {
|
|
||||||
return Err(color_eyre::eyre::eyre!("Invalid template value"));
|
|
||||||
};
|
|
||||||
let mut path = PathBuf::from(path);
|
|
||||||
if let Some(templates_dir) = &config.templates_dir {
|
|
||||||
path = templates_dir.join(path);
|
|
||||||
}
|
|
||||||
tracing::info!(?name, ?path, "Registering template");
|
|
||||||
let content = fs::read_to_string(path).await?;
|
|
||||||
handlebars.register_template_string(name, content)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
handle_direntries(TEMPLATE_DIR.entries(), handlebars).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
9
templates/annotate/comment.jinja
Normal file
9
templates/annotate/comment.jinja
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{% if comment_config %}{{config_delimiter}}
|
||||||
|
{{comment_config}}
|
||||||
|
{{config_delimiter}}{% endif %}
|
||||||
|
|
||||||
|
Table: {{table_name}}
|
||||||
|
|
||||||
|
Columns:
|
||||||
|
|
||||||
|
{{column_info_table}}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
use {{entities_path}}::{{model_path}}::{ActiveModel, Model, Entity};
|
|
||||||
use sea_orm::ActiveModelBehavior;
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl ActiveModelBehavior for ActiveModel {}
|
|
||||||
|
|
||||||
impl Model {}
|
|
||||||
|
|
||||||
impl ActiveModel {}
|
|
||||||
20
templates/model.jinja
Normal file
20
templates/model.jinja
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{% if config.prelude %}
|
||||||
|
use {{prelude_path}}::*;
|
||||||
|
use sea_orm::ActiveModelBehavior;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl ActiveModelBehavior for {{pascalCase(table_name)}}ActiveModel {}
|
||||||
|
|
||||||
|
impl {{pascalCase(table_name)}}Model {}
|
||||||
|
|
||||||
|
impl {{pascalCase(table_name)}}ActiveModel {}
|
||||||
|
{% else %}
|
||||||
|
use {{entities_path}}::{{table_name}}::{ActiveModel, Model, Entity};
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl ActiveModelBehavior for ActiveModel {}
|
||||||
|
|
||||||
|
impl Model {}
|
||||||
|
|
||||||
|
impl ActiveModel {}
|
||||||
|
{% endif %}
|
||||||
9
templates/model_mod.jinja
Normal file
9
templates/model_mod.jinja
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{% if config.prelude %}
|
||||||
|
pub mod prelude;
|
||||||
|
{% endif%}
|
||||||
|
{% if entities_path == "super::_entities" %}
|
||||||
|
pub mod _entities;
|
||||||
|
{% endif %}
|
||||||
|
{% for table in tables %}
|
||||||
|
pub mod {{table.name}};
|
||||||
|
{% endfor %}
|
||||||
7
templates/model_prelude.jinja
Normal file
7
templates/model_prelude.jinja
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{% for table in tables %}
|
||||||
|
pub use {{ entities_path }}::{{ table.name }}::{ActiveModel as {{pascalCase(table.name)}}ActiveModel, Model as {{pascalCase(table.name)}}Model, Entity as {{pascalCase(table.name)}}, Column as {{pascalCase(table.name)}}Column};
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
{% for table in tables %}
|
||||||
|
{# pub use {{entities_path}}::{{table.name}}::{ActiveModel as {% call upperCamelCase(table.name) %}ActiveModel, Model as {% call upperCamelCase(table.name) %}Model, Entity as {% call upperCamelCase(table.name) %}}; #}
|
||||||
|
{% endfor %}
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
use {{prelude_path}}::*;
|
|
||||||
use sea_orm::ActiveModelBehavior;
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl ActiveModelBehavior for {{active_model_name}} {}
|
|
||||||
|
|
||||||
impl {{model_name}} {}
|
|
||||||
|
|
||||||
impl {{active_model_name}} {}
|
|
||||||
3
tests/src/models/_entities/mod.rs
Normal file
3
tests/src/models/_entities/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.10
|
||||||
|
|
||||||
|
pub mod user ;
|
||||||
9
tests/src/models/_entities/user.rs
Normal file
9
tests/src/models/_entities/user.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.10
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
use sea_orm :: entity :: prelude :: * ;
|
||||||
|
|
||||||
|
# [derive (Clone , Debug , PartialEq , DeriveEntityModel , Eq)] # [sea_orm (table_name = "user")] pub struct Model { # [sea_orm (primary_key)] pub id : i32 , # [sea_orm (unique)] pub username : String , # [sea_orm (unique)] pub email : String , pub password : String , # [sea_orm (unique)] pub test : String , }
|
||||||
|
|
||||||
|
# [derive (Copy , Clone , Debug , EnumIter , DeriveRelation)] pub enum Relation { }
|
||||||
9
tests/src/models/mod.rs
Normal file
9
tests/src/models/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
pub mod prelude;
|
||||||
|
|
||||||
|
|
||||||
|
pub mod _entities;
|
||||||
|
|
||||||
|
|
||||||
|
pub mod user;
|
||||||
|
pub mod user;
|
||||||
3
tests/src/models/prelude.rs
Normal file
3
tests/src/models/prelude.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
pub use super::_entities::user::{ActiveModel as UserActiveModel, Model as UserModel, Entity as User};
|
||||||
|
|
||||||
20
tests/src/models/user.rs
Normal file
20
tests/src/models/user.rs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/* == Schema Information
|
||||||
|
* ```
|
||||||
|
* column_db_type = false
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* Table: user
|
||||||
|
*
|
||||||
|
* Columns:
|
||||||
|
*
|
||||||
|
* Name RsType Attrs
|
||||||
|
* +==========+========+======================================+
|
||||||
|
* id i32 autoincrement, not null, primary key
|
||||||
|
* username String not null, unique key
|
||||||
|
* email String not null, unique key
|
||||||
|
* password String not null
|
||||||
|
* test String not null, unique key
|
||||||
|
* +----------+--------+--------------------------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
sdsfsdfsdfsdf
|
||||||
Reference in New Issue
Block a user