Use Julids for DbId.

First phase just does a typedef of "DbId = Julid", and confirms the plugin will load into
sqlite. All tests pass. Next phase removes client-side generation of IDs.
This commit is contained in:
Joe Ardent 2023-07-26 17:10:52 -07:00
parent 8a23272cfe
commit 7aefedf993
10 changed files with 277 additions and 259 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/target /target
/libjulid.so

289
Cargo.lock generated
View file

@ -234,6 +234,17 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]] [[package]]
name = "auto-future" name = "auto-future"
version = "1.0.0" version = "1.0.0"
@ -439,6 +450,29 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "bindgen"
version = "0.60.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "062dddbc1ba4aca46de6338e2bf87771414c335f7b2f2036e8f3e9befebf88e6"
dependencies = [
"bitflags 1.3.2",
"cexpr",
"clang-sys",
"clap 3.2.25",
"env_logger",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"which",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -520,6 +554,15 @@ version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom",
]
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "0.1.10" version = "0.1.10"
@ -545,6 +588,32 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "clang-sys"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "3.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123"
dependencies = [
"atty",
"bitflags 1.3.2",
"clap_lex 0.2.4",
"indexmap 1.9.3",
"strsim",
"termcolor",
"textwrap",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.3.19" version = "4.3.19"
@ -564,7 +633,7 @@ checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1"
dependencies = [ dependencies = [
"anstream", "anstream",
"anstyle", "anstyle",
"clap_lex", "clap_lex 0.5.0",
"strsim", "strsim",
"unicase", "unicase",
"unicode-width", "unicode-width",
@ -582,6 +651,15 @@ dependencies = [
"syn 2.0.27", "syn 2.0.27",
] ]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.5.0" version = "0.5.0"
@ -754,6 +832,19 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "env_logger"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]] [[package]]
name = "equivalent" name = "equivalent"
version = "1.0.1" version = "1.0.1"
@ -958,6 +1049,18 @@ version = "0.27.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e"
[[package]]
name = "glob"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]] [[package]]
name = "hashbrown" name = "hashbrown"
version = "0.14.0" version = "0.14.0"
@ -974,7 +1077,7 @@ version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f" checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f"
dependencies = [ dependencies = [
"hashbrown", "hashbrown 0.14.0",
] ]
[[package]] [[package]]
@ -1011,6 +1114,15 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.2" version = "0.3.2"
@ -1109,6 +1221,12 @@ dependencies = [
"libm", "libm",
] ]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "0.14.27" version = "0.14.27"
@ -1165,6 +1283,16 @@ dependencies = [
"unicode-normalization", "unicode-normalization",
] ]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown 0.12.3",
]
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.0.0" version = "2.0.0"
@ -1172,7 +1300,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown", "hashbrown 0.14.0",
] ]
[[package]] [[package]]
@ -1181,7 +1309,7 @@ version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi 0.3.2",
"rustix", "rustix",
"windows-sys", "windows-sys",
] ]
@ -1210,6 +1338,16 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "julid-rs"
version = "0.1.6"
dependencies = [
"rand",
"serde",
"sqlite-loadable",
"sqlx",
]
[[package]] [[package]]
name = "justerror" name = "justerror"
version = "1.1.0" version = "1.1.0"
@ -1230,12 +1368,28 @@ dependencies = [
"spin 0.5.2", "spin 0.5.2",
] ]
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]] [[package]]
name = "libc" name = "libc"
version = "0.2.147" version = "0.2.147"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if 1.0.0",
"winapi",
]
[[package]] [[package]]
name = "libm" name = "libm"
version = "0.2.7" version = "0.2.7"
@ -1421,7 +1575,7 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
dependencies = [ dependencies = [
"hermit-abi", "hermit-abi 0.3.2",
"libc", "libc",
] ]
@ -1454,6 +1608,12 @@ dependencies = [
"syn 2.0.27", "syn 2.0.27",
] ]
[[package]]
name = "os_str_bytes"
version = "6.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d5d9eb14b174ee9aa2ef96dc2b94637a2d4b6e7cb873c7e171f0c20c6cf3eac"
[[package]] [[package]]
name = "overload" name = "overload"
version = "0.1.1" version = "0.1.1"
@ -1500,6 +1660,12 @@ version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]] [[package]]
name = "pem-rfc7468" name = "pem-rfc7468"
version = "0.7.0" version = "0.7.0"
@ -1743,6 +1909,12 @@ version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.4" version = "0.38.4"
@ -1825,18 +1997,18 @@ dependencies = [
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.175" version = "1.0.176"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc"
dependencies = [ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]] [[package]]
name = "serde_derive" name = "serde_derive"
version = "1.0.175" version = "1.0.176"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1929,6 +2101,12 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shlex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.1" version = "1.4.1"
@ -2009,6 +2187,40 @@ dependencies = [
"unicode_categories", "unicode_categories",
] ]
[[package]]
name = "sqlite-loadable"
version = "0.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a916b7bb8738eef189dea88731b619b80bf3f62b3acf05138fa43fbf8621cc94"
dependencies = [
"bitflags 1.3.2",
"serde",
"serde_json",
"sqlite-loadable-macros",
"sqlite3ext-sys",
]
[[package]]
name = "sqlite-loadable-macros"
version = "0.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f98bc75a8d6fd24f6a2cfea34f28758780fa17279d3051eec926efa381971e48"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "sqlite3ext-sys"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3afdc2b3dc08f16d6eecf8aa07d19975a268603ab1cca67d3f9b4172c507cf16"
dependencies = [
"bindgen",
"cc",
]
[[package]] [[package]]
name = "sqlx" name = "sqlx"
version = "0.7.1" version = "0.7.1"
@ -2032,7 +2244,6 @@ dependencies = [
"atoi", "atoi",
"byteorder", "byteorder",
"bytes", "bytes",
"chrono",
"crc", "crc",
"crossbeam-queue", "crossbeam-queue",
"dotenvy", "dotenvy",
@ -2045,7 +2256,7 @@ dependencies = [
"futures-util", "futures-util",
"hashlink", "hashlink",
"hex", "hex",
"indexmap", "indexmap 2.0.0",
"log", "log",
"memchr", "memchr",
"once_cell", "once_cell",
@ -2059,7 +2270,6 @@ dependencies = [
"smallvec", "smallvec",
"sqlformat", "sqlformat",
"thiserror", "thiserror",
"time",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tracing", "tracing",
@ -2098,7 +2308,6 @@ dependencies = [
"sha2 0.10.7", "sha2 0.10.7",
"sqlx-core", "sqlx-core",
"sqlx-mysql", "sqlx-mysql",
"sqlx-postgres",
"sqlx-sqlite", "sqlx-sqlite",
"syn 1.0.109", "syn 1.0.109",
"tempfile", "tempfile",
@ -2117,7 +2326,6 @@ dependencies = [
"bitflags 2.3.3", "bitflags 2.3.3",
"byteorder", "byteorder",
"bytes", "bytes",
"chrono",
"crc", "crc",
"digest 0.10.7", "digest 0.10.7",
"dotenvy", "dotenvy",
@ -2145,7 +2353,6 @@ dependencies = [
"sqlx-core", "sqlx-core",
"stringprep", "stringprep",
"thiserror", "thiserror",
"time",
"tracing", "tracing",
"whoami", "whoami",
] ]
@ -2160,7 +2367,6 @@ dependencies = [
"base64 0.21.2", "base64 0.21.2",
"bitflags 2.3.3", "bitflags 2.3.3",
"byteorder", "byteorder",
"chrono",
"crc", "crc",
"dotenvy", "dotenvy",
"etcetera", "etcetera",
@ -2186,7 +2392,6 @@ dependencies = [
"sqlx-core", "sqlx-core",
"stringprep", "stringprep",
"thiserror", "thiserror",
"time",
"tracing", "tracing",
"whoami", "whoami",
] ]
@ -2198,7 +2403,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4c21bf34c7cae5b283efb3ac1bcc7670df7561124dc2f8bdc0b59be40f79a2" checksum = "be4c21bf34c7cae5b283efb3ac1bcc7670df7561124dc2f8bdc0b59be40f79a2"
dependencies = [ dependencies = [
"atoi", "atoi",
"chrono",
"flume", "flume",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
@ -2210,7 +2414,6 @@ dependencies = [
"percent-encoding", "percent-encoding",
"serde", "serde",
"sqlx-core", "sqlx-core",
"time",
"tracing", "tracing",
"url", "url",
] ]
@ -2278,6 +2481,21 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "termcolor"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]] [[package]]
name = "thiserror" name = "thiserror"
version = "1.0.44" version = "1.0.44"
@ -2528,15 +2746,6 @@ version = "1.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
[[package]]
name = "ulid"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13a3aaa69b04e5b66cc27309710a569ea23593612387d67daaf102e73aa974fd"
dependencies = [
"rand",
]
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.6.0" version = "2.6.0"
@ -2727,7 +2936,8 @@ dependencies = [
"axum-macros", "axum-macros",
"axum-test", "axum-test",
"chrono", "chrono",
"clap", "clap 4.3.19",
"julid-rs",
"justerror", "justerror",
"optional_optional_user", "optional_optional_user",
"password-hash", "password-hash",
@ -2744,10 +2954,20 @@ dependencies = [
"tower-http", "tower-http",
"tracing", "tracing",
"tracing-subscriber", "tracing-subscriber",
"ulid",
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "which"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
dependencies = [
"either",
"libc",
"once_cell",
]
[[package]] [[package]]
name = "whoami" name = "whoami"
version = "1.4.1" version = "1.4.1"
@ -2770,6 +2990,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "winapi-x86_64-pc-windows-gnu" name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0" version = "0.4.0"

View file

@ -18,11 +18,13 @@ axum-login = { git = "https://github.com/nebkor/axum-login", branch = "sqlx-0.7"
axum-macros = "0.3" axum-macros = "0.3"
chrono = { version = "0.4", default-features = false, features = ["std", "clock"] } chrono = { version = "0.4", default-features = false, features = ["std", "clock"] }
clap = { version = "4", features = ["derive", "env", "unicode", "suggestions", "usage"] } clap = { version = "4", features = ["derive", "env", "unicode", "suggestions", "usage"] }
julid-rs = { path = "../julid" }
justerror = "1" justerror = "1"
password-hash = { version = "0.5", features = ["std", "getrandom"] } password-hash = { version = "0.5", features = ["std", "getrandom"] }
rand = "0.8" rand = "0.8"
rand_distr = "0.4"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
sqlx = { version = "0.7", default-features = false, features = ["runtime-tokio-rustls", "sqlite", "chrono", "time"] } sqlx = { version = "0.7", default-features = false, features = ["runtime-tokio-rustls", "sqlite"] }
thiserror = "1" thiserror = "1"
tokio = { version = "1", features = ["full", "tracing"], default-features = false } tokio = { version = "1", features = ["full", "tracing"], default-features = false }
tokio-retry = "0.3" tokio-retry = "0.3"
@ -31,9 +33,7 @@ tower = { version = "0.4", features = ["util", "timeout"], default-features = fa
tower-http = { version = "0.4", features = ["add-extension", "trace"] } tower-http = { version = "0.4", features = ["add-extension", "trace"] }
tracing = "0.1" tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] }
ulid = { version = "1", features = ["rand"] }
unicode-segmentation = "1" unicode-segmentation = "1"
rand_distr = "0.4"
[dev-dependencies] [dev-dependencies]
axum-test = "9" axum-test = "9"

View file

@ -5,7 +5,7 @@
-- users -- users
create table if not exists users ( create table if not exists users (
id blob not null primary key, id blob not null primary key default (julid_new()),
username text not null unique, username text not null unique,
displayname text, displayname text,
email text, email text,
@ -16,7 +16,7 @@ create table if not exists users (
-- table of things to watch -- table of things to watch
create table if not exists watches ( create table if not exists watches (
id blob not null primary key, id blob not null primary key default (julid_new()),
kind int not null, -- enum for movie or tv show or whatev kind int not null, -- enum for movie or tv show or whatev
title text not null, title text not null,
metadata_url text, -- possible url for imdb or other metadata-esque site to show the user metadata_url text, -- possible url for imdb or other metadata-esque site to show the user
@ -52,7 +52,7 @@ create table if not exists follows (
); );
create table if not exists watch_notes ( create table if not exists watch_notes (
id blob not null primary key, -- a user can have multiple notes about the same thing id blob not null primary key default (julid_new()), -- a user can have multiple notes about the same thing
user blob not null, user blob not null,
watch blob not null, watch blob not null,
note blob, note blob,

View file

@ -12,7 +12,7 @@ use sqlx::{
SqlitePool, SqlitePool,
}; };
use crate::{db_id::DbId, User}; use crate::{DbId, User};
const MAX_CONNS: u32 = 200; const MAX_CONNS: u32 = 200;
const MIN_CONNS: u32 = 5; const MIN_CONNS: u32 = 5;
@ -49,6 +49,9 @@ pub fn get_db_pool() -> SqlitePool {
.journal_mode(SqliteJournalMode::Wal) .journal_mode(SqliteJournalMode::Wal)
.synchronous(sqlx::sqlite::SqliteSynchronous::Normal) .synchronous(sqlx::sqlite::SqliteSynchronous::Normal)
.filename(&db_filename) .filename(&db_filename)
// need to build this out of band and put it in the project root; see instructions at
// https://gitlab.com/nebkor/julid
.extension("./libjulid")
.busy_timeout(Duration::from_secs(TIMEOUT)) .busy_timeout(Duration::from_secs(TIMEOUT))
.create_if_missing(true); .create_if_missing(true);

View file

@ -1,214 +0,0 @@
use std::{
borrow::Cow,
fmt::{Debug, Display},
};
use chrono::Utc;
use serde::{de::Visitor, Deserialize, Serialize};
use sqlx::{
encode::IsNull,
sqlite::{SqliteArgumentValue, SqliteValueRef},
Decode, Encode, Sqlite,
};
use ulid::Ulid;
#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct DbId(pub Ulid);
impl DbId {
pub fn bytes(&self) -> [u8; 16] {
self.to_be_bytes()
}
pub fn to_be_bytes(self) -> [u8; 16] {
self.0 .0.to_be_bytes()
}
pub fn is_nil(&self) -> bool {
self.0.is_nil()
}
pub fn new() -> Self {
Self(Ulid::new())
}
pub fn from_string(s: &str) -> Result<Self, ulid::DecodeError> {
let id = Ulid::from_string(s)?;
Ok(id.into())
}
pub fn as_string(&self) -> String {
self.0.to_string()
}
pub fn created_at(&self) -> chrono::DateTime<Utc> {
self.0.datetime().into()
}
}
//-************************************************************************
// standard trait impls
//-************************************************************************
impl Display for DbId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0.to_string())
}
}
impl Debug for DbId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("DbId").field(&self.as_string()).finish()
}
}
impl From<Ulid> for DbId {
fn from(value: Ulid) -> Self {
DbId(value)
}
}
impl From<u128> for DbId {
fn from(value: u128) -> Self {
DbId(value.into())
}
}
//-************************************************************************
// sqlx traits for going in and out of the db
//-************************************************************************
impl sqlx::Type<sqlx::Sqlite> for DbId {
fn type_info() -> <sqlx::Sqlite as sqlx::Database>::TypeInfo {
<&[u8] as sqlx::Type<sqlx::Sqlite>>::type_info()
}
}
impl<'q> Encode<'q, Sqlite> for DbId {
fn encode_by_ref(&self, args: &mut Vec<SqliteArgumentValue<'q>>) -> IsNull {
args.push(SqliteArgumentValue::Blob(Cow::Owned(self.bytes().to_vec())));
IsNull::No
}
}
impl Decode<'_, Sqlite> for DbId {
fn decode(value: SqliteValueRef<'_>) -> Result<Self, sqlx::error::BoxDynError> {
let bytes = <&[u8] as Decode<Sqlite>>::decode(value)?;
let bytes: [u8; 16] = bytes.try_into().unwrap_or_default();
Ok(u128::from_be_bytes(bytes).into())
}
}
//-************************************************************************
// serde traits
//-************************************************************************
impl Serialize for DbId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_bytes(&self.bytes())
}
}
struct DbIdVisitor;
impl<'de> Visitor<'de> for DbIdVisitor {
type Value = DbId;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("16 bytes")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
match std::convert::TryInto::<[u8; 16]>::try_into(v) {
Ok(v) => Ok(u128::from_be_bytes(v).into()),
Err(_) => Err(serde::de::Error::invalid_length(v.len(), &self)),
}
}
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
DbId::from_string(&v)
.map_err(|_| serde::de::Error::invalid_value(serde::de::Unexpected::Str(&v), &self))
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
DbId::from_string(v)
.map_err(|_| serde::de::Error::invalid_value(serde::de::Unexpected::Str(v), &self))
}
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let len = v.len();
match std::convert::TryInto::<[u8; 16]>::try_into(v) {
Ok(v) => Ok(u128::from_be_bytes(v).into()),
Err(_) => Err(serde::de::Error::invalid_length(len, &self)),
}
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut raw_bytes_from_db = [0u8; 16];
let size = seq.size_hint().unwrap_or(0);
let mut count = 0;
while let Some(val) = seq.next_element()? {
if count > 15 {
break;
}
raw_bytes_from_db[count] = val;
count += 1;
}
if count != 16 || size > 16 {
let sz = if count < 16 { count } else { size };
Err(serde::de::Error::invalid_length(sz, &self))
} else {
Ok(u128::from_be_bytes(raw_bytes_from_db).into())
}
}
}
impl<'de> Deserialize<'de> for DbId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
deserializer.deserialize_bytes(DbIdVisitor)
}
}
//-************************************************************************
// serialization tests
//-************************************************************************
#[cfg(test)]
mod test {
use serde_test::{assert_tokens, Token};
use super::*;
#[test]
fn test_ser_de() {
let bytes: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
let id: DbId = u128::from_be_bytes(bytes).into();
assert_tokens(
&id,
&[Token::Bytes(&[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
])],
);
}
}

