diff --git a/akd/.gitignore b/akd/.gitignore new file mode 100644 index 0000000000..e3ad67e439 --- /dev/null +++ b/akd/.gitignore @@ -0,0 +1,8 @@ +/target +config.toml + +/lib + +docker/data + +.structurizr diff --git a/akd/Cargo.lock b/akd/Cargo.lock new file mode 100644 index 0000000000..4d9faaa823 --- /dev/null +++ b/akd/Cargo.lock @@ -0,0 +1,1587 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "async-native-tls" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d57d4cec3c647232e1094dc013546c0b33ce785d8aeb251e1f20dfaf8a9a13fe" +dependencies = [ + "futures-util", + "native-tls", + "thiserror 1.0.69", + "url", +] + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "asynchronous-codec" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057f2c32adbb2fc158e22fb38433c8e9bbf76b75a4732c7c0cbaf695fb65568" +dependencies = [ + "bytes", + "futures-sink", + "futures-util", + "memchr", + "pin-project-lite", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link", +] + +[[package]] +name = "bb8" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d8b8e1a22743d9241575c6ba822cf9c8fef34771c86ab7e477a4fbfd254e5" +dependencies = [ + "futures-util", + "parking_lot", + "tokio", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "num-traits", +] + +[[package]] +name = "connection-string" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "510ca239cf13b7f8d16a2b48f263de7b4f8c566f0af58d901031473c76afb1e3" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.1", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.7+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "hashbrown" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" + +[[package]] +name = "icu_collections" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "libc", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "js-sys" +version = "0.3.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.176" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" + +[[package]] +name = "lock_api" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "macros" +version = "0.1.0" +dependencies = [ + "ms_database", + "proc-macro2", + "quote", + "syn", + "trybuild", +] + +[[package]] +name = "md5" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e6bcd6433cff03a4bfc3d9834d504467db1f1cf6d0ea765d37d330249ed629d" + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" +dependencies = [ + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", +] + +[[package]] +name = "ms_database" +version = "0.1.0" +dependencies = [ + "bb8", + "macros", + "thiserror 2.0.17", + "tiberius", + "tokio", + "tokio-util", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "pretty-hex" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6fa0831dd7cc608c38a5e323422a0077678fa5744aa2be4ad91c4ece8eec8d5" + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +dependencies = [ + "bitflags 2.9.4", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags 2.9.4", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.1", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.1", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.9.4", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_spanned" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" +dependencies = [ + "serde_core", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "target-triple" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.3", + "once_cell", + "rustix", + "windows-sys 0.61.1", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[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]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[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]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tiberius" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1446cb4198848d1562301a3340424b4f425ef79f35ef9ee034769a9dd92c10d" +dependencies = [ + "async-native-tls", + "async-trait", + "asynchronous-codec", + "byteorder", + "bytes", + "chrono", + "connection-string", + "encoding_rs", + "enumflags2", + "futures-util", + "num-traits", + "once_cell", + "pin-project-lite", + "pretty-hex", + "thiserror 1.0.69", + "tokio", + "tracing", + "uuid", + "winauth", +] + +[[package]] +name = "tinystr" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.47.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +dependencies = [ + "backtrace", + "io-uring", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "slab", + "socket2", + "windows-sys 0.59.0", +] + +[[package]] +name = "tokio-util" +version = "0.7.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" +dependencies = [ + "bytes", + "futures-core", + "futures-io", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +dependencies = [ + "once_cell", +] + +[[package]] +name = "trybuild" +version = "1.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ded9fdb81f30a5708920310bfcd9ea7482ff9cba5f54601f7a19a877d5c2392" +dependencies = [ + "glob", + "serde", + "serde_derive", + "serde_json", + "target-triple", + "termcolor", + "toml", +] + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.7+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" +dependencies = [ + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.1", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "winauth" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f820cd208ce9c6b050812dc2d724ba98c6c1e9db5ce9b3f58d925ae5723a5e6" +dependencies = [ + "bitflags 1.3.2", + "byteorder", + "md5", + "rand", + "winapi", +] + +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/akd/Cargo.toml b/akd/Cargo.toml new file mode 100644 index 0000000000..88c598c52b --- /dev/null +++ b/akd/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +members = ["crates/*"] +resolver = "2" + +[workspace.package] +edition = "2021" +version = "0.1.0" +authors = ["Bitwarden Inc"] +license-file = "LICENSE" +keywords = ["akd", "key transparency"] + +[workspace.lints.clippy] +unused_async = "deny" +unwrap_used = "deny" diff --git a/akd/crates/macros/Cargo.toml b/akd/crates/macros/Cargo.toml new file mode 100644 index 0000000000..33ccb10b41 --- /dev/null +++ b/akd/crates/macros/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = { version = "2.0", features = ["full", "extra-traits"] } + +[dev-dependencies] +ms_database = { path = "../ms_database" } +trybuild = "1.0.111" diff --git a/akd/crates/macros/example_migrations/20250930_01_initial/down.sql b/akd/crates/macros/example_migrations/20250930_01_initial/down.sql new file mode 100644 index 0000000000..441087ad7e --- /dev/null +++ b/akd/crates/macros/example_migrations/20250930_01_initial/down.sql @@ -0,0 +1 @@ +DROP TABLE users; \ No newline at end of file diff --git a/akd/crates/macros/example_migrations/20250930_01_initial/up.sql b/akd/crates/macros/example_migrations/20250930_01_initial/up.sql new file mode 100644 index 0000000000..50df13bb6d --- /dev/null +++ b/akd/crates/macros/example_migrations/20250930_01_initial/up.sql @@ -0,0 +1 @@ +CREATE TABLE users (id INT PRIMARY KEY); \ No newline at end of file diff --git a/akd/crates/macros/src/lib.rs b/akd/crates/macros/src/lib.rs new file mode 100644 index 0000000000..f5c2d260d3 --- /dev/null +++ b/akd/crates/macros/src/lib.rs @@ -0,0 +1,356 @@ +//! SQL Migration Macros +//! +//! This crate provides procedural macros for managing SQL migrations at compile time. +//! +//! # Overview +//! +//! The macros in this crate are inspired by Diesel's `embed_migrations!` macro and +//! enable embedding SQL migration files directly into your Rust binary at compile time. +//! +//! It is up to the caller to ensure that a `Migration` struct is in scope and defines an accessible `new` constructor. +//! +//! # Migration Name Requirements +//! +//! Migration directory names must: +//! - Be valid UTF-8 +//! - Not exceed 50 characters (to fit in a VARCHAR(50) database column) +//! +//! # Available Macros +//! +//! - [`migration!`] - Load a single migration from a directory +//! - [`load_migrations!`] - Automatically load all migrations from a directory +//! +//! # Usage Example +//! +//! ```rust +//! use ms_database::Migration; +//! use macros::{migration, load_migrations}; +//! +//! // Load a single migration +//! let single_migration: Migration = migration!("./example_migrations/20250930_01_initial"); +//! +//! // Load all migrations from a directory +//! const ALL_MIGRATIONS: &[Migration] = load_migrations!("./example_migrations"); +//! ``` + +use proc_macro::TokenStream; +use quote::quote; +use std::fs; +use std::path::Path; +use syn::{parse_macro_input, LitStr}; + +/// Resolves a directory path by walking up to find the workspace root (first Cargo.toml). +/// All paths are resolved relative to the workspace root. +fn resolve_path(path_str: &str) -> std::path::PathBuf { + // Try to get the current crate directory from CARGO_MANIFEST_DIR + let start_dir = if let Ok(crate_dir) = std::env::var("CARGO_MANIFEST_DIR") { + std::path::PathBuf::from(crate_dir) + } else { + // CARGO_MANIFEST_DIR not set (e.g., in trybuild tests) + // Start from the current directory and walk up + std::env::current_dir().expect("Could not determine current directory") + }; + + let mut current = start_dir.as_path(); + + // Walk up to find the first Cargo.toml (workspace root) + loop { + if current.join("Cargo.toml").exists() { + return current.join(path_str); + } + + // Move to parent directory + match current.parent() { + Some(parent) => current = parent, + None => { + // Reached filesystem root without finding Cargo.toml + panic!( + "Could not find Cargo.toml in any parent directory starting from {}", + start_dir.display() + ); + } + } + } +} + +/// Helper function to load a migration from a directory path. +/// Returns a tuple of (migration_name, up_content, down_content_tokens). +fn load_migration_from_path(full_path: &Path, relative_path: &str) -> (String, String, proc_macro2::TokenStream) { + // Get the migration name from the directory name + let migration_name = full_path + .file_name() + .expect("Invalid directory path") + .to_str() + .expect("Invalid UTF-8 in directory name") + .to_string(); + + // Validate that the migration name fits in a varchar(50) + if migration_name.len() > 50 { + panic!( + "Migration name '{}' exceeds 50 characters (length: {})", + migration_name, + migration_name.len() + ); + } + + // Read up.sql (required) + let up_sql_path = full_path.join("up.sql"); + if !up_sql_path.exists() { + panic!( + "Required file 'up.sql' not found in migration directory: {}", + full_path.display() + ); + } + let up_content = fs::read_to_string(&up_sql_path) + .unwrap_or_else(|e| panic!("Failed to read up.sql in {}: {}", relative_path, e)); + + // Read down.sql (optional) + let down_sql_path = full_path.join("down.sql"); + let down_content = if down_sql_path.exists() { + let content = fs::read_to_string(&down_sql_path) + .unwrap_or_else(|e| panic!("Failed to read down.sql in {}: {}", relative_path, e)); + quote! { Some(#content) } + } else { + quote! { None } + }; + + (migration_name, up_content, down_content) +} + +/// Loads a single migration from a directory. +/// +/// The directory must contain: +/// - `up.sql` (required) - SQL to apply the migration +/// - `down.sql` (optional) - SQL to roll back the migration +/// +/// # Arguments +/// +/// * `path` - A string literal containing the relative path to the migration directory +/// +/// # Returns +/// +/// A `Migration` struct with: +/// - `name`: The directory name (must fit in VARCHAR(50)) +/// - `up`: The contents of `up.sql` +/// - `down`: `Some(contents)` if `down.sql` exists, `None` otherwise +/// - `run_in_transaction`: Always `true` +/// +/// # Usage +/// +/// ```rust +/// use ms_database::Migration; +/// use macros::migration; +/// +/// // This will load the migration at compile time +/// let migration: Migration = migration!("./example_migrations/20250930_01_initial"); +/// assert_eq!(migration.name, "20250930_01_initial"); +/// assert!(migration.run_in_transaction); +/// ``` +/// +/// # Generated Code +/// +/// This macro generates code that creates a `Migration` struct: +/// +/// ```text +/// // Given a directory structure: +/// // example_migrations/20250930_01_initial/ +/// // ├── up.sql (contains: "CREATE TABLE users (id INT PRIMARY KEY);") +/// // └── down.sql (contains: "DROP TABLE users;") +/// +/// // The macro call: +/// migration!("./example_migrations/20250930_01_initial") +/// +/// // Expands to: +/// Migration { +/// name: #migration_name, +/// up: #up_content, +/// down: #down_content, +/// run_in_transaction: true, +/// } +/// ``` +/// +/// # Panics +/// +/// - If the migration name exceeds 50 characters +/// - If `up.sql` is not found +/// - If any file cannot be read +#[proc_macro] +pub fn migration(input: TokenStream) -> TokenStream { + // Parse the input as a string literal (the directory path) + let dir_path = parse_macro_input!(input as LitStr).value(); + + // Resolve the path (supports both absolute and relative paths) + let full_path = resolve_path(&dir_path); + + // Load the migration using the helper function + let (migration_name, up_content, down_content) = load_migration_from_path(&full_path, &dir_path); + + // Generate the Migration struct + let expanded = quote! { + Migration { + name: #migration_name, + up: #up_content, + down: #down_content, + run_in_transaction: true, + } + }; + + expanded.into() +} + +/// Automatically loads all migrations from a directory. +/// +/// Scans the specified directory for subdirectories containing migration files. +/// Each subdirectory must contain at least an `up.sql` file to be considered +/// a valid migration. Directories are processed in alphabetical order to ensure +/// consistent migration ordering. +/// +/// # Arguments +/// +/// * `path` - A string literal containing the relative path to the migrations directory +/// +/// # Returns +/// +/// A static reference to an array of `Migration` structs: `&[Migration]` +/// +/// # Directory Structure +/// +/// ```text +/// migrations/ +/// ├── 20250930_01_initial/ +/// │ └── up.sql +/// ├── 20250930_02_add_users/ +/// │ ├── up.sql +/// │ └── down.sql +/// └── 20250930_03_add_permissions/ +/// ├── up.sql +/// └── down.sql +/// ``` +/// +/// # Usage +/// +/// ```rust +/// use ms_database::Migration; +/// use macros::load_migrations; +/// +/// // This will load all migrations at compile time +/// const MIGRATIONS: &[Migration] = load_migrations!("./example_migrations"); +/// +/// // Migrations are loaded in alphabetical order +/// for migration in MIGRATIONS { +/// println!("Migration: {}", migration.name); +/// } +/// ``` +/// +/// # Generated Code +/// +/// This macro generates code that creates an array of `Migration` structs: +/// +/// ```text +/// // Given a directory structure: +/// // migrations/ +/// // ├── 20250930_01_initial/ +/// // │ └── up.sql +/// // ├── 20250930_02_add_users/ +/// // │ ├── up.sql +/// // │ └── down.sql +/// // └── 20250930_03_add_permissions/ +/// // └── up.sql +/// +/// // The macro call: +/// const MIGRATIONS: &[Migration] = load_migrations!("./migrations"); +/// +/// // Expands to: +/// &[ +/// Migration { +/// name: "20250930_01_initial", +/// up: "...", +/// down: None, +/// run_in_transaction: true, +/// }, +/// Migration { +/// name: "20250930_02_add_users", +/// up: "...", +/// down: Some("..."), +/// run_in_transaction: true, +/// }, +/// Migration { +/// name: "20250930_03_add_permissions", +/// up: "...", +/// down: None, +/// run_in_transaction: true, +/// }, +/// ] +/// ``` +/// +/// # Panics +/// +/// - If the migrations directory does not exist +/// - If the migrations directory cannot be read +/// - If any migration name exceeds 50 characters +/// - If any required `up.sql` file is missing +#[proc_macro] +pub fn load_migrations(input: TokenStream) -> TokenStream { + // Parse the input as a string literal (the migrations directory path) + let migrations_dir = parse_macro_input!(input as LitStr).value(); + + // Resolve the path (supports both absolute and relative paths) + let migrations_path = resolve_path(&migrations_dir); + + if !migrations_path.exists() || !migrations_path.is_dir() { + panic!( + "Migrations directory not found: {}", + migrations_path.display() + ); + } + + // Read all directories in the migrations directory + let mut migration_paths = Vec::new(); + for entry in fs::read_dir(&migrations_path) + .unwrap_or_else(|e| panic!("Failed to read migrations directory: {}", e)) + { + let entry = entry.unwrap(); + let path = entry.path(); + + if path.is_dir() { + // Check if it has up.sql + if path.join("up.sql").exists() { + migration_paths.push(path); + } + } + } + + // Sort the migrations by name to ensure consistent ordering + migration_paths.sort(); + + // Generate Migration structs inline for each directory + let migrations: Vec<_> = migration_paths + .iter() + .map(|full_path| { + // Use the directory name as the display path for error messages + let display_path = full_path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("unknown"); + + // Load the migration using the helper function + let (migration_name, up_content, down_content) = load_migration_from_path(full_path, display_path); + + quote! { + Migration { + name: #migration_name, + up: #up_content, + down: #down_content, + run_in_transaction: true, + } + } + }) + .collect(); + + // Generate the static array + let expanded = quote! { + &[#(#migrations),*] + }; + + expanded.into() +} diff --git a/akd/crates/macros/tests/compile_panics/load_migrations.rs b/akd/crates/macros/tests/compile_panics/load_migrations.rs new file mode 100644 index 0000000000..2f2564a152 --- /dev/null +++ b/akd/crates/macros/tests/compile_panics/load_migrations.rs @@ -0,0 +1,6 @@ +use macros::load_migrations; +use ms_database::Migration; + +const MIGRATIONS: &[Migration] = load_migrations!("tests/test_panic_migrations"); + +fn main() {} diff --git a/akd/crates/macros/tests/compile_panics/migration.rs b/akd/crates/macros/tests/compile_panics/migration.rs new file mode 100644 index 0000000000..618dabea8f --- /dev/null +++ b/akd/crates/macros/tests/compile_panics/migration.rs @@ -0,0 +1,7 @@ +use macros::migration; +use ms_database::Migration; + +const LONG_MIGRATION: Migration = migration!("tests/test_panic_migrations/20250930_03_really_long_name_that_exceeds_the_50_character_limit"); +const NO_UP_MIGRATION: Migration = migration!("tests/test_panic_migrations/20250930_04_no_up"); + +fn main() {} diff --git a/akd/crates/macros/tests/load_migrations.rs b/akd/crates/macros/tests/load_migrations.rs new file mode 100644 index 0000000000..a21a1b2d18 --- /dev/null +++ b/akd/crates/macros/tests/load_migrations.rs @@ -0,0 +1,35 @@ +use macros::load_migrations; +use ms_database::Migration; + +const TEST_MIGRIATONS: &[Migration] = load_migrations!("tests/test_migrations"); +const EXPECTED_SQL: &str = "SELECT 1;"; + +#[test] +fn migration_ordering() { + let names: Vec<&str> = TEST_MIGRIATONS.iter().map(|m| m.name).collect(); + assert_eq!(names, vec!["20250930_01_test", "20250930_02_up_down",]); +} + +#[test] +fn up_only_migration() { + let migration = &TEST_MIGRIATONS[0]; + assert_eq!(migration.name, "20250930_01_test"); + assert_eq!(migration.up, EXPECTED_SQL); + assert!(migration.down.is_none()); + assert!(migration.run_in_transaction); +} + +#[test] +fn up_down_migration() { + let migration = &TEST_MIGRIATONS[1]; + assert_eq!(migration.name, "20250930_02_up_down"); + assert_eq!(migration.up, EXPECTED_SQL); + assert_eq!(migration.down, Some(EXPECTED_SQL)); + assert!(migration.run_in_transaction); +} + +#[test] +fn long_name_migration() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/compile_panics/load_migrations.rs"); +} diff --git a/akd/crates/macros/tests/migration.rs b/akd/crates/macros/tests/migration.rs new file mode 100644 index 0000000000..e837ab0d41 --- /dev/null +++ b/akd/crates/macros/tests/migration.rs @@ -0,0 +1,28 @@ +use macros::{migration}; +use ms_database::Migration; + +const EXPECTED_SQL: &str = "SELECT 1;"; + +#[test] +fn up_only_migration() { + let migration = migration!("tests/test_migrations/20250930_01_test"); + assert_eq!(migration.name, "20250930_01_test"); + assert_eq!(migration.up, EXPECTED_SQL); + assert!(migration.down.is_none()); + assert!(migration.run_in_transaction); +} + +#[test] +fn up_down_migration() { + let migration = migration!("tests/test_migrations/20250930_02_up_down"); + assert_eq!(migration.name, "20250930_02_up_down"); + assert_eq!(migration.up, EXPECTED_SQL); + assert_eq!(migration.down, Some(EXPECTED_SQL)); + assert!(migration.run_in_transaction); +} + +#[test] +fn long_name_migration() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/compile_panics/migration.rs"); +} diff --git a/akd/crates/macros/tests/test_migrations/20250930_01_test/up.sql b/akd/crates/macros/tests/test_migrations/20250930_01_test/up.sql new file mode 100644 index 0000000000..027b7d63fd --- /dev/null +++ b/akd/crates/macros/tests/test_migrations/20250930_01_test/up.sql @@ -0,0 +1 @@ +SELECT 1; \ No newline at end of file diff --git a/akd/crates/macros/tests/test_migrations/20250930_02_up_down/down.sql b/akd/crates/macros/tests/test_migrations/20250930_02_up_down/down.sql new file mode 100644 index 0000000000..027b7d63fd --- /dev/null +++ b/akd/crates/macros/tests/test_migrations/20250930_02_up_down/down.sql @@ -0,0 +1 @@ +SELECT 1; \ No newline at end of file diff --git a/akd/crates/macros/tests/test_migrations/20250930_02_up_down/up.sql b/akd/crates/macros/tests/test_migrations/20250930_02_up_down/up.sql new file mode 100644 index 0000000000..027b7d63fd --- /dev/null +++ b/akd/crates/macros/tests/test_migrations/20250930_02_up_down/up.sql @@ -0,0 +1 @@ +SELECT 1; \ No newline at end of file diff --git a/akd/crates/macros/tests/test_panic_migrations/20250930_03_really_long_name_that_exceeds_the_50_character_limit/up.sql b/akd/crates/macros/tests/test_panic_migrations/20250930_03_really_long_name_that_exceeds_the_50_character_limit/up.sql new file mode 100644 index 0000000000..027b7d63fd --- /dev/null +++ b/akd/crates/macros/tests/test_panic_migrations/20250930_03_really_long_name_that_exceeds_the_50_character_limit/up.sql @@ -0,0 +1 @@ +SELECT 1; \ No newline at end of file diff --git a/akd/crates/macros/tests/test_panic_migrations/20250930_04_no_up/down.sql b/akd/crates/macros/tests/test_panic_migrations/20250930_04_no_up/down.sql new file mode 100644 index 0000000000..027b7d63fd --- /dev/null +++ b/akd/crates/macros/tests/test_panic_migrations/20250930_04_no_up/down.sql @@ -0,0 +1 @@ +SELECT 1; \ No newline at end of file diff --git a/akd/crates/ms_database/Cargo.toml b/akd/crates/ms_database/Cargo.toml new file mode 100644 index 0000000000..c26a048c20 --- /dev/null +++ b/akd/crates/ms_database/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "ms_database" +edition.workspace = true +version.workspace = true +authors.workspace = true +license-file.workspace = true +keywords.workspace = true + +[dependencies] +bb8 = "0.9.0" +macros = { path = "../macros" } +thiserror = "2.0.17" +tiberius = { version = "0.12.3", features = ["chrono", "tokio"] } +tokio = "1.47.1" +tokio-util = {version = "0.7.16", features = ["compat"] } + +[lints] +workspace = true diff --git a/akd/crates/ms_database/src/lib.rs b/akd/crates/ms_database/src/lib.rs new file mode 100644 index 0000000000..18af284567 --- /dev/null +++ b/akd/crates/ms_database/src/lib.rs @@ -0,0 +1,105 @@ +mod migrate; + +use tokio::net::TcpStream; +use tokio_util::compat::TokioAsyncWriteCompatExt; + +pub use migrate::{Migration, MigrationError, run_pending_migrations}; +use bb8::ManageConnection; +use tiberius::{AuthMethod, Client, Config}; + +pub struct ConnectionManager { + connection_string: String, +} + +impl ConnectionManager { + pub fn new(connection_string: String) -> Self { + Self { connection_string } + } + + pub async fn connect(&self) -> Result { + let mut config = Config::new(); + + config.host("localhost"); + config.port(1433); + config.authentication(AuthMethod::sql_server("SA", "")); + config.trust_cert(); // on production, it is not a good idea to do this + + let tcp = TcpStream::connect(config.get_addr()).await?; + tcp.set_nodelay(true)?; + + // To be able to use Tokio's tcp, we're using the `compat_write` from + // the `TokioAsyncWriteCompatExt` to get a stream compatible with the + // traits from the `futures` crate. + let client = Client::connect(config, tcp.compat_write()).await?; + + Ok(ManagedConnection(client)) + } +} + +type Stream = tokio_util::compat::Compat; +pub struct ManagedConnection(Client); + +// Transparently forward methods to the inner Client +impl ManagedConnection { + async fn execute( + &mut self, + sql: &str, + params: &[&(dyn tiberius::ToSql)], + ) -> Result { + self.0.execute(sql, params).await + } + + async fn query<'a>( + &'a mut self, + sql: &str, + params: &[&(dyn tiberius::ToSql)], + ) -> Result, tiberius::error::Error> { + self.0.query(sql, params).await + } + + async fn simple_query<'a>( + &'a mut self, + sql: &str, + ) -> Result, tiberius::error::Error> { + self.0.simple_query(sql).await + } + + async fn bulk_insert<'a>( + &'a mut self, + table: &'a str, + ) -> Result, tiberius::error::Error> { + self.0.bulk_insert(&table).await + } + + async fn ping(&mut self) -> Result { + let row = self.0.simple_query("SELECT 1").await?.into_first_result().await?; + let value = row[0].get(0).expect("value is present"); + Ok(value) + } +} + +impl ManageConnection for ConnectionManager { + type Connection = ManagedConnection; + + type Error = tiberius::error::Error; + + async fn connect(&self) -> Result { + self.connect().await + } + + async fn is_valid(&self, conn: &mut Self::Connection) -> Result<(), Self::Error> { + match conn.ping().await { + Ok(v) if v == 1 => Ok(()), + Ok(_) => Err(tiberius::error::Error::Protocol( + "Unexpected ping response".into(), + )), + Err(e) => Err(e), + } + } + + fn has_broken(&self, _conn: &mut Self::Connection) -> bool { + // We don't have a good way to determine this sync. r2d2 (which bb8 is based on) recommends + // always returning false here and relying on `is_valid` to catch broken connections. + false + } +} diff --git a/akd/crates/ms_database/src/migrate.rs b/akd/crates/ms_database/src/migrate.rs new file mode 100644 index 0000000000..7598671fb0 --- /dev/null +++ b/akd/crates/ms_database/src/migrate.rs @@ -0,0 +1,130 @@ +use macros::load_migrations; +use tiberius::{error}; + +use crate::{ManagedConnection}; + +type Result = std::result::Result; +const MIGRATIONS: &[Migration] = load_migrations!("./migrations"); + +#[derive(thiserror::Error, Debug)] +pub enum MigrationError { + #[error("Database error: {0}")] + DatabaseError(#[from] error::Error), + // Other error variants can be added here +} + +pub(crate) async fn pending_migrations(conn: &mut ManagedConnection) -> Result> { + // get applied migrations + let applied = read_applied_migrations(conn).await?; + + // create list of migrations that haven't been applied, in order. + let pending = MIGRATIONS + .iter() + .filter(|m| !applied.contains(&m.name.to_string())) + .cloned() + .collect(); + + Ok(pending) +} + +const CREATE_MIGRATIONS_TABLE_SQL: &str = r#" +IF OBJECT_ID('dbo.__migrations') IS NULL +BEGIN + CREATE TABLE dbo.__migrations ( + version VARCHAR(50) PRIMARY KEY, + run_on DATETIME NOT NULL DEFAULT GETDATE() + ); +END +"#; + +async fn ensure_migrations_table_exists(conn: &mut ManagedConnection) -> Result<()> { + // create the migrations table if it doesn't exist + conn.simple_query(CREATE_MIGRATIONS_TABLE_SQL).await?; + + Ok(()) +} + +const READ_APPLIED_MIGRATIONS: &str = "SELECT version FROM dbo.__migrations ORDER BY version"; + +async fn read_applied_migrations(conn: &mut ManagedConnection) -> Result> { + let applied = conn.query(READ_APPLIED_MIGRATIONS, &[]) + .await? + .into_first_result() + .await? + .into_iter() + .map(|row| row.get::<&str,_>("version").expect("version column is present").to_owned()) + .collect(); + Ok(applied) +} + +const RECORD_MIGRATION_SQL: &str = "INSERT INTO dbo.__migrations (version) VALUES (@P1)"; + +async fn record_migration(conn: &mut ManagedConnection, migration: &Migration) -> Result<()> { + conn.execute(RECORD_MIGRATION_SQL, &[&migration.name]).await?; + Ok(()) +} + +pub(crate) async fn run_migration(migration: &Migration, conn: &mut ManagedConnection) -> Result<()> { + if migration.run_in_transaction { + conn.simple_query("BEGIN TRANSACTION").await?; + + let result = async { + conn.simple_query(&migration.up).await?; + record_migration(conn, migration).await?; + Ok::<_, MigrationError>(()) + }.await; + + match result { + Ok(_) => { + conn.simple_query("COMMIT").await?; + Ok(()) + } + Err(e) => { + conn.simple_query("ROLLBACK").await?; + Err(e) + } + } + } else { + conn.simple_query(&migration.up).await?; + record_migration(conn, migration).await?; + Ok(()) + } +} + +pub async fn run_pending_migrations(conn: &mut ManagedConnection) -> Result<()> { + ensure_migrations_table_exists(conn).await?; + let pending = pending_migrations(conn).await?; + for migration in pending { + run_migration(&migration, conn).await?; + } + Ok(()) +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Migration { + /// The name of the migration, derived from the directory name + pub name: &'static str, + /// The SQL to execute when applying the migration + pub up: &'static str, + /// The SQL to execute when rolling back the migration (if provided) + pub down: Option<&'static str>, + /// Whether to run this migration in a transaction + pub run_in_transaction: bool, +} + +impl Migration { + /// Creates a new migration with the given properties. + pub const fn new( + name: &'static str, + up: &'static str, + down: Option<&'static str>, + run_in_transaction: bool, + ) -> Self { + Self { + name, + up, + down, + run_in_transaction, + } + } +} diff --git a/akd/rust-toolchain.toml b/akd/rust-toolchain.toml new file mode 100644 index 0000000000..9102f2e2ab --- /dev/null +++ b/akd/rust-toolchain.toml @@ -0,0 +1,8 @@ +[toolchain] +channel = "1.88.0" +components = [ "rustfmt", "clippy" ] +profile = "minimal" + +# The following is not part of the rust-toolchain.toml format, +# but is used by our Renovate config to manage nightly versions. +nightly-channel = "nightly-2025-05-08" diff --git a/bitwarden-server.sln b/bitwarden-server.sln index 6786ad610c..2523487c7f 100644 --- a/bitwarden-server.sln +++ b/bitwarden-server.sln @@ -46,6 +46,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Events", "src\Events\Events EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Admin", "src\Admin\Admin.csproj", "{B131CEF3-89FB-4C90-ADB0-9E9C4246EB56}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AkdStorage", "src\AkdStorage\AkdStorage.csproj", "{DBA98266-C460-4BFF-87C2-FD5266A283FE}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Notifications", "src\Notifications\Notifications.csproj", "{28635027-20E5-42FA-B218-B6C878DE5350}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Core.Test", "test\Core.Test\Core.Test.csproj", "{8EF31E6C-400A-4174-8BE3-502B08FB10B5}" @@ -186,6 +188,10 @@ Global {B131CEF3-89FB-4C90-ADB0-9E9C4246EB56}.Debug|Any CPU.Build.0 = Debug|Any CPU {B131CEF3-89FB-4C90-ADB0-9E9C4246EB56}.Release|Any CPU.ActiveCfg = Release|Any CPU {B131CEF3-89FB-4C90-ADB0-9E9C4246EB56}.Release|Any CPU.Build.0 = Release|Any CPU + {DBA98266-C460-4BFF-87C2-FD5266A283FE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DBA98266-C460-4BFF-87C2-FD5266A283FE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DBA98266-C460-4BFF-87C2-FD5266A283FE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DBA98266-C460-4BFF-87C2-FD5266A283FE}.Release|Any CPU.Build.0 = Release|Any CPU {28635027-20E5-42FA-B218-B6C878DE5350}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {28635027-20E5-42FA-B218-B6C878DE5350}.Debug|Any CPU.Build.0 = Debug|Any CPU {28635027-20E5-42FA-B218-B6C878DE5350}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -369,6 +375,7 @@ Global {9CF59342-3912-4B45-A2BA-0F173666586D} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D} {994DD611-F266-4BD3-8072-3B1B57267ED5} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D} {B131CEF3-89FB-4C90-ADB0-9E9C4246EB56} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D} + {DBA98266-C460-4BFF-87C2-FD5266A283FE} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D} {28635027-20E5-42FA-B218-B6C878DE5350} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D} {8EF31E6C-400A-4174-8BE3-502B08FB10B5} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84F} {79BB453F-D0D8-4DDF-9809-A405C56692BD} = {DD5BD056-4AAE-43EF-BBD2-0B569B8DA84D}