diff --git a/Cargo.lock b/Cargo.lock index 5a4615f..ab14b6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,9 +104,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.102" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arc-swap" @@ -141,9 +141,9 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-config" -version = "1.8.18" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33f815b73a3899c03b380d543532e5865f230dce9678d108dc10732a8682275" +checksum = "47712fde1909402600ccfbb26e47d482d2e58bb9e9e603d9f17e67cc435a6319" dependencies = [ "aws-credential-types", "aws-runtime", @@ -161,7 +161,7 @@ dependencies = [ "bytes", "fastrand", "hex", - "http 1.4.1", + "http 1.4.2", "sha1", "time", "tokio", @@ -172,9 +172,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.14" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f20799b373a1be121fe3005fba0c2090af9411573878f224df44b42727fcaf7" +checksum = "e93964ffdaf57857f544be3666a5f57570bb699e934700f11b49708f61bb556e" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -184,15 +184,16 @@ dependencies = [ [[package]] name = "aws-lc-fips-sys" -version = "0.13.14" +version = "0.13.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d619165468401dec3caa3366ebffbcb83f2f31883e5b3932f8e2dec2ddc568" +checksum = "6c0e6249c249b8916c98ebae7bc06216c8dcab3002f32872b4abe642d17063b1" dependencies = [ "bindgen", "cc", "cmake", "dunce", "fs_extra", + "pkg-config", "regex", ] @@ -223,9 +224,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.7.5" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c9b9de216a988dd54b754a82a7660cfe14cee4f6782ae4524470972fa0ccb39" +checksum = "7816e98ee912159f45d307e5ee6bfea4a335a55aee15f7f3e32f81a6f3000f1d" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -238,7 +239,7 @@ dependencies = [ "bytes", "bytes-utils", "fastrand", - "http 1.4.1", + "http 1.4.2", "http-body 1.0.1", "percent-encoding", "pin-project-lite", @@ -248,9 +249,9 @@ dependencies = [ [[package]] name = "aws-sdk-acm" -version = "1.107.0" +version = "1.108.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "894c814dcf2877eec15e874a9877898504847afacd990478fd9276f845f7bdcb" +checksum = "142f21db8d4143af128e54dcb26b4f7504dacfe5874646ff8856d3323fd62698" dependencies = [ "arc-swap", "aws-credential-types", @@ -261,21 +262,22 @@ dependencies = [ "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", "fastrand", "http 0.2.12", - "http 1.4.1", + "http 1.4.2", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-secretsmanager" -version = "1.108.0" +version = "1.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd24cfd47bda71881c399a7cc4850d5a61727246163d5cd1a7c0b48ef19983cb" +checksum = "661ce910a6ed895c6bde66aa78040272628b127714fe0e9bb799f07ab4db407b" dependencies = [ "arc-swap", "aws-credential-types", @@ -286,21 +288,22 @@ dependencies = [ "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", "fastrand", "http 0.2.12", - "http 1.4.1", + "http 1.4.2", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sso" -version = "1.101.0" +version = "1.103.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b647baea49ff551960b904f905681e9b4765a6c4ea08631e89dc52d8bd3f5896" +checksum = "0469f435f645ad2162cfb463b15bde37115966ee3acf2d87fb4871ee309b8401" dependencies = [ "arc-swap", "aws-credential-types", @@ -311,21 +314,22 @@ dependencies = [ "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", "fastrand", "http 0.2.12", - "http 1.4.1", + "http 1.4.2", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-ssooidc" -version = "1.103.0" +version = "1.105.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ae401c65ff288aa7873117fe535cd32b7b1bb0bc43751d28901a1d5f20636b9" +checksum = "085faefb253f770655e162b9304321e62a1e71adf7f019ee1f4454228a377b3a" dependencies = [ "arc-swap", "aws-credential-types", @@ -336,21 +340,22 @@ dependencies = [ "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", "fastrand", "http 0.2.12", - "http 1.4.1", + "http 1.4.2", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sts" -version = "1.106.0" +version = "1.108.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c80de7bb7d03e9ca8c9fd7b489f20f3948d3f3be91a7953591347d238115408" +checksum = "3c72b08911d8128dd360fe1b22a9fec0fa8b552dde8ec828dcf20ef5ec974e9f" dependencies = [ "arc-swap", "aws-credential-types", @@ -362,21 +367,22 @@ dependencies = [ "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-smithy-xml", "aws-types", "fastrand", "http 0.2.12", - "http 1.4.1", + "http 1.4.2", "regex-lite", "tracing", ] [[package]] name = "aws-sigv4" -version = "1.4.5" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae38512beae0ffee7010fc24e7a8a123c53efdfef42a61e80fda4882418dc71" +checksum = "723c2234ad7511ceef63eab016b7ba6ff7c55590fefb96fa8467af014a07309f" dependencies = [ "aws-credential-types", "aws-smithy-http", @@ -388,7 +394,7 @@ dependencies = [ "hex", "hmac 0.13.0", "http 0.2.12", - "http 1.4.1", + "http 1.4.2", "p256", "percent-encoding", "sha2 0.11.0", @@ -400,9 +406,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.14" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffcaf626bdda484571968400c326a244598634dc75fd451325a54ad1a59acfc" +checksum = "f02e407fb3b54891734224b9ffac8a71fdd35f542500fa1af95754a6b2beb316" dependencies = [ "futures-util", "pin-project-lite", @@ -411,9 +417,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.63.6" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ab2dc1c2c3749ead27180d333c42f11be8b0e934058fb4b2258ee8dbe5231" +checksum = "37843d9add67c3aff5856f409c6dc315d3cdff60f9c0cb5b670dab1e9920306d" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -421,7 +427,7 @@ dependencies = [ "bytes-utils", "futures-core", "futures-util", - "http 1.4.1", + "http 1.4.2", "http-body 1.0.1", "http-body-util", "percent-encoding", @@ -432,9 +438,9 @@ dependencies = [ [[package]] name = "aws-smithy-http-client" -version = "1.1.12" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a2f165a7feee6f263028b899d0a181987f4fa7179a6411a32a439fba7c5f769" +checksum = "635d23afda0a6ab48d666c4d447c4873e8d1e83518a2be2093122397e50b838e" dependencies = [ "aws-smithy-async", "aws-smithy-protocol-test", @@ -442,9 +448,9 @@ dependencies = [ "aws-smithy-types", "bytes", "h2 0.3.27", - "h2 0.4.14", + "h2 0.4.15", "http 0.2.12", - "http 1.4.1", + "http 1.4.2", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -469,9 +475,9 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.62.7" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "701a947f4797e52a911e114a898667c746c39feea467bbd1abd7b3721f702ffa" +checksum = "3dc65a121adb4b33729919fcfa14fa36fb33c1555a8f06bb0e2188dbfdc1d9ef" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-schema", @@ -487,7 +493,7 @@ dependencies = [ "aws-smithy-http-client", "aws-smithy-runtime-api", "aws-smithy-types", - "http 1.4.1", + "http 1.4.2", ] [[package]] @@ -499,23 +505,23 @@ dependencies = [ "aws-smithy-http-client", "aws-smithy-runtime-api", "aws-smithy-types", - "http 1.4.1", + "http 1.4.2", ] [[package]] name = "aws-smithy-observability" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06c2315d173edbf1920da8ba3a7189695827002e4c0fc961973ab1c54abca9c" +checksum = "8e86338c869539a581bf161247762a6e87f92c5c075060057b5ed6d06632ed0c" dependencies = [ "aws-smithy-runtime-api", ] [[package]] name = "aws-smithy-protocol-test" -version = "0.63.14" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b227aa94af99a8e5ee52551cc7e3ee30a217019ef99207b6f0b7a1527685941" +checksum = "f76511a0e223ce78deb6a78b8afebda99cb737cfbc8a58d96dcb190f012dd40a" dependencies = [ "assert-json-diff", "aws-smithy-runtime-api", @@ -532,9 +538,9 @@ dependencies = [ [[package]] name = "aws-smithy-query" -version = "0.60.15" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a56d79744fb3edb5d722ef79d86081e121d3b9422cb209eb03aea6aa4f21ebd" +checksum = "dd22a6ba36e3f113cb8d5b3d1fe0ed31c76ee608ef63322d753bb8d2c9479e77" dependencies = [ "aws-smithy-types", "urlencoding", @@ -542,9 +548,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e6f5caf6fea86f8c2206541ab5857cfcda9013426cdbe8fa0098b9e2d32182" +checksum = "bea94a9ff8464016338c851e24b472d7131c388c88898a502e781815b2ee6045" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -556,7 +562,7 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.4.1", + "http 1.4.2", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -569,16 +575,16 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.12.3" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9db177daa6ba8afb9ee1aefcf548c907abcf52065e394ee11a92780057fe0e8c" +checksum = "22ed1ebe6e0a95ea84570225f5a8208dec4b8f77e61a9b0d6f51773fcb4612f0" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api-macros", "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.4.1", + "http 1.4.2", "pin-project-lite", "tokio", "tracing", @@ -587,9 +593,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api-macros" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d7396fd9500589e62e460e987ecb671bad374934e55ec3b5f498cc7a8a8a7b7" +checksum = "221eaa237ddf1ca79b60d1372aad77e47f9c0ea5b3ce5099da8c61d027dc77b3" dependencies = [ "proc-macro2", "quote", @@ -598,27 +604,27 @@ dependencies = [ [[package]] name = "aws-smithy-schema" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7442cb268338f0eb8278140a107c046756aa01093d8ef5e99628d34ae09c94f5" +checksum = "7d56e0a4e53127a632224e43633b0fe045fa9e1e3cfc68b9830f1115e103f910" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", - "http 1.4.1", + "http 1.4.2", ] [[package]] name = "aws-smithy-types" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b42fcf341259d85ca10fac9a2f6448a8ec691c6955a18e45bc3b71a85fab85" +checksum = "d6dc683efb34b9e755675b37fedbe0103141e5b6df7bdc9eb6967756a8c167d8" dependencies = [ "base64-simd", "bytes", "bytes-utils", "futures-core", "http 0.2.12", - "http 1.4.1", + "http 1.4.2", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -635,18 +641,21 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.60.15" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce02add1aa3677d022f8adf81dcbe3046a95f17a1b1e8979c145cd21d3d22b3" +checksum = "ea3f68eec3607f02acd24067969ce2abc6ba16aa7d5ce59ca450ed2fb5f78957" dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", "xmlparser", ] [[package]] name = "aws-types" -version = "1.3.16" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16bf10b03a3c01e6b3b7d47cd964e873ffe9e7d4e80fad16bd4c077cb068531" +checksum = "e957a6c6dbce82b7a91f44231c09273159703769f447cbe85e854dfe9cf67f86" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -826,9 +835,9 @@ dependencies = [ [[package]] name = "bitflags" -version = "2.11.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" [[package]] name = "block-buffer" @@ -841,9 +850,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" dependencies = [ "hybrid-array", ] @@ -874,9 +883,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "bytes-utils" @@ -924,9 +933,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.63" +version = "1.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" dependencies = [ "find-msvc-tools", "jobserver", @@ -957,9 +966,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" dependencies = [ "cfg-if", "cpufeatures 0.3.0", @@ -1202,9 +1211,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -1212,18 +1221,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crunchy" @@ -1363,7 +1372,6 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ - "powerfmt", "serde_core", ] @@ -1450,7 +1458,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ - "block-buffer 0.12.0", + "block-buffer 0.12.1", "const-oid 0.10.2", "crypto-common 0.2.2", "ctutils", @@ -1582,12 +1590,6 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1673,25 +1675,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", - "js-sys", "libc", "r-efi 5.3.0", "wasip2", - "wasm-bindgen", ] [[package]] name = "getrandom" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", "rand_core 0.10.1", - "wasip2", - "wasip3", + "wasm-bindgen", ] [[package]] @@ -1732,16 +1732,16 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.4.1", + "http 1.4.2", "indexmap 2.14.0", "slab", "tokio", @@ -1766,15 +1766,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash", -] - [[package]] name = "hashbrown" version = "0.17.1" @@ -1824,9 +1815,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" dependencies = [ "bytes", "itoa", @@ -1850,7 +1841,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.1", + "http 1.4.2", ] [[package]] @@ -1861,7 +1852,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.1", + "http 1.4.2", "http-body 1.0.1", "pin-project-lite", ] @@ -1880,15 +1871,15 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424" +checksum = "15cdd26707701c53297e2fa6afb323d55fbc1d0810c3aec078ae3ef0424c3c15" [[package]] name = "hybrid-array" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ "typenum", ] @@ -1927,8 +1918,8 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2 0.4.14", - "http 1.4.1", + "h2 0.4.15", + "http 1.4.2", "http-body 1.0.1", "httparse", "httpdate", @@ -1960,7 +1951,7 @@ version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http 1.4.1", + "http 1.4.2", "hyper 1.10.1", "hyper-util", "rustls 0.23.41", @@ -1980,7 +1971,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.4.1", + "http 1.4.2", "http-body 1.0.1", "hyper 1.10.1", "ipnet", @@ -2101,12 +2092,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - [[package]] name = "ident_case" version = "1.0.1" @@ -2265,23 +2250,22 @@ dependencies = [ [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] [[package]] name = "js-sys" -version = "0.3.99" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" dependencies = [ "cfg-if", "futures-util", - "once_cell", "wasm-bindgen", ] @@ -2291,12 +2275,6 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - [[package]] name = "libc" version = "0.2.186" @@ -2402,9 +2380,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.1" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "mime" @@ -2441,9 +2419,9 @@ dependencies = [ [[package]] name = "mock_instant" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce6dd36094cac388f119d2e9dc82dc730ef91c32a6222170d630e5414b956e6" +checksum = "9bb517913cfcfb9eeda59f36020269075a152701a01606c612f547e4890be399" [[package]] name = "nom" @@ -2466,9 +2444,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.6" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", @@ -2756,9 +2734,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.9" +version = "0.11.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", "cfg_aliases", @@ -2776,15 +2754,16 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.14" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.4", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash", "rustls 0.23.41", @@ -2798,23 +2777,23 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ "cfg_aliases", "libc", "once_cell", "socket2 0.6.4", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] name = "quote" -version = "1.0.45" +version = "1.0.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" dependencies = [ "proc-macro2", ] @@ -2848,7 +2827,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" dependencies = [ "chacha20", - "getrandom 0.4.2", + "getrandom 0.4.3", "rand_core 0.10.1", ] @@ -2886,6 +2865,15 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + [[package]] name = "rayon" version = "1.12.0" @@ -2937,9 +2925,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.4" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2" dependencies = [ "aho-corasick", "memchr", @@ -2949,9 +2937,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db" dependencies = [ "aho-corasick", "memchr", @@ -2980,8 +2968,8 @@ dependencies = [ "bytes", "encoding_rs", "futures-core", - "h2 0.4.14", - "http 1.4.1", + "h2 0.4.15", + "http 1.4.2", "http-body 1.0.1", "http-body-util", "hyper 1.10.1", @@ -3043,9 +3031,9 @@ dependencies = [ [[package]] name = "rustc-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" [[package]] name = "rustc_version" @@ -3098,9 +3086,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +checksum = "dab5152771c58876a2146916e53e35057e1a4dfa2b9df0f0305b07f611fdea4d" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -3110,9 +3098,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" dependencies = [ "web-time", "zeroize", @@ -3169,9 +3157,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "ryu" @@ -3425,9 +3413,9 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", @@ -3527,9 +3515,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.1" +version = "1.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "socket2" @@ -3581,9 +3569,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.117" +version = "2.0.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" dependencies = [ "proc-macro2", "quote", @@ -3638,7 +3626,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.2", + "getrandom 0.4.3", "once_cell", "rustix", "windows-sys 0.61.2", @@ -3685,12 +3673,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.47" +version = "0.3.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" dependencies = [ "deranged", - "itoa", "num-conv", "powerfmt", "serde_core", @@ -3700,15 +3687,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.27" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" dependencies = [ "num-conv", "time-core", @@ -3935,7 +3922,7 @@ dependencies = [ "bitflags", "bytes", "futures-util", - "http 1.4.1", + "http 1.4.2", "http-body 1.0.1", "pin-project-lite", "tower", @@ -4059,9 +4046,9 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" [[package]] name = "unicode-xid" @@ -4128,9 +4115,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.2" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d258b83ceec21034727ecee8c382cfa6c3e133699b0742c64571814fb420c9f7" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ "js-sys", "wasm-bindgen", @@ -4181,27 +4168,18 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.3+wasi-0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" -dependencies = [ - "wit-bindgen 0.57.1", -] - -[[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +version = "1.0.4+wasi-0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" dependencies = [ "cfg-if", "once_cell", @@ -4212,9 +4190,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.72" +version = "0.4.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" dependencies = [ "js-sys", "wasm-bindgen", @@ -4222,9 +4200,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4232,9 +4210,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" dependencies = [ "bumpalo", "proc-macro2", @@ -4245,52 +4223,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.122" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" dependencies = [ "unicode-ident", ] -[[package]] -name = "wasm-encoder" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" -dependencies = [ - "leb128fmt", - "wasmparser", -] - -[[package]] -name = "wasm-metadata" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" -dependencies = [ - "anyhow", - "indexmap 2.14.0", - "wasm-encoder", - "wasmparser", -] - -[[package]] -name = "wasmparser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" -dependencies = [ - "bitflags", - "hashbrown 0.15.5", - "indexmap 2.14.0", - "semver", -] - [[package]] name = "web-sys" -version = "0.3.99" +version = "0.3.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" dependencies = [ "js-sys", "wasm-bindgen", @@ -4308,9 +4252,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" dependencies = [ "rustls-pki-types", ] @@ -4481,16 +4425,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", -] - -[[package]] -name = "windows-sys" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" -dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -4508,31 +4443,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" -dependencies = [ - "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "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]] @@ -4550,96 +4468,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" -[[package]] -name = "windows_aarch64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" - [[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_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" - [[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_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" - [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" -[[package]] -name = "windows_i686_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" - [[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_gnu" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" - [[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_gnullvm" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "windows_x86_64_msvc" -version = "0.53.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" - [[package]] name = "winnow" version = "0.7.15" @@ -4655,100 +4525,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" -[[package]] -name = "wit-bindgen" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - [[package]] name = "wit-bindgen" version = "0.57.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck", - "indexmap 2.14.0", - "prettyplease", - "syn", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags", - "indexmap 2.14.0", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap 2.14.0", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] - [[package]] name = "writeable" version = "0.6.3" @@ -4769,9 +4551,9 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "yoke" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -4792,18 +4574,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.50" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.50" +version = "0.8.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" dependencies = [ "proc-macro2", "quote", diff --git a/aws_secretsmanager_caching/src/lib.rs b/aws_secretsmanager_caching/src/lib.rs index 2e47dc2..3f4ec14 100644 --- a/aws_secretsmanager_caching/src/lib.rs +++ b/aws_secretsmanager_caching/src/lib.rs @@ -529,34 +529,48 @@ impl SecretsManagerCachingClient { #[cfg(test)] mod tests { + use aws_sdk_secretsmanager::{ + config::http::HttpResponse, + error::ProvideErrorMetadata, + operation::{ + describe_secret::DescribeSecretOutput, + get_secret_value::{GetSecretValueError, GetSecretValueOutput}, + }, + types::error::ResourceNotFoundException, + }; + use aws_smithy_mocks::{mock, mock_client, RuleMode}; + use aws_smithy_types::body::SdkBody; use tokio::time::sleep; use super::*; - use aws_smithy_runtime_api::client::http::SharedHttpClient; - - fn fake_client( - ttl: Option, - ignore_transient_errors: bool, - http_client: Option, - endpoint_url: Option, - ) -> SecretsManagerCachingClient { - SecretsManagerCachingClient::new( - asm_mock::def_fake_client(http_client, endpoint_url), - NonZeroUsize::new(1000).unwrap(), - match ttl { - Some(ttl) => ttl, - None => Duration::from_secs(1000), - }, - ignore_transient_errors, - ) - .expect("client should create") - } + use aws_smithy_runtime_api::{client::result::SdkError, http::StatusCode}; #[tokio::test] async fn test_get_secret_value() { - let client = fake_client(None, false, None, None); let secret_id = "test_secret"; + let arn = "arn"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(|req| req.secret_id() == Some(secret_id)) + .then_output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("hunter2") + .version_stages("AWSCURRENT") + .build() + }); + + let asm_mock = mock_client!(aws_sdk_secretsmanager, [&gsv]); + + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(1000), + true, + ) + .unwrap(); let response = client .get_secret_value(secret_id, None, None, false) @@ -565,53 +579,87 @@ mod tests { assert_eq!(response.name, Some(secret_id.to_string())); assert_eq!(response.secret_string, Some("hunter2".to_string())); - assert_eq!( - response.arn, - Some( - asm_mock::FAKE_ARN - .replace("{{name}}", secret_id) - .to_string() - ) - ); + assert_eq!(response.arn, Some(arn.into())); assert_eq!( response.version_stages, Some(vec!["AWSCURRENT".to_string()]) ); + assert_eq!(gsv.num_calls(), 1) } #[tokio::test] async fn test_get_secret_value_version_id() { - let client = fake_client(None, false, None, None); let secret_id = "test_secret"; let version_id = "test_version"; + let arn = "arn"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(|req| { + req.secret_id() == Some(secret_id) && req.version_id() == Some(version_id) + }) + .then_output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("hunter2") + .version_id(version_id) + .version_stages("AWSCURRENT") + .build() + }); + + let asm_mock = mock_client!(aws_sdk_secretsmanager, [&gsv]); + + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(1000), + true, + ) + .unwrap(); let response = client .get_secret_value(secret_id, Some(version_id), None, false) .await .unwrap(); - assert_eq!(response.name, Some(secret_id.to_string())); assert_eq!(response.secret_string, Some("hunter2".to_string())); assert_eq!(response.version_id, Some(version_id.to_string())); - assert_eq!( - response.arn, - Some( - asm_mock::FAKE_ARN - .replace("{{name}}", secret_id) - .to_string() - ) - ); + assert_eq!(response.arn, Some(arn.into())); assert_eq!( response.version_stages, Some(vec!["AWSCURRENT".to_string()]) ); + assert_eq!(gsv.num_calls(), 1) } #[tokio::test] async fn test_get_secret_value_version_stage() { - let client = fake_client(None, false, None, None); let secret_id = "test_secret"; let stage_label = "STAGEHERE"; + let arn = "arn"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(|req| { + req.secret_id() == Some(secret_id) && req.version_stage() == Some(stage_label) + }) + .then_output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("hunter2") + .version_stages(stage_label) + .build() + }); + + let asm_mock = mock_client!(aws_sdk_secretsmanager, [&gsv]); + + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(1000), + true, + ) + .unwrap(); let response = client .get_secret_value(secret_id, None, Some(stage_label), false) @@ -620,47 +668,97 @@ mod tests { assert_eq!(response.name, Some(secret_id.to_string())); assert_eq!(response.secret_string, Some("hunter2".to_string())); - assert_eq!( - response.arn, - Some( - asm_mock::FAKE_ARN - .replace("{{name}}", secret_id) - .to_string() - ) - ); + assert_eq!(response.arn, Some(arn.into())); assert_eq!(response.version_stages, Some(vec![stage_label.to_string()])); + assert_eq!(gsv.num_calls(), 1) } #[tokio::test] async fn test_get_secret_value_version_id_and_stage() { - let client = fake_client(None, false, None, None); let secret_id = "test_secret"; let version_id = "test_version"; let stage_label = "STAGEHERE"; + let arn = "arn"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(|req| { + req.secret_id() == Some(secret_id) + && req.version_stage() == Some(stage_label) + && req.version_id() == Some(version_id) + }) + .then_output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("hunter2") + .version_stages(stage_label) + .version_id(version_id) + .build() + }); + + let asm_mock = mock_client!(aws_sdk_secretsmanager, [&gsv]); + + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(1000), + true, + ) + .unwrap(); let response = client .get_secret_value(secret_id, Some(version_id), Some(stage_label), false) .await .unwrap(); - assert_eq!(response.name, Some(secret_id.to_string())); assert_eq!(response.secret_string, Some("hunter2".to_string())); assert_eq!(response.version_id, Some(version_id.to_string())); - assert_eq!( - response.arn, - Some( - asm_mock::FAKE_ARN - .replace("{{name}}", secret_id) - .to_string() - ) - ); + assert_eq!(response.arn, Some(arn.into())); assert_eq!(response.version_stages, Some(vec![stage_label.to_string()])); + assert_eq!(gsv.num_calls(), 1) } #[tokio::test] async fn test_get_cache_expired() { - let client = fake_client(Some(Duration::from_secs(0)), false, None, None); let secret_id = "test_secret"; + let version_id = "version_id"; + let version_stage = "AWSCURRENT"; + let arn = "arn"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(|req| req.secret_id() == Some(secret_id)) + .then_output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("hunter2") + .version_id(version_id) + .version_stages(version_stage) + .build() + }); + + let describe_secret = + mock!(aws_sdk_secretsmanager::Client::describe_secret).then_output(move || { + // Don't serve the same value + DescribeSecretOutput::builder() + .name(secret_id) + .version_ids_to_stages("different_version_id", vec![version_stage.into()]) + .build() + }); + + let asm_mock = mock_client!( + aws_sdk_secretsmanager, + RuleMode::MatchAny, + [&gsv, &describe_secret] + ); + + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(0), + true, + ) + .unwrap(); // Run through this twice to test the cache expiration for i in 0..2 { @@ -671,14 +769,7 @@ mod tests { assert_eq!(response.name, Some(secret_id.to_string())); assert_eq!(response.secret_string, Some("hunter2".to_string())); - assert_eq!( - response.arn, - Some( - asm_mock::FAKE_ARN - .replace("{{name}}", secret_id) - .to_string() - ) - ); + assert_eq!(response.arn, Some(arn.into())); assert_eq!( response.version_stages, Some(vec!["AWSCURRENT".to_string()]) @@ -688,229 +779,345 @@ mod tests { sleep(Duration::from_millis(50)).await; } } + + assert_eq!(gsv.num_calls(), 2) } #[tokio::test] - #[should_panic] async fn test_get_secret_value_kms_access_denied() { - let client = fake_client(None, false, None, None); + let gsv = + mock!(aws_sdk_secretsmanager::Client::get_secret_value).then_http_response(|| { + HttpResponse::new( + StatusCode::try_from(400).unwrap(), + SdkBody::from( + r##"{ + "__type":"AccessDeniedException", + "message":"Access to KMS is not allowed" + }"##, + ), + ) + }); + + let asm_mock = mock_client!(aws_sdk_secretsmanager, &[gsv]); + + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(1000), + true, + ) + .unwrap(); let secret_id = "KMSACCESSDENIEDabcdef"; - client - .get_secret_value(secret_id, None, None, false) - .await - .unwrap(); + let error = match client.get_secret_value(secret_id, None, None, false).await { + Ok(_) => panic!(), + Err(e) => e + .downcast::>() + .expect("should be an SdkError") + .into_service_error(), + }; + + assert_eq!(error.code(), Some("AccessDeniedException")); + assert_eq!(error.message(), Some("Access to KMS is not allowed")); } #[tokio::test] - #[should_panic] async fn test_get_secret_value_resource_not_found() { - let client = fake_client(None, false, None, None); + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value).then_error(|| { + GetSecretValueError::ResourceNotFoundException( + ResourceNotFoundException::builder() + .message("Secrets Manager can't find the specified secret.") + .build(), + ) + }); + + let asm_mock = mock_client!(aws_sdk_secretsmanager, &[gsv]); + + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(1000), + true, + ) + .unwrap(); + let secret_id = "NOTFOUNDfasefasef"; - client - .get_secret_value(secret_id, None, None, false) - .await - .unwrap(); + match client.get_secret_value(secret_id, None, None, false).await { + Ok(_) => panic!(), + Err(e) => assert!(e + .downcast::>() + .unwrap() + .into_service_error() + .is_resource_not_found_exception()), + }; } - #[tokio::test] - async fn test_is_current_default_succeeds() { - let client = fake_client(Some(Duration::from_secs(0)), false, None, None); + /// Helper for is_current tests: builds a mock client with GSV + DescribeSecret + /// that returns matching version data, then asserts two fetches (with a TTL=0 + /// expiry sleep in between) result in only 1 GSV call and 1 Describe call. + async fn assert_is_current_fast_refreshes( + query_version_id: Option<&'static str>, + query_version_stage: Option<&'static str>, + ) { let secret_id = "test_secret"; + let version_id = "version_id"; + let version_stage = query_version_stage.unwrap_or("AWSCURRENT"); + let arn = "arn"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(move |req| { + req.secret_id() == Some(secret_id) + && req.version_id() == query_version_id + && req.version_stage() == query_version_stage + }) + .then_output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("hunter2") + .version_id(version_id) + .version_stages(version_stage) + .build() + }); + + let describe_secret = + mock!(aws_sdk_secretsmanager::Client::describe_secret).then_output(move || { + // Cache is current. We fast-refresh + DescribeSecretOutput::builder() + .name(secret_id) + .version_ids_to_stages(version_id, vec![version_stage.into()]) + .build() + }); + + let asm_mock = mock_client!( + aws_sdk_secretsmanager, + RuleMode::MatchAny, + [&gsv, &describe_secret] + ); - let res1 = client - .get_secret_value(secret_id, None, None, false) - .await - .unwrap(); + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(0), + true, + ) + .unwrap(); - let res2 = client - .get_secret_value(secret_id, None, None, false) - .await - .unwrap(); + // Run through this twice to test the cache expiration + for i in 0..2 { + let response = client + .get_secret_value(secret_id, query_version_id, query_version_stage, false) + .await + .unwrap(); - assert_eq!(res1, res2) + assert_eq!(response.name, Some(secret_id.to_string())); + assert_eq!(response.secret_string, Some("hunter2".to_string())); + assert_eq!(response.arn, Some(arn.into())); + assert_eq!(response.version_stages, Some(vec![version_stage.into()])); + if i == 0 { + sleep(Duration::from_millis(50)).await; + } + } + + assert_eq!(gsv.num_calls(), 1); + assert_eq!(describe_secret.num_calls(), 1); } #[tokio::test] - async fn test_is_current_version_id_succeeds() { - let client = fake_client(Some(Duration::from_secs(0)), false, None, None); - let secret_id = "test_secret"; - let version_id = Some("test_version"); - - let res1 = client - .get_secret_value(secret_id, version_id, None, false) - .await - .unwrap(); - - let res2 = client - .get_secret_value(secret_id, version_id, None, false) - .await - .unwrap(); + async fn test_get_cache_is_current_fast_refreshes() { + assert_is_current_fast_refreshes(None, None).await; + } - assert_eq!(res1, res2) + #[tokio::test] + async fn test_is_current_version_id_succeeds() { + assert_is_current_fast_refreshes(Some("version_id"), None).await; } #[tokio::test] async fn test_is_current_version_stage_succeeds() { - let client = fake_client(Some(Duration::from_secs(0)), false, None, None); - let secret_id = "test_secret"; - let version_stage = Some("VERSIONSTAGE"); - - let res1 = client - .get_secret_value(secret_id, None, version_stage, false) - .await - .unwrap(); - - let res2 = client - .get_secret_value(secret_id, None, version_stage, false) - .await - .unwrap(); + assert_is_current_fast_refreshes(None, Some("VERSIONSTAGE")).await; + } - assert_eq!(res1, res2) + #[tokio::test] + async fn test_is_current_both_version_id_and_version_stage_succeed() { + assert_is_current_fast_refreshes(Some("version_id"), Some("VERSIONSTAGE")).await; } #[tokio::test] - async fn test_is_current_both_version_id_and_version_stage_succeeds() { - let client = fake_client(Some(Duration::from_secs(0)), false, None, None); + async fn test_is_current_describe_access_denied_fails() { let secret_id = "test_secret"; - let version_id = Some("test_version"); - let version_stage = Some("VERSIONSTAGE"); + let version_id = "version_id"; + let version_stage = "VERSIONSTAGE"; + let arn = "arn"; + let secret_string = "hunter2"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(|req| { + req.secret_id() == Some(secret_id) + && req.version_stage() == Some(version_stage) + && req.version_id() == Some(version_id) + }) + .then_output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string(secret_string) + .version_id(version_id) + .version_stages(version_stage) + .build() + }); + + let describe_secret = + mock!(aws_sdk_secretsmanager::Client::describe_secret).then_http_response(|| { + HttpResponse::new( + StatusCode::try_from(400).unwrap(), + SdkBody::from( + r##"{ + "__type":"AccessDeniedException", + "message":"is not authorized to perform: secretsmanager:DescribeSecret on resource: XXXXXXXX" + }"##, + ), + ) + }); - let res1 = client - .get_secret_value(secret_id, version_id, version_stage, false) - .await - .unwrap(); + let asm_mock = mock_client!( + aws_sdk_secretsmanager, + RuleMode::MatchAny, + [&gsv, &describe_secret] + ); - let res2 = client - .get_secret_value(secret_id, version_id, version_stage, false) + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(0), + true, + ) + .unwrap(); + + // Run through this twice to test the cache expiration + let response = client + .get_secret_value(secret_id, Some(version_id), Some(version_stage), false) .await .unwrap(); - assert_eq!(res1, res2) - } - - #[tokio::test] - async fn test_is_current_describe_access_denied_fails() { - let client = fake_client(Some(Duration::from_secs(0)), false, None, None); - let secret_id = "DESCRIBEACCESSDENIED_test_secret"; - let version_id = Some("test_version"); + assert_eq!(response.name, Some(secret_id.to_string())); + assert_eq!(response.secret_string, Some(secret_string.to_string())); + assert_eq!(response.arn, Some(arn.into())); + assert_eq!( + response.version_stages, + Some(vec![version_stage.to_string()]) + ); + // let the entry expire + sleep(Duration::from_millis(50)).await; - client - .get_secret_value(secret_id, version_id, None, false) + if client + .get_secret_value(secret_id, Some(version_id), Some(version_stage), false) .await - .unwrap(); - - if (client - .get_secret_value(secret_id, version_id, None, false) - .await) .is_ok() { panic!("Expected failure") } - } - - #[tokio::test] - async fn test_is_current_describe_timeout_error_succeeds() { - use asm_mock::GSV_BODY; - use aws_smithy_runtime::client::http::test_util::wire::{ReplayedEvent, WireMockServer}; - - let mock = WireMockServer::start(vec![ - ReplayedEvent::with_body(GSV_BODY), - ReplayedEvent::Timeout, - ]) - .await; - let client = fake_client( - Some(Duration::from_secs(0)), - true, - Some(mock.http_client()), - Some(mock.endpoint_url()), - ); - let secret_id = "DESCRIBETIMEOUT_test_secret"; - let version_id = Some("test_version"); - let res1 = client - .get_secret_value(secret_id, version_id, None, false) - .await - .unwrap(); - - let res2 = client - .get_secret_value(secret_id, version_id, None, false) - .await - .unwrap(); - - mock.shutdown(); - - assert_eq!(res1, res2) + assert_eq!(gsv.num_calls(), 1) } #[tokio::test] async fn test_is_current_describe_service_error_succeeds() { - let client = fake_client(Some(Duration::from_secs(0)), true, None, None); let secret_id = "DESCRIBESERVICEERROR_test_secret"; - let version_id = Some("test_version"); - let version_stage = Some("VERSIONSTAGE"); - - let res1 = client - .get_secret_value(secret_id, version_id, version_stage, false) - .await - .unwrap(); - - let res2 = client - .get_secret_value(secret_id, version_id, version_stage, false) - .await - .unwrap(); - - assert_eq!(res1, res2) - } + let version_id = "test_version"; + let version_stage = "VERSIONSTAGE"; + let arn = "arn"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(|req| { + req.secret_id() == Some(secret_id) + && req.version_stage() == Some(version_stage) + && req.version_id() == Some(version_id) + }) + .then_output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("hunter2") + .version_id(version_id) + .version_stages(version_stage) + .build() + }); + + let describe_secret = mock!(aws_sdk_secretsmanager::Client::describe_secret) + .then_http_response(|| { + HttpResponse::new( + StatusCode::try_from(500).unwrap(), + SdkBody::from( + r##"{ + "__type": "InternalServiceError", + "message": "Internal service error" + }"##, + ), + ) + }); - #[tokio::test] - async fn test_is_current_gsv_timeout_error_succeeds() { - use asm_mock::DESC_BODY; - use asm_mock::GSV_BODY; - use aws_smithy_runtime::client::http::test_util::wire::{ReplayedEvent, WireMockServer}; - - let mock = WireMockServer::start(vec![ - ReplayedEvent::with_body( - GSV_BODY - .replace("{{version}}", "old_version") - .replace("{{label}}", "AWSCURRENT"), - ), - ReplayedEvent::with_body( - DESC_BODY - .replace("{{version}}", "new_version") - .replace("{{label}}", "AWSCURRENT"), - ), - ReplayedEvent::Timeout, - ]) - .await; - let client = fake_client( - Some(Duration::from_secs(0)), - true, - Some(mock.http_client()), - Some(mock.endpoint_url()), + let asm_mock = mock_client!( + aws_sdk_secretsmanager, + RuleMode::MatchAny, + [&gsv, &describe_secret] ); - let secret_id = "GSVTIMEOUT_test_secret"; + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::ZERO, + true, + ) + .unwrap(); let res1 = client - .get_secret_value(secret_id, None, None, false) + .get_secret_value(secret_id, Some(version_id), Some(version_stage), false) .await .unwrap(); let res2 = client - .get_secret_value(secret_id, None, None, false) + .get_secret_value(secret_id, Some(version_id), Some(version_stage), false) .await .unwrap(); - mock.shutdown(); - assert_eq!(res1, res2) } #[tokio::test] async fn test_get_secret_value_refresh_now_true() { - let client = fake_client(Some(Duration::from_secs(30)), false, None, None); let secret_id = "REFRESHNOW_test_secret"; + let arn = "arn"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(|req| req.secret_id() == Some(secret_id)) + .sequence() + .output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("hunter2") + .version_stages("AWSCURRENT") + .build() + }) + .output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("some other string") + .version_stages("AWSCURRENT") + .build() + }) + .build(); + + let asm_mock = mock_client!(aws_sdk_secretsmanager, [&gsv]); + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(30), + true, + ) + .unwrap(); let response1 = client .get_secret_value(secret_id, None, None, false) @@ -918,35 +1125,57 @@ mod tests { .unwrap(); assert_eq!(response1.name, Some(secret_id.to_string())); - assert_eq!( - response1.arn, - Some( - asm_mock::FAKE_ARN - .replace("{{name}}", secret_id) - .to_string() - ) - ); + assert_eq!(response1.arn, Some(arn.into())); assert_eq!( response1.version_stages, Some(vec!["AWSCURRENT".to_string()]) ); - sleep(Duration::from_millis(1)).await; - let response2 = client .get_secret_value(secret_id, None, None, true) .await .unwrap(); - assert_ne!(response1.secret_string, response2.secret_string); assert_eq!(response1.arn, response2.arn); assert_eq!(response1.version_stages, response2.version_stages); + + assert_eq!(gsv.num_calls(), 2) } #[tokio::test] async fn test_get_secret_value_refresh_now_false() { - let client = fake_client(Some(Duration::from_secs(30)), false, None, None); let secret_id = "REFRESHNOW_test_secret"; + let arn = "arn"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(|req| req.secret_id() == Some(secret_id)) + .sequence() + .output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("hunter2") + .version_stages("AWSCURRENT") + .build() + }) + .output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("some other string") + .version_stages("AWSCURRENT") + .build() + }) + .build(); + + let asm_mock = mock_client!(aws_sdk_secretsmanager, [&gsv]); + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(30), + true, + ) + .unwrap(); let response1 = client .get_secret_value(secret_id, None, None, false) @@ -954,157 +1183,88 @@ mod tests { .unwrap(); assert_eq!(response1.name, Some(secret_id.to_string())); - assert_eq!( - response1.arn, - Some( - asm_mock::FAKE_ARN - .replace("{{name}}", secret_id) - .to_string() - ) - ); + assert_eq!(response1.arn, Some(arn.into())); assert_eq!( response1.version_stages, Some(vec!["AWSCURRENT".to_string()]) ); - sleep(Duration::from_millis(1)).await; - let response2 = client .get_secret_value(secret_id, None, None, false) .await .unwrap(); assert_eq!(response1, response2); + + assert_eq!(gsv.num_calls(), 1) } #[tokio::test] async fn test_get_secret_value_version_id_and_stage_refresh_now() { - let client = fake_client(Some(Duration::from_secs(30)), false, None, None); let secret_id = "REFRESHNOW_test_secret"; let version_id = "test_version"; let stage_label = "STAGEHERE"; + let arn = "arn"; + + let gsv = mock!(aws_sdk_secretsmanager::Client::get_secret_value) + .match_requests(|req| req.secret_id() == Some(secret_id)) + .sequence() + .output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("hunter2") + .version_stages("AWSCURRENT") + .build() + }) + .output(move || { + GetSecretValueOutput::builder() + .name(secret_id) + .arn(arn) + .secret_string("some other string") + .version_stages("AWSCURRENT") + .build() + }) + .build(); + + let asm_mock = mock_client!(aws_sdk_secretsmanager, [&gsv]); + let client = SecretsManagerCachingClient::new( + asm_mock, + NonZeroUsize::new(1000).unwrap(), + Duration::from_secs(30), + true, + ) + .unwrap(); let response1 = client .get_secret_value(secret_id, Some(version_id), Some(stage_label), false) .await .unwrap(); - sleep(Duration::from_millis(1)).await; - let response2 = client .get_secret_value(secret_id, Some(version_id), Some(stage_label), true) .await .unwrap(); - assert_ne!(response1.secret_string, response2.secret_string); assert_eq!(response1.arn, response2.arn); assert_eq!(response1.version_stages, response2.version_stages); - } - - #[tokio::test] - async fn test_batch_get_secret_value_success() { - let client = fake_client(None, false, None, None); - - // Mock returns one secret per batch call (keyed on first ID) - let result = client - .batch_get_secret_value(Some(vec!["MyTest".to_string()]), None, None, None) - .await; - - assert!(result.is_ok()); - let resp = result.unwrap().unwrap(); - assert_eq!(resp.secret_values().len(), 1); - assert!(resp.errors().is_empty()); - - // Verify the secret was written to the cache (cache hit, no network call) - let cached = client.get_secret_value("MyTest", None, None, false).await; - assert!(cached.is_ok()); - assert_eq!(cached.unwrap().secret_string, Some("hunter2".to_string())); - } - - #[tokio::test] - async fn test_batch_get_secret_value_partial_failure() { - let client = fake_client(None, false, None, None); - - let result = client - .batch_get_secret_value( - Some(vec![ - "NOTFOUNDsecret".to_string(), - "ValidSecret".to_string(), - ]), - None, - None, - None, - ) - .await; - - assert!(result.is_ok()); - let resp = result.unwrap().unwrap(); - // One secret succeeded, one errored - assert_eq!(resp.secret_values().len(), 1); - assert_eq!(resp.errors().len(), 1); - - // Valid secret should be cached - let cached = client - .get_secret_value("ValidSecret", None, None, false) - .await; - assert!(cached.is_ok()); - } - - #[tokio::test] - async fn test_batch_get_secret_value_api_error() { - let client = fake_client(None, false, None, None); - - let result = client - .batch_get_secret_value( - Some(vec!["BATCHAPIERROR_secret".to_string()]), - None, - None, - None, - ) - .await; - - assert!(result.is_err()); - } - - #[tokio::test] - async fn test_batch_get_secret_value_transient_suppressed() { - use aws_smithy_runtime::client::http::test_util::wire::{ReplayedEvent, WireMockServer}; - - let mock = WireMockServer::start(vec![ReplayedEvent::Timeout]).await; - let client = fake_client( - None, - true, // ignore_transient_errors = true - Some(mock.http_client()), - Some(mock.endpoint_url()), - ); - - let result = client - .batch_get_secret_value(Some(vec!["MyTest".to_string()]), None, None, None) - .await; - - mock.shutdown(); - // Transient error should be suppressed → Ok(None) - assert!(result.is_ok()); - assert!(result.unwrap().is_none()); + assert_eq!(gsv.num_calls(), 2); } - mod asm_mock { + mod wire_tests { use aws_sdk_secretsmanager as secretsmanager; - use aws_smithy_runtime::client::http::test_util::infallible_client_fn; + + use aws_smithy_runtime::client::http::test_util::wire::WireMockServer; use aws_smithy_runtime_api::client::http::SharedHttpClient; - use aws_smithy_types::body::SdkBody; + use aws_smithy_types::timeout::TimeoutConfig; - use http::{Request, Response}; + use secretsmanager::config::BehaviorVersion; - use serde_json::Value; - use std::time::{Duration, SystemTime, UNIX_EPOCH}; - pub const FAKE_ARN: &str = - "arn:aws:secretsmanager:us-west-2:123456789012:secret:{{name}}-NhBWsc"; - pub const DEFAULT_VERSION: &str = "5767290c-d089-49ed-b97c-17086f8c9d79"; - pub const DEFAULT_LABEL: &str = "AWSCURRENT"; - pub const DEFAULT_SECRET_STRING: &str = "hunter2"; + use std::{num::NonZeroUsize, time::Duration}; + + use crate::SecretsManagerCachingClient; // Template GetSecretValue responses for testing pub const GSV_BODY: &str = r###"{ @@ -1117,7 +1277,6 @@ mod tests { ], "CreatedDate": 1569534789.046 }"###; - // Template DescribeSecret responses for testing pub const DESC_BODY: &str = r###"{ "ARN": "{{arn}}", @@ -1134,175 +1293,10 @@ mod tests { "CreatedDate": 1569534789.046 }"###; - // Template for access denied testing - const KMS_ACCESS_DENIED_BODY: &str = r###"{ - "__type":"AccessDeniedException", - "Message":"Access to KMS is not allowed" - }"###; - - // Template for testing resource not found with DescribeSecret - const NOT_FOUND_EXCEPTION_BODY: &str = r###"{ - "__type":"ResourceNotFoundException", - "message":"Secrets Manager can't find the specified secret." - }"###; - - const SECRETSMANAGER_ACCESS_DENIED_BODY: &str = r###"{ - "__type:"AccessDeniedException", - "Message": "is not authorized to perform: secretsmanager:DescribeSecret on resource: XXXXXXXX" - }"###; - - const SECRETSMANAGER_INTERNAL_SERVICE_ERROR_BODY: &str = r###"{ - "__type:"InternalServiceError", - "Message": "Internal service error" - }"###; - - // Template BatchGetSecretValue response for testing - const BATCH_GSV_BODY: &str = r###"{ - "SecretValues": [ - { - "ARN": "{{arn}}", - "Name": "{{name}}", - "VersionId": "{{version}}", - "SecretString": "{{secret}}", - "VersionStages": ["{{label}}"], - "CreatedDate": 1569534789.046 - } - ], - "Errors": [] - }"###; - - // Template BatchGetSecretValue response with per-secret errors - const BATCH_WITH_ERRORS_BODY: &str = r###"{ - "SecretValues": [ - { - "ARN": "{{arn}}", - "Name": "{{valid_name}}", - "VersionId": "{{version}}", - "SecretString": "{{secret}}", - "VersionStages": ["{{label}}"], - "CreatedDate": 1569534789.046 - } - ], - "Errors": [ - { - "SecretId": "{{err_name}}", - "ErrorCode": "ResourceNotFoundException", - "Message": "Secrets Manager can't find the specified secret." - } - ] - }"###; - - // Template for BatchGetSecretValue API-level error - const BATCH_INVALID_PARAMETER_BODY: &str = r###"{ - "__type":"InvalidParameterException", - "message":"The parameter SecretIdList contains invalid values" - }"###; - - // Private helper to look at the request and provide the correct response. - fn format_rsp(req: Request) -> (u16, String) { - let (parts, body) = req.into_parts(); - let target = parts.headers["x-amz-target"].to_str().unwrap(); - - let req_map: serde_json::Map = - serde_json::from_slice(body.bytes().unwrap()).unwrap(); - - // Handle BatchGetSecretValue requests - if target == "secretsmanager.BatchGetSecretValue" { - let secret_ids = req_map - .get("SecretIdList") - .and_then(|v| v.as_array()) - .map(|arr| arr.iter().filter_map(|v| v.as_str()).collect::>()) - .unwrap_or_default(); - let has_filters = req_map.get("Filters").is_some(); - - if secret_ids.iter().any(|s| s.starts_with("BATCHAPIERROR")) { - return (400, BATCH_INVALID_PARAMETER_BODY.to_string()); - } - - if secret_ids.iter().any(|s| s.starts_with("NOTFOUND")) { - let valid_name = secret_ids - .iter() - .find(|s| !s.starts_with("NOTFOUND")) - .unwrap_or(&"Valid"); - let err_name = secret_ids - .iter() - .find(|s| s.starts_with("NOTFOUND")) - .unwrap_or(&"NOTFOUND"); - let rsp = BATCH_WITH_ERRORS_BODY - .replace("{{arn}}", &FAKE_ARN.replace("{{name}}", valid_name)) - .replace("{{valid_name}}", valid_name) - .replace("{{err_name}}", err_name) - .replace("{{version}}", DEFAULT_VERSION) - .replace("{{secret}}", DEFAULT_SECRET_STRING) - .replace("{{label}}", DEFAULT_LABEL); - return (200, rsp); - } - - // For filter-based requests return "TaggedSecret"; for SecretIdList use first ID - let name = if has_filters { - "TaggedSecret" - } else { - secret_ids.first().unwrap_or(&"MyTest") - }; - let rsp = BATCH_GSV_BODY - .replace("{{arn}}", &FAKE_ARN.replace("{{name}}", name)) - .replace("{{name}}", name) - .replace("{{version}}", DEFAULT_VERSION) - .replace("{{secret}}", DEFAULT_SECRET_STRING) - .replace("{{label}}", DEFAULT_LABEL); - return (200, rsp); - } - - // Existing single-request handling - let version = req_map - .get("VersionId") - .map_or(DEFAULT_VERSION, |x| x.as_str().unwrap()); - let label = req_map - .get("VersionStage") - .map_or(DEFAULT_LABEL, |x| x.as_str().unwrap()); - let name = req_map.get("SecretId").unwrap().as_str().unwrap(); // Does not handle full ARN case. - - let secret_string = match name { - secret if secret.starts_with("REFRESHNOW") => SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis() - .to_string(), - _ => DEFAULT_SECRET_STRING.to_string(), - }; - - let (code, template) = match target { - "secretsmanager.GetSecretValue" if name.starts_with("KMSACCESSDENIED") => { - (400, KMS_ACCESS_DENIED_BODY) - } - "secretsmanager.GetSecretValue" if name.starts_with("NOTFOUND") => { - (400, NOT_FOUND_EXCEPTION_BODY) - } - "secretsmanager.GetSecretValue" => (200, GSV_BODY), - "secretsmanager.DescribeSecret" if name.contains("DESCRIBEACCESSDENIED") => { - (400, SECRETSMANAGER_ACCESS_DENIED_BODY) - } - "secretsmanager.DescribeSecret" if name.contains("DESCRIBESERVICEERROR") => { - (500, SECRETSMANAGER_INTERNAL_SERVICE_ERROR_BODY) - } - "secretsmanager.DescribeSecret" => (200, DESC_BODY), - _ => panic!("Unknown operation"), - }; - - // Fill in the template and return the response. - let rsp = template - .replace("{{arn}}", FAKE_ARN) - .replace("{{name}}", name) - .replace("{{version}}", version) - .replace("{{secret}}", &secret_string) - .replace("{{label}}", label); - (code, rsp) - } - // Test client that stubs off network call and provides a canned response. pub fn def_fake_client( - http_client: Option, - endpoint_url: Option, + http_client: SharedHttpClient, + endpoint_url: String, ) -> secretsmanager::Client { use aws_smithy_types::retry::RetryConfig; @@ -1313,8 +1307,7 @@ mod tests { None, "", ); - - let mut config_builder = secretsmanager::Config::builder() + let config_builder = secretsmanager::Config::builder() .behavior_version(BehaviorVersion::latest()) .credentials_provider(fake_creds) .region(secretsmanager::config::Region::new("us-west-2")) @@ -1324,22 +1317,97 @@ mod tests { .build(), ) .retry_config(RetryConfig::disabled()) - .http_client(match http_client { - Some(custom_client) => custom_client, - None => infallible_client_fn(|_req| { - let (code, rsp) = format_rsp(_req); - Response::builder() - .status(code) - .body(SdkBody::from(rsp)) - .unwrap() - }), - }); - config_builder = match endpoint_url { - Some(endpoint_url) => config_builder.endpoint_url(endpoint_url), - None => config_builder, - }; + .http_client(http_client) + .endpoint_url(endpoint_url); secretsmanager::Client::from_conf(config_builder.build()) } + + fn fake_client( + ttl: Option, + ignore_transient_errors: bool, + wire_server: &WireMockServer, + ) -> SecretsManagerCachingClient { + SecretsManagerCachingClient::new( + def_fake_client(wire_server.http_client(), wire_server.endpoint_url()), + NonZeroUsize::new(1000).unwrap(), + match ttl { + Some(ttl) => ttl, + None => Duration::from_secs(1000), + }, + ignore_transient_errors, + ) + .expect("client should create") + } + + #[tokio::test] + async fn test_is_current_gsv_timeout_error_succeeds() { + use aws_smithy_runtime::client::http::test_util::wire::{ + ReplayedEvent, WireMockServer, + }; + + let mock = WireMockServer::start(vec![ + ReplayedEvent::with_body( + GSV_BODY + .replace("{{version}}", "old_version") + .replace("{{label}}", "AWSCURRENT"), + ), + ReplayedEvent::with_body( + DESC_BODY + .replace("{{version}}", "new_version") + .replace("{{label}}", "AWSCURRENT"), + ), + ReplayedEvent::Timeout, + ]) + .await; + + let client = fake_client(Some(Duration::from_secs(0)), true, &mock); + + let secret_id = "GSVTIMEOUT_test_secret"; + + let res1 = client + .get_secret_value(secret_id, None, None, false) + .await + .unwrap(); + + let res2 = client + .get_secret_value(secret_id, None, None, false) + .await + .unwrap(); + + mock.shutdown(); + + assert_eq!(res1, res2) + } + + #[tokio::test] + async fn test_is_current_describe_timeout_error_succeeds() { + use aws_smithy_runtime::client::http::test_util::wire::{ + ReplayedEvent, WireMockServer, + }; + + let mock = WireMockServer::start(vec![ + ReplayedEvent::with_body(GSV_BODY), + ReplayedEvent::Timeout, + ]) + .await; + let client = fake_client(Some(Duration::from_secs(0)), true, &mock); + let secret_id = "DESCRIBETIMEOUT_test_secret"; + let version_id = Some("test_version"); + + let res1 = client + .get_secret_value(secret_id, version_id, None, false) + .await + .unwrap(); + + let res2 = client + .get_secret_value(secret_id, version_id, None, false) + .await + .unwrap(); + + mock.shutdown(); + + assert_eq!(res1, res2) + } } }