View file

@ -1,6 +1,6 @@
use sqlx::{query_scalar, SqlitePool}; use sqlx::{query_scalar, SqlitePool};
use crate::{db_id::DbId, util::year_to_epoch, ShowKind, User, Watch, WatchQuest}; use crate::{util::year_to_epoch, DbId, ShowKind, User, Watch, WatchQuest};
const USER_EXISTS_QUERY: &str = "select count(*) from users where id = $1"; const USER_EXISTS_QUERY: &str = "select count(*) from users where id = $1";

View file

@ -9,7 +9,6 @@ pub mod test_utils;
/// Some public interfaces for interacting with the database outside of the web /// Some public interfaces for interacting with the database outside of the web
/// app /// app
pub use db::get_db_pool; pub use db::get_db_pool;
pub use db_id::DbId;
pub mod import_utils; pub mod import_utils;
pub use users::User; pub use users::User;
pub use watches::{ShowKind, Watch, WatchQuest}; pub use watches::{ShowKind, Watch, WatchQuest};
@ -18,7 +17,6 @@ pub type WWRouter = axum::Router<SqlitePool>;
// everything else is private to the crate // everything else is private to the crate
mod db; mod db;
mod db_id;
mod generic_handlers; mod generic_handlers;
mod login; mod login;
mod signup; mod signup;
@ -32,6 +30,7 @@ use optional_optional_user::OptionalOptionalUser;
use templates::*; use templates::*;
use watches::templates::*; use watches::templates::*;
pub type DbId = julid::Julid;
type AuthContext = axum_login::extractors::AuthContext<DbId, User, axum_login::SqliteStore<User>>; type AuthContext = axum_login::extractors::AuthContext<DbId, User, axum_login::SqliteStore<User>>;
/// Returns the router to be used as a service or test object, you do you. /// Returns the router to be used as a service or test object, you do you.

View file

@ -110,7 +110,7 @@ pub async fn post_create_user(
let id = DbId::new(); let id = DbId::new();
let user = create_user(username, &displayname, &email, password, &pool, id).await?; let user = create_user(username, &displayname, &email, password, &pool, id).await?;
let now = user.id.created_at(); let now = user.id.timestamp();
tracing::debug!("created {user:?} at {now:?}"); tracing::debug!("created {user:?} at {now:?}");
let id = user.id.as_string(); let id = user.id.as_string();
let location = format!("/signup_success/{id}"); let location = format!("/signup_success/{id}");
@ -137,7 +137,7 @@ pub async fn get_signup_success(
let mut resp = SignupSuccessPage(user.clone()).into_response(); let mut resp = SignupSuccessPage(user.clone()).into_response();
if user.username.is_empty() || id.is_nil() { if user.username.is_empty() || id.is_alpha() {
// redirect to front page if we got here without a valid user ID // redirect to front page if we got here without a valid user ID
*resp.status_mut() = StatusCode::SEE_OTHER; *resp.status_mut() = StatusCode::SEE_OTHER;
resp.headers_mut().insert("Location", "/".parse().unwrap()); resp.headers_mut().insert("Location", "/".parse().unwrap());
@ -267,7 +267,8 @@ mod test {
let server = server_with_pool(&pool).await; let server = server_with_pool(&pool).await;
let user = get_test_user(); let user = get_test_user();
let id = user.id.0.to_string(); let id = user.id.to_string();
dbg!(&id);
let path = format!("/signup_success/{id}"); let path = format!("/signup_success/{id}");

View file

@ -8,9 +8,8 @@ use sqlx::{query, query_as, SqlitePool};
use super::templates::{AddNewWatchPage, GetWatchPage, SearchWatchesPage}; use super::templates::{AddNewWatchPage, GetWatchPage, SearchWatchesPage};
use crate::{ use crate::{
db_id::DbId,
util::{empty_string_as_none, year_to_epoch}, util::{empty_string_as_none, year_to_epoch},
AuthContext, MyWatchesPage, ShowKind, Watch, WatchQuest, AuthContext, DbId, MyWatchesPage, ShowKind, Watch, WatchQuest,
}; };
//-************************************************************************ //-************************************************************************