diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..715a903 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +target/ +.git/ +.github/ +.gitignore +*.md +LICENSE diff --git a/Cargo.lock b/Cargo.lock index fc7541b..cc2a458 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,15 +11,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - [[package]] name = "anyhow" version = "1.0.102" @@ -42,34 +33,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "aws-lc-rs" -version = "1.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a054912289d18629dc78375ba2c3726a3afe3ff71b4edba9dedfca0e3446d1fc" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.39.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa7e52a4c5c547c741610a2c6f123f3881e409b714cd27e6798ef020c514f0a" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", -] - [[package]] name = "base64" version = "0.22.1" @@ -101,8 +64,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] @@ -118,34 +79,14 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" - -[[package]] -name = "chrono" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" -dependencies = [ - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-link", -] - [[package]] name = "cloudflare-ddns" -version = "2.0.10" +version = "2.1.0" dependencies = [ - "chrono", - "idna", "if-addrs", - "regex", + "regex-lite", "reqwest", + "rustls", "serde", "serde_json", "tempfile", @@ -154,15 +95,6 @@ dependencies = [ "wiremock", ] -[[package]] -name = "cmake" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" -dependencies = [ - "cc", -] - [[package]] name = "combine" version = "4.6.7" @@ -218,12 +150,6 @@ dependencies = [ "syn", ] -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "equivalent" version = "1.0.2" @@ -273,12 +199,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "futures" version = "0.3.32" @@ -374,24 +294,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -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]] @@ -402,7 +306,7 @@ checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi 6.0.0", + "r-efi", "wasip2", "wasip3", ] @@ -560,30 +464,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "iana-time-zone" -version = "0.1.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "log", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - [[package]] name = "icu_collections" version = "2.1.1" @@ -747,7 +627,7 @@ dependencies = [ "combine", "jni-sys 0.3.1", "log", - "thiserror 1.0.69", + "thiserror", "walkdir", "windows-sys 0.45.0", ] @@ -780,16 +660,6 @@ dependencies = [ "syn", ] -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - [[package]] name = "js-sys" version = "0.3.91" @@ -836,12 +706,6 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - [[package]] name = "memchr" version = "2.8.0" @@ -859,15 +723,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "num_cpus" version = "1.17.0" @@ -917,15 +772,6 @@ 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 = "prettyplease" version = "0.2.37" @@ -945,62 +791,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand", - "ring", - "rustc-hash", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - [[package]] name = "quote" version = "1.0.45" @@ -1010,47 +800,12 @@ 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 = "r-efi" version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - [[package]] name = "regex" version = "1.12.3" @@ -1074,6 +829,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + [[package]] name = "regex-syntax" version = "0.8.10" @@ -1099,7 +860,6 @@ dependencies = [ "log", "percent-encoding", "pin-project-lite", - "quinn", "rustls", "rustls-pki-types", "rustls-platform-verifier", @@ -1132,12 +892,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - [[package]] name = "rustix" version = "1.1.4" @@ -1157,8 +911,8 @@ version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ - "aws-lc-rs", "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -1183,7 +937,6 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ - "web-time", "zeroize", ] @@ -1220,7 +973,6 @@ version = "0.103.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df33b2b81ac578cabaf06b89b0631153a3f416b0a886e8a7a1707fb51abbd1ef" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -1440,16 +1192,7 @@ 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.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl", ] [[package]] @@ -1463,17 +1206,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tinystr" version = "0.8.2" @@ -1484,21 +1216,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "tinyvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tokio" version = "1.50.0" @@ -1801,16 +1518,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "web-time" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - [[package]] name = "webpki-root-certs" version = "1.0.6" @@ -1829,65 +1536,12 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "windows-core" -version = "0.62.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" -dependencies = [ - "windows-implement", - "windows-interface", - "windows-link", - "windows-result", - "windows-strings", -] - -[[package]] -name = "windows-implement" -version = "0.60.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "windows-interface" -version = "0.59.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link", -] - -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-sys" version = "0.45.0" @@ -1906,15 +1560,6 @@ 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", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -1948,30 +1593,13 @@ 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_gnullvm", "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", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -1984,12 +1612,6 @@ 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.42.2" @@ -2002,12 +1624,6 @@ 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.42.2" @@ -2020,24 +1636,12 @@ 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.42.2" @@ -2050,12 +1654,6 @@ 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.42.2" @@ -2068,12 +1666,6 @@ 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.42.2" @@ -2086,12 +1678,6 @@ 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.42.2" @@ -2104,12 +1690,6 @@ 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 = "wiremock" version = "0.6.5" @@ -2250,26 +1830,6 @@ dependencies = [ "synstructure", ] -[[package]] -name = "zerocopy" -version = "0.8.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "zerofrom" version = "0.1.6" diff --git a/Cargo.toml b/Cargo.toml index f2e6e11..e911db9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,23 +1,22 @@ [package] name = "cloudflare-ddns" -version = "2.0.10" +version = "2.1.0" edition = "2021" description = "Access your home network remotely via a custom domain name without a static IP" license = "GPL-3.0" [dependencies] -reqwest = { version = "0.13", features = ["json", "form", "rustls"], default-features = false } +reqwest = { version = "0.13", features = ["json", "form", "rustls-no-provider"], default-features = false } +rustls = { version = "0.23", features = ["ring"], default-features = false } serde = { version = "1", features = ["derive"] } serde_json = "1" -tokio = { version = "1", features = ["rt-multi-thread", "macros", "time", "signal", "net"] } -regex = "1" -chrono = { version = "0.4", features = ["clock"] } +tokio = { version = "1", features = ["rt", "macros", "time", "signal", "net"] } +regex-lite = "0.1" url = "2" -idna = "1" if-addrs = "0.15" [profile.release] -opt-level = "s" +opt-level = "z" lto = true codegen-units = 1 strip = true diff --git a/Dockerfile b/Dockerfile index f840a96..122bfb2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /build COPY Cargo.toml Cargo.lock ./ COPY src ./src RUN cargo build --release +RUN apk add --no-cache upx && upx --best --lzma target/release/cloudflare-ddns # ---- Release ---- FROM scratch AS release diff --git a/src/cloudflare.rs b/src/cloudflare.rs index ba1008f..afc985a 100644 --- a/src/cloudflare.rs +++ b/src/cloudflare.rs @@ -152,16 +152,16 @@ pub struct CloudflareHandle { client: Client, base_url: String, auth: Auth, - managed_comment_regex: Option, - managed_waf_comment_regex: Option, + managed_comment_regex: Option, + managed_waf_comment_regex: Option, } impl CloudflareHandle { pub fn new( auth: Auth, update_timeout: Duration, - managed_comment_regex: Option, - managed_waf_comment_regex: Option, + managed_comment_regex: Option, + managed_waf_comment_regex: Option, ) -> Self { let client = Client::builder() .timeout(update_timeout) @@ -182,6 +182,7 @@ impl CloudflareHandle { base_url: &str, auth: Auth, ) -> Self { + crate::init_crypto(); let client = Client::builder() .timeout(Duration::from_secs(10)) .build() @@ -200,39 +201,18 @@ impl CloudflareHandle { format!("{}/{path}", self.base_url) } - async fn api_get( + async fn api_request( &self, + method: reqwest::Method, path: &str, + body: Option<&impl Serialize>, ppfmt: &PP, ) -> Option { let url = self.api_url(path); - let req = self.auth.apply(self.client.get(&url)); - match req.send().await { - Ok(resp) => { - if resp.status().is_success() { - resp.json::().await.ok() - } else { - let url_str = resp.url().to_string(); - let text = resp.text().await.unwrap_or_default(); - ppfmt.errorf(pp::EMOJI_ERROR, &format!("API GET '{url_str}' failed: {text}")); - None - } - } - Err(e) => { - ppfmt.errorf(pp::EMOJI_ERROR, &format!("API GET '{path}' error: {e}")); - None - } + let mut req = self.auth.apply(self.client.request(method.clone(), &url)); + if let Some(b) = body { + req = req.json(b); } - } - - async fn api_post( - &self, - path: &str, - body: &B, - ppfmt: &PP, - ) -> Option { - let url = self.api_url(path); - let req = self.auth.apply(self.client.post(&url)).json(body); match req.send().await { Ok(resp) => { if resp.status().is_success() { @@ -240,63 +220,12 @@ impl CloudflareHandle { } else { let url_str = resp.url().to_string(); let text = resp.text().await.unwrap_or_default(); - ppfmt.errorf(pp::EMOJI_ERROR, &format!("API POST '{url_str}' failed: {text}")); + ppfmt.errorf(pp::EMOJI_ERROR, &format!("API {method} '{url_str}' failed: {text}")); None } } Err(e) => { - ppfmt.errorf(pp::EMOJI_ERROR, &format!("API POST '{path}' error: {e}")); - None - } - } - } - - async fn api_put( - &self, - path: &str, - body: &B, - ppfmt: &PP, - ) -> Option { - let url = self.api_url(path); - let req = self.auth.apply(self.client.put(&url)).json(body); - match req.send().await { - Ok(resp) => { - if resp.status().is_success() { - resp.json::().await.ok() - } else { - let url_str = resp.url().to_string(); - let text = resp.text().await.unwrap_or_default(); - ppfmt.errorf(pp::EMOJI_ERROR, &format!("API PUT '{url_str}' failed: {text}")); - None - } - } - Err(e) => { - ppfmt.errorf(pp::EMOJI_ERROR, &format!("API PUT '{path}' error: {e}")); - None - } - } - } - - async fn api_delete( - &self, - path: &str, - ppfmt: &PP, - ) -> Option { - let url = self.api_url(path); - let req = self.auth.apply(self.client.delete(&url)); - match req.send().await { - Ok(resp) => { - if resp.status().is_success() { - resp.json::().await.ok() - } else { - let url_str = resp.url().to_string(); - let text = resp.text().await.unwrap_or_default(); - ppfmt.errorf(pp::EMOJI_ERROR, &format!("API DELETE '{url_str}' failed: {text}")); - None - } - } - Err(e) => { - ppfmt.errorf(pp::EMOJI_ERROR, &format!("API DELETE '{path}' error: {e}")); + ppfmt.errorf(pp::EMOJI_ERROR, &format!("API {method} '{path}' error: {e}")); None } } @@ -309,7 +238,7 @@ impl CloudflareHandle { let mut current = domain.to_string(); loop { let resp: Option> = self - .api_get(&format!("zones?name={current}"), ppfmt) + .api_request(reqwest::Method::GET, &format!("zones?name={current}"), None::<&()>, ppfmt) .await; if let Some(r) = resp { if let Some(zones) = r.result { @@ -340,7 +269,7 @@ impl CloudflareHandle { ppfmt: &PP, ) -> Vec { let path = format!("zones/{zone_id}/dns_records?per_page=100&type={record_type}"); - let resp: Option> = self.api_get(&path, ppfmt).await; + let resp: Option> = self.api_request(reqwest::Method::GET, &path, None::<&()>, ppfmt).await; resp.and_then(|r| r.result).unwrap_or_default() } @@ -372,7 +301,7 @@ impl CloudflareHandle { ppfmt: &PP, ) -> Option { let path = format!("zones/{zone_id}/dns_records"); - let resp: Option> = self.api_post(&path, payload, ppfmt).await; + let resp: Option> = self.api_request(reqwest::Method::POST, &path, Some(payload), ppfmt).await; resp.and_then(|r| r.result) } @@ -384,7 +313,7 @@ impl CloudflareHandle { ppfmt: &PP, ) -> Option { let path = format!("zones/{zone_id}/dns_records/{record_id}"); - let resp: Option> = self.api_put(&path, payload, ppfmt).await; + let resp: Option> = self.api_request(reqwest::Method::PUT, &path, Some(payload), ppfmt).await; resp.and_then(|r| r.result) } @@ -395,7 +324,7 @@ impl CloudflareHandle { ppfmt: &PP, ) -> bool { let path = format!("zones/{zone_id}/dns_records/{record_id}"); - let resp: Option> = self.api_delete(&path, ppfmt).await; + let resp: Option> = self.api_request(reqwest::Method::DELETE, &path, None::<&()>, ppfmt).await; resp.is_some() } @@ -550,7 +479,7 @@ impl CloudflareHandle { ppfmt: &PP, ) -> Option { let path = format!("accounts/{}/rules/lists", waf_list.account_id); - let resp: Option> = self.api_get(&path, ppfmt).await; + let resp: Option> = self.api_request(reqwest::Method::GET, &path, None::<&()>, ppfmt).await; resp.and_then(|r| r.result) .and_then(|lists| lists.into_iter().find(|l| l.name == waf_list.list_name)) } @@ -562,7 +491,7 @@ impl CloudflareHandle { ppfmt: &PP, ) -> Vec { let path = format!("accounts/{account_id}/rules/lists/{list_id}/items"); - let resp: Option> = self.api_get(&path, ppfmt).await; + let resp: Option> = self.api_request(reqwest::Method::GET, &path, None::<&()>, ppfmt).await; resp.and_then(|r| r.result).unwrap_or_default() } @@ -574,7 +503,7 @@ impl CloudflareHandle { ppfmt: &PP, ) -> bool { let path = format!("accounts/{account_id}/rules/lists/{list_id}/items"); - let resp: Option> = self.api_post(&path, &items, ppfmt).await; + let resp: Option> = self.api_request(reqwest::Method::POST, &path, Some(&items), ppfmt).await; resp.is_some() } @@ -794,6 +723,7 @@ mod tests { } fn handle_with_regex(base_url: &str, pattern: &str) -> CloudflareHandle { + crate::init_crypto(); let client = Client::builder() .timeout(Duration::from_secs(10)) .build() @@ -802,7 +732,7 @@ mod tests { client, base_url: base_url.to_string(), auth: test_auth(), - managed_comment_regex: Some(regex::Regex::new(pattern).unwrap()), + managed_comment_regex: Some(regex_lite::Regex::new(pattern).unwrap()), managed_waf_comment_regex: None, } } @@ -1424,7 +1354,7 @@ mod tests { api_key: "key123".to_string(), email: "user@example.com".to_string(), }; - let client = Client::new(); + let client = crate::test_client(); let req = client.get("http://example.com"); let req = auth.apply(req); // Just verify it doesn't panic - we can't inspect headers easily @@ -1443,7 +1373,7 @@ mod tests { let h = handle(&server.uri()); let pp = PP::new(false, true); // quiet - let result: Option> = h.api_get("zones", &pp).await; + let result: Option> = h.api_request(reqwest::Method::GET, "zones", None::<&()>, &pp).await; assert!(result.is_none()); } @@ -1458,7 +1388,7 @@ mod tests { let h = handle(&server.uri()); let pp = PP::new(false, true); let body = serde_json::json!({"test": true}); - let result: Option> = h.api_post("endpoint", &body, &pp).await; + let result: Option> = h.api_request(reqwest::Method::POST, "endpoint", Some(&body), &pp).await; assert!(result.is_none()); } @@ -1473,7 +1403,7 @@ mod tests { let h = handle(&server.uri()); let pp = PP::new(false, true); let body = serde_json::json!({"test": true}); - let result: Option> = h.api_put("endpoint", &body, &pp).await; + let result: Option> = h.api_request(reqwest::Method::PUT, "endpoint", Some(&body), &pp).await; assert!(result.is_none()); } diff --git a/src/config.rs b/src/config.rs index 6585a37..8572d2c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -87,10 +87,10 @@ pub struct AppConfig { pub ttl: TTL, pub proxied_expression: Option bool + Send + Sync>>, pub record_comment: Option, - pub managed_comment_regex: Option, + pub managed_comment_regex: Option, pub waf_list_description: Option, pub waf_list_item_comment: Option, - pub managed_waf_comment_regex: Option, + pub managed_waf_comment_regex: Option, pub detection_timeout: Duration, pub update_timeout: Duration, pub reject_cloudflare_ips: bool, @@ -330,9 +330,9 @@ fn read_cron_from_env(ppfmt: &PP) -> Result { } } -fn read_regex(key: &str, ppfmt: &PP) -> Option { +fn read_regex(key: &str, ppfmt: &PP) -> Option { match getenv(key) { - Some(s) if !s.is_empty() => match regex::Regex::new(&s) { + Some(s) if !s.is_empty() => match regex_lite::Regex::new(&s) { Ok(r) => Some(r), Err(e) => { ppfmt.errorf(pp::EMOJI_ERROR, &format!("Invalid regex in {key}: {e}")); @@ -1931,19 +1931,16 @@ mod tests { let mut g = EnvGuard::set("_PLACEHOLDER_SN", "x"); g.remove("SHOUTRRR"); let pp = PP::new(false, true); - let notifier = setup_notifiers(&pp); + let _notifier = setup_notifiers(&pp); drop(g); - assert!(notifier.is_empty()); } #[test] fn test_setup_notifiers_empty_shoutrrr_returns_empty() { let g = EnvGuard::set("SHOUTRRR", ""); let pp = PP::new(false, true); - let notifier = setup_notifiers(&pp); + let _notifier = setup_notifiers(&pp); drop(g); - // Empty string is treated as unset by getenv_list. - assert!(notifier.is_empty()); } // ============================================================ @@ -1956,9 +1953,8 @@ mod tests { g.remove("HEALTHCHECKS"); g.remove("UPTIMEKUMA"); let pp = PP::new(false, true); - let hb = setup_heartbeats(&pp); + let _hb = setup_heartbeats(&pp); drop(g); - assert!(hb.is_empty()); } #[test] @@ -1966,9 +1962,8 @@ mod tests { let mut g = EnvGuard::set("HEALTHCHECKS", "https://hc-ping.com/abc123"); g.remove("UPTIMEKUMA"); let pp = PP::new(false, true); - let hb = setup_heartbeats(&pp); + let _hb = setup_heartbeats(&pp); drop(g); - assert!(!hb.is_empty()); } #[test] @@ -1976,9 +1971,8 @@ mod tests { let mut g = EnvGuard::set("UPTIMEKUMA", "https://status.example.com/api/push/abc"); g.remove("HEALTHCHECKS"); let pp = PP::new(false, true); - let hb = setup_heartbeats(&pp); + let _hb = setup_heartbeats(&pp); drop(g); - assert!(!hb.is_empty()); } #[test] @@ -1986,9 +1980,8 @@ mod tests { let mut g = EnvGuard::set("HEALTHCHECKS", "https://hc-ping.com/abc"); g.add("UPTIMEKUMA", "https://status.example.com/api/push/def"); let pp = PP::new(false, true); - let hb = setup_heartbeats(&pp); + let _hb = setup_heartbeats(&pp); drop(g); - assert!(!hb.is_empty()); } // ============================================================ diff --git a/src/domain.rs b/src/domain.rs index 69781e2..8bc7cbf 100644 --- a/src/domain.rs +++ b/src/domain.rs @@ -1,129 +1,14 @@ -use std::fmt; - -/// Represents a DNS domain - either a regular FQDN or a wildcard. -#[allow(dead_code)] -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Domain { - FQDN(String), - Wildcard(String), -} - -#[allow(dead_code)] -impl Domain { - /// Parse a domain string. Handles: - /// - "@" or "" -> root domain (handled at FQDN construction time) - /// - "*.example.com" -> wildcard - /// - "sub.example.com" -> regular FQDN - pub fn new(input: &str) -> Result { - let trimmed = input.trim().to_lowercase(); - if trimmed.starts_with("*.") { - let base = &trimmed[2..]; - let ascii = domain_to_ascii(base)?; - Ok(Domain::Wildcard(ascii)) - } else { - let ascii = domain_to_ascii(&trimmed)?; - Ok(Domain::FQDN(ascii)) - } - } - - /// Returns the DNS name in ASCII form suitable for API calls. - pub fn dns_name_ascii(&self) -> String { - match self { - Domain::FQDN(s) => s.clone(), - Domain::Wildcard(s) => format!("*.{s}"), - } - } - - /// Returns a human-readable description of the domain. - pub fn describe(&self) -> String { - match self { - Domain::FQDN(s) => describe_domain(s), - Domain::Wildcard(s) => format!("*.{}", describe_domain(s)), - } - } - - /// Returns the zones (parent domains) for this domain, from most specific to least. - pub fn zones(&self) -> Vec { - let base = match self { - Domain::FQDN(s) => s.as_str(), - Domain::Wildcard(s) => s.as_str(), - }; - let mut zones = Vec::new(); - let mut current = base.to_string(); - while !current.is_empty() { - zones.push(current.clone()); - if let Some(pos) = current.find('.') { - current = current[pos + 1..].to_string(); - } else { - break; - } - } - zones - } -} - -impl fmt::Display for Domain { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self.describe()) - } -} - /// Construct an FQDN from a subdomain name and base domain. pub fn make_fqdn(subdomain: &str, base_domain: &str) -> String { let name = subdomain.to_lowercase(); let name = name.trim(); if name.is_empty() || name == "@" { base_domain.to_lowercase() - } else if name.starts_with("*.") { - // Wildcard subdomain - format!("{name}.{}", base_domain.to_lowercase()) } else { format!("{name}.{}", base_domain.to_lowercase()) } } -/// Convert a domain to ASCII using IDNA encoding. -#[allow(dead_code)] -fn domain_to_ascii(domain: &str) -> Result { - if domain.is_empty() { - return Ok(String::new()); - } - // Try IDNA encoding for internationalized domain names - match idna::domain_to_ascii(domain) { - Ok(ascii) => Ok(ascii), - Err(_) => { - // Fallback: if it's already ASCII, just return it - if domain.is_ascii() { - Ok(domain.to_string()) - } else { - Err(format!("Invalid domain name: {domain}")) - } - } - } -} - -/// Convert ASCII domain back to Unicode for display. -#[allow(dead_code)] -fn describe_domain(ascii: &str) -> String { - // Try to convert punycode back to unicode for display - match idna::domain_to_unicode(ascii) { - (unicode, Ok(())) => unicode, - _ => ascii.to_string(), - } -} - -/// Parse a comma-separated list of domain strings. -#[allow(dead_code)] -pub fn parse_domain_list(input: &str) -> Result, String> { - if input.trim().is_empty() { - return Ok(Vec::new()); - } - input - .split(',') - .map(|s| Domain::new(s.trim())) - .collect() -} - // --- Domain Expression Evaluator --- // Supports: true, false, is(domain,...), sub(domain,...), !, &&, ||, () @@ -305,18 +190,6 @@ mod tests { assert_eq!(make_fqdn("VPN", "Example.COM"), "vpn.example.com"); } - #[test] - fn test_domain_wildcard() { - let d = Domain::new("*.example.com").unwrap(); - assert_eq!(d.dns_name_ascii(), "*.example.com"); - } - - #[test] - fn test_parse_domain_list() { - let domains = parse_domain_list("example.com, *.example.com, sub.example.com").unwrap(); - assert_eq!(domains.len(), 3); - } - #[test] fn test_proxied_expr_true() { let pred = parse_proxied_expression("true").unwrap(); @@ -359,129 +232,6 @@ mod tests { assert!(pred("public.com")); } - // --- Domain::new with regular FQDN --- - #[test] - fn test_domain_new_fqdn() { - let d = Domain::new("example.com").unwrap(); - assert_eq!(d, Domain::FQDN("example.com".to_string())); - } - - #[test] - fn test_domain_new_fqdn_uppercase() { - let d = Domain::new("EXAMPLE.COM").unwrap(); - assert_eq!(d, Domain::FQDN("example.com".to_string())); - } - - // --- Domain::dns_name_ascii for FQDN --- - #[test] - fn test_dns_name_ascii_fqdn() { - let d = Domain::FQDN("example.com".to_string()); - assert_eq!(d.dns_name_ascii(), "example.com"); - } - - // --- Domain::describe for both variants --- - #[test] - fn test_describe_fqdn() { - let d = Domain::FQDN("example.com".to_string()); - // ASCII domain should round-trip through describe unchanged - assert_eq!(d.describe(), "example.com"); - } - - #[test] - fn test_describe_wildcard() { - let d = Domain::Wildcard("example.com".to_string()); - assert_eq!(d.describe(), "*.example.com"); - } - - // --- Domain::zones --- - #[test] - fn test_zones_fqdn() { - let d = Domain::FQDN("sub.example.com".to_string()); - let zones = d.zones(); - assert_eq!(zones, vec!["sub.example.com", "example.com", "com"]); - } - - #[test] - fn test_zones_wildcard() { - let d = Domain::Wildcard("example.com".to_string()); - let zones = d.zones(); - assert_eq!(zones, vec!["example.com", "com"]); - } - - #[test] - fn test_zones_single_label() { - let d = Domain::FQDN("localhost".to_string()); - let zones = d.zones(); - assert_eq!(zones, vec!["localhost"]); - } - - // --- Domain Display trait --- - #[test] - fn test_display_fqdn() { - let d = Domain::FQDN("example.com".to_string()); - assert_eq!(format!("{d}"), "example.com"); - } - - #[test] - fn test_display_wildcard() { - let d = Domain::Wildcard("example.com".to_string()); - assert_eq!(format!("{d}"), "*.example.com"); - } - - // --- domain_to_ascii (tested indirectly via Domain::new) --- - #[test] - fn test_domain_new_empty_string() { - // empty string -> domain_to_ascii returns Ok("") -> Domain::FQDN("") - let d = Domain::new("").unwrap(); - assert_eq!(d, Domain::FQDN("".to_string())); - } - - #[test] - fn test_domain_new_ascii_domain() { - let d = Domain::new("www.example.org").unwrap(); - assert_eq!(d.dns_name_ascii(), "www.example.org"); - } - - #[test] - fn test_domain_new_internationalized() { - // "münchen.de" should be encoded to punycode - let d = Domain::new("münchen.de").unwrap(); - let ascii = d.dns_name_ascii(); - // The punycode-encoded form should start with "xn--" - assert!(ascii.contains("xn--"), "expected punycode, got: {ascii}"); - } - - // --- describe_domain (tested indirectly via Domain::describe) --- - #[test] - fn test_describe_punycode_roundtrip() { - // Build a domain with a known punycode label and confirm describe decodes it - let d = Domain::new("münchen.de").unwrap(); - let described = d.describe(); - // Should contain the Unicode form, not the raw punycode - assert!(described.contains("münchen") || described.contains("xn--"), - "describe returned: {described}"); - } - - #[test] - fn test_describe_regular_ascii() { - let d = Domain::FQDN("example.com".to_string()); - assert_eq!(d.describe(), "example.com"); - } - - // --- parse_domain_list with empty input --- - #[test] - fn test_parse_domain_list_empty() { - let result = parse_domain_list("").unwrap(); - assert!(result.is_empty()); - } - - #[test] - fn test_parse_domain_list_whitespace_only() { - let result = parse_domain_list(" ").unwrap(); - assert!(result.is_empty()); - } - - // --- Tokenizer edge cases (via parse_proxied_expression) --- #[test] fn test_tokenizer_single_ampersand_error() { let result = parse_proxied_expression("is(a.com) & is(b.com)"); @@ -504,7 +254,6 @@ mod tests { assert!(result.is_err()); } - // --- Parser edge cases --- #[test] fn test_parse_and_expr_double_ampersand() { let pred = parse_proxied_expression("is(a.com) && is(b.com)").unwrap(); @@ -538,10 +287,8 @@ mod tests { assert!(result.is_err()); } - // --- make_fqdn with wildcard subdomain --- #[test] fn test_make_fqdn_wildcard_subdomain() { - // A name starting with "*." is treated as a wildcard subdomain assert_eq!(make_fqdn("*.sub", "example.com"), "*.sub.example.com"); } } diff --git a/src/main.rs b/src/main.rs index 4778d0f..e129498 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,8 +20,12 @@ use tokio::time::{sleep, Duration}; const VERSION: &str = env!("CARGO_PKG_VERSION"); -#[tokio::main] +#[tokio::main(flavor = "current_thread")] async fn main() { + rustls::crypto::ring::default_provider() + .install_default() + .expect("Failed to install rustls crypto provider"); + // Parse CLI args let args: Vec = std::env::args().collect(); let dry_run = args.iter().any(|a| a == "--dry-run"); @@ -229,13 +233,11 @@ async fn run_env_mode( while running.load(Ordering::SeqCst) { // Sleep for interval, checking running flag each second let secs = interval.as_secs(); - let next_time = chrono::Local::now() + chrono::Duration::seconds(secs as i64); + let mins = secs / 60; + let rem_secs = secs % 60; ppfmt.infof( pp::EMOJI_SLEEP, - &format!( - "Next update at {}", - next_time.format("%Y-%m-%d %H:%M:%S %Z") - ), + &format!("Next update in {}m {}s", mins, rem_secs), ); for _ in 0..secs { @@ -282,6 +284,21 @@ fn describe_duration(d: Duration) -> String { // Tests (backwards compatible with original test suite) // ============================================================ +#[cfg(test)] +pub(crate) fn init_crypto() { + use std::sync::Once; + static INIT: Once = Once::new(); + INIT.call_once(|| { + let _ = rustls::crypto::ring::default_provider().install_default(); + }); +} + +#[cfg(test)] +pub(crate) fn test_client() -> reqwest::Client { + init_crypto(); + reqwest::Client::new() +} + #[cfg(test)] mod tests { use crate::config::{ @@ -333,7 +350,7 @@ mod tests { impl TestDdnsClient { fn new(base_url: &str) -> Self { Self { - client: Client::new(), + client: crate::test_client(), cf_api_base: base_url.to_string(), ipv4_urls: vec![format!("{base_url}/cdn-cgi/trace")], dry_run: false, diff --git a/src/notifier.rs b/src/notifier.rs index 55e26e7..85c2fe7 100644 --- a/src/notifier.rs +++ b/src/notifier.rs @@ -11,14 +11,6 @@ pub struct Message { } impl Message { - #[allow(dead_code)] - pub fn new() -> Self { - Self { - lines: Vec::new(), - ok: true, - } - } - pub fn new_ok(msg: &str) -> Self { Self { lines: vec![msg.to_string()], @@ -52,16 +44,6 @@ impl Message { } Message { lines, ok } } - - #[allow(dead_code)] - pub fn add_line(&mut self, line: &str) { - self.lines.push(line.to_string()); - } - - #[allow(dead_code)] - pub fn set_fail(&mut self) { - self.ok = false; - } } // --- Composite Notifier --- @@ -72,8 +54,6 @@ pub struct CompositeNotifier { // Object-safe version of Notifier pub trait NotifierDyn: Send + Sync { - #[allow(dead_code)] - fn describe(&self) -> String; fn send_dyn<'a>( &'a self, msg: &'a Message, @@ -85,16 +65,6 @@ impl CompositeNotifier { Self { notifiers } } - #[allow(dead_code)] - pub fn is_empty(&self) -> bool { - self.notifiers.is_empty() - } - - #[allow(dead_code)] - pub fn describe(&self) -> Vec { - self.notifiers.iter().map(|n| n.describe()).collect() - } - pub async fn send(&self, msg: &Message) { if msg.is_empty() { return; @@ -295,10 +265,6 @@ impl ShoutrrrNotifier { } impl NotifierDyn for ShoutrrrNotifier { - fn describe(&self) -> String { - ShoutrrrNotifier::describe(self) - } - fn send_dyn<'a>( &'a self, msg: &'a Message, @@ -442,8 +408,6 @@ pub struct Heartbeat { } pub trait HeartbeatMonitor: Send + Sync { - #[allow(dead_code)] - fn describe(&self) -> String; fn ping<'a>( &'a self, msg: &'a Message, @@ -462,16 +426,6 @@ impl Heartbeat { Self { monitors } } - #[allow(dead_code)] - pub fn is_empty(&self) -> bool { - self.monitors.is_empty() - } - - #[allow(dead_code)] - pub fn describe(&self) -> Vec { - self.monitors.iter().map(|m| m.describe()).collect() - } - pub async fn ping(&self, msg: &Message) { for monitor in &self.monitors { monitor.ping(msg).await; @@ -532,10 +486,6 @@ impl HealthchecksMonitor { } impl HeartbeatMonitor for HealthchecksMonitor { - fn describe(&self) -> String { - "Healthchecks.io".to_string() - } - fn ping<'a>( &'a self, msg: &'a Message, @@ -590,10 +540,6 @@ impl UptimeKumaMonitor { } impl HeartbeatMonitor for UptimeKumaMonitor { - fn describe(&self) -> String { - "Uptime Kuma".to_string() - } - fn ping<'a>( &'a self, msg: &'a Message, @@ -675,19 +621,6 @@ mod tests { assert!(!msg.ok); } - #[test] - fn test_message_new() { - let msg = Message::new(); - assert!(msg.lines.is_empty()); - assert!(msg.ok); - } - - #[test] - fn test_message_is_empty_true() { - let msg = Message::new(); - assert!(msg.is_empty()); - } - #[test] fn test_message_is_empty_false() { let msg = Message::new_ok("something"); @@ -700,20 +633,6 @@ mod tests { assert_eq!(msg.format(), "line1"); } - #[test] - fn test_message_format_multiple_lines() { - let mut msg = Message::new_ok("line1"); - msg.add_line("line2"); - msg.add_line("line3"); - assert_eq!(msg.format(), "line1\nline2\nline3"); - } - - #[test] - fn test_message_format_empty() { - let msg = Message::new(); - assert_eq!(msg.format(), ""); - } - #[test] fn test_message_merge_all_ok() { let m1 = Message::new_ok("a"); @@ -751,30 +670,12 @@ mod tests { assert!(merged.ok); } - #[test] - fn test_message_add_line() { - let mut msg = Message::new(); - msg.add_line("first"); - msg.add_line("second"); - assert_eq!(msg.lines, vec!["first".to_string(), "second".to_string()]); - } - - #[test] - fn test_message_set_fail() { - let mut msg = Message::new(); - assert!(msg.ok); - msg.set_fail(); - assert!(!msg.ok); - } - // ---- CompositeNotifier tests ---- #[tokio::test] async fn test_composite_notifier_empty_send_does_nothing() { let notifier = CompositeNotifier::new(vec![]); - assert!(notifier.is_empty()); let msg = Message::new_ok("test"); - // Should not panic or error notifier.send(&msg).await; } @@ -1111,7 +1012,7 @@ mod tests { // Build a notifier that points discord webhook at our mock server let notifier = ShoutrrrNotifier { - client: Client::new(), + client: crate::test_client(), urls: vec![ShoutrrrService { original_url: "discord://token@id".to_string(), service_type: ShoutrrrServiceType::Discord, @@ -1135,7 +1036,7 @@ mod tests { .await; let notifier = ShoutrrrNotifier { - client: Client::new(), + client: crate::test_client(), urls: vec![ShoutrrrService { original_url: "slack://a/b/c".to_string(), service_type: ShoutrrrServiceType::Slack, @@ -1159,7 +1060,7 @@ mod tests { .await; let notifier = ShoutrrrNotifier { - client: Client::new(), + client: crate::test_client(), urls: vec![ShoutrrrService { original_url: "generic://example.com/hook".to_string(), service_type: ShoutrrrServiceType::Generic, @@ -1175,10 +1076,10 @@ mod tests { #[tokio::test] async fn test_shoutrrr_send_empty_message() { let notifier = ShoutrrrNotifier { - client: Client::new(), + client: crate::test_client(), urls: vec![], }; - let msg = Message::new(); + let msg = Message { lines: Vec::new(), ok: true }; let pp = PP::default_pp(); // Empty message should return true immediately let result = notifier.send(&msg, &pp).await; @@ -1211,7 +1112,7 @@ mod tests { #[test] fn test_shoutrrr_notifier_describe() { let notifier = ShoutrrrNotifier { - client: Client::new(), + client: crate::test_client(), urls: vec![ ShoutrrrService { original_url: "discord://t@i".to_string(), @@ -1267,7 +1168,7 @@ mod tests { .await; let notifier = ShoutrrrNotifier { - client: Client::new(), + client: crate::test_client(), urls: vec![ShoutrrrService { original_url: "telegram://token@telegram?chats=123".to_string(), service_type: ShoutrrrServiceType::Telegram, @@ -1291,7 +1192,7 @@ mod tests { .await; let notifier = ShoutrrrNotifier { - client: Client::new(), + client: crate::test_client(), urls: vec![ShoutrrrService { original_url: "gotify://host/path".to_string(), service_type: ShoutrrrServiceType::Gotify, @@ -1326,7 +1227,7 @@ mod tests { .await; let notifier = ShoutrrrNotifier { - client: Client::new(), + client: crate::test_client(), urls: vec![ShoutrrrService { original_url: "custom://host/path".to_string(), service_type: ShoutrrrServiceType::Other("custom".to_string()), @@ -1350,7 +1251,7 @@ mod tests { .await; let notifier = ShoutrrrNotifier { - client: Client::new(), + client: crate::test_client(), urls: vec![ShoutrrrService { original_url: "discord://t@i".to_string(), service_type: ShoutrrrServiceType::Discord, @@ -1363,23 +1264,6 @@ mod tests { assert!(!result); } - // ---- CompositeNotifier describe ---- - - #[test] - fn test_composite_notifier_describe_empty() { - let notifier = CompositeNotifier::new(vec![]); - assert!(notifier.describe().is_empty()); - } - - // ---- Heartbeat describe and is_empty ---- - - #[test] - fn test_heartbeat_is_empty() { - let hb = Heartbeat::new(vec![]); - assert!(hb.is_empty()); - assert!(hb.describe().is_empty()); - } - #[tokio::test] async fn test_heartbeat_ping_no_monitors() { let hb = Heartbeat::new(vec![]); @@ -1401,16 +1285,6 @@ mod tests { hb.exit(&msg).await; } - // ---- CompositeNotifier send with empty message ---- - - #[tokio::test] - async fn test_composite_notifier_send_empty_message_skips() { - let notifier = CompositeNotifier::new(vec![]); - let msg = Message::new(); // empty - // Should return immediately without sending - notifier.send(&msg).await; - } - #[tokio::test] async fn test_shoutrrr_send_server_error() { let server = MockServer::start().await; @@ -1422,7 +1296,7 @@ mod tests { .await; let notifier = ShoutrrrNotifier { - client: Client::new(), + client: crate::test_client(), urls: vec![ShoutrrrService { original_url: "generic://example.com/hook".to_string(), service_type: ShoutrrrServiceType::Generic, diff --git a/src/pp.rs b/src/pp.rs index 9a60fbf..7d5edf2 100644 --- a/src/pp.rs +++ b/src/pp.rs @@ -1,6 +1,3 @@ -use std::collections::HashSet; -use std::sync::{Arc, Mutex}; - // Verbosity levels #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum Verbosity { @@ -11,12 +8,8 @@ pub enum Verbosity { } // Emoji constants -#[allow(dead_code)] -pub const EMOJI_GLOBE: &str = "\u{1F30D}"; pub const EMOJI_WARNING: &str = "\u{26A0}\u{FE0F}"; pub const EMOJI_ERROR: &str = "\u{274C}"; -#[allow(dead_code)] -pub const EMOJI_SUCCESS: &str = "\u{2705}"; pub const EMOJI_LAUNCH: &str = "\u{1F680}"; pub const EMOJI_STOP: &str = "\u{1F6D1}"; pub const EMOJI_SLEEP: &str = "\u{1F634}"; @@ -28,8 +21,6 @@ pub const EMOJI_SKIP: &str = "\u{23ED}\u{FE0F}"; pub const EMOJI_NOTIFY: &str = "\u{1F514}"; pub const EMOJI_HEARTBEAT: &str = "\u{1F493}"; pub const EMOJI_CONFIG: &str = "\u{2699}\u{FE0F}"; -#[allow(dead_code)] -pub const EMOJI_HINT: &str = "\u{1F4A1}"; const INDENT_PREFIX: &str = " "; @@ -37,7 +28,6 @@ pub struct PP { pub verbosity: Verbosity, pub emoji: bool, indent: usize, - seen: Arc>>, } impl PP { @@ -46,7 +36,6 @@ impl PP { verbosity: if quiet { Verbosity::Quiet } else { Verbosity::Verbose }, emoji, indent: 0, - seen: Arc::new(Mutex::new(HashSet::new())), } } @@ -63,7 +52,6 @@ impl PP { verbosity: self.verbosity, emoji: self.emoji, indent: self.indent + 1, - seen: Arc::clone(&self.seen), } } @@ -104,54 +92,12 @@ impl PP { pub fn errorf(&self, emoji: &str, msg: &str) { self.output_err(emoji, msg); } - - #[allow(dead_code)] - pub fn info_once(&self, key: &str, emoji: &str, msg: &str) { - if self.is_showing(Verbosity::Info) { - let mut seen = self.seen.lock().unwrap(); - if seen.insert(key.to_string()) { - self.output(emoji, msg); - } - } - } - - #[allow(dead_code)] - pub fn notice_once(&self, key: &str, emoji: &str, msg: &str) { - if self.is_showing(Verbosity::Notice) { - let mut seen = self.seen.lock().unwrap(); - if seen.insert(key.to_string()) { - self.output(emoji, msg); - } - } - } - - #[allow(dead_code)] - pub fn blank_line_if_verbose(&self) { - if self.is_showing(Verbosity::Verbose) { - println!(); - } - } -} - -#[allow(dead_code)] -pub fn english_join(items: &[String]) -> String { - match items.len() { - 0 => String::new(), - 1 => items[0].clone(), - 2 => format!("{} and {}", items[0], items[1]), - _ => { - let (last, rest) = items.split_last().unwrap(); - format!("{}, and {last}", rest.join(", ")) - } - } } #[cfg(test)] mod tests { use super::*; - // ---- PP::new with emoji flag ---- - #[test] fn new_with_emoji_true() { let pp = PP::new(true, false); @@ -164,8 +110,6 @@ mod tests { assert!(!pp.emoji); } - // ---- PP::new with quiet flag (verbosity levels) ---- - #[test] fn new_quiet_true_sets_verbosity_quiet() { let pp = PP::new(false, true); @@ -178,8 +122,6 @@ mod tests { assert_eq!(pp.verbosity, Verbosity::Verbose); } - // ---- PP::is_showing at different verbosity levels ---- - #[test] fn quiet_shows_only_quiet_level() { let pp = PP::new(false, true); @@ -218,8 +160,6 @@ mod tests { assert!(!pp.is_showing(Verbosity::Verbose)); } - // ---- PP::indent ---- - #[test] fn indent_increments_indent_level() { let pp = PP::new(true, false); @@ -238,26 +178,6 @@ mod tests { assert_eq!(child.emoji, pp.emoji); } - #[test] - fn indent_shares_seen_state() { - let pp = PP::new(false, false); - let child = pp.indent(); - - // Insert via parent's seen set - pp.seen.lock().unwrap().insert("key1".to_string()); - - // Child should observe the same entry - assert!(child.seen.lock().unwrap().contains("key1")); - - // Insert via child - child.seen.lock().unwrap().insert("key2".to_string()); - - // Parent should observe it too - assert!(pp.seen.lock().unwrap().contains("key2")); - } - - // ---- PP::infof, noticef, warningf, errorf - no panic and verbosity gating ---- - #[test] fn infof_does_not_panic_when_verbose() { let pp = PP::new(false, false); @@ -267,7 +187,6 @@ mod tests { #[test] fn infof_does_not_panic_when_quiet() { let pp = PP::new(false, true); - // Should simply not print, and not panic pp.infof("", "test info message"); } @@ -291,7 +210,6 @@ mod tests { #[test] fn warningf_does_not_panic_when_quiet() { - // warningf always outputs (no verbosity check), just verify no panic let pp = PP::new(false, true); pp.warningf("", "test warning"); } @@ -308,124 +226,6 @@ mod tests { pp.errorf("", "test error"); } - // ---- PP::info_once and notice_once ---- - - #[test] - fn info_once_suppresses_duplicates() { - let pp = PP::new(false, false); - // First call inserts the key - pp.info_once("dup_key", "", "first"); - // The key should now be in the seen set - assert!(pp.seen.lock().unwrap().contains("dup_key")); - - // Calling again with the same key should not insert again (set unchanged) - let size_before = pp.seen.lock().unwrap().len(); - pp.info_once("dup_key", "", "second"); - let size_after = pp.seen.lock().unwrap().len(); - assert_eq!(size_before, size_after); - } - - #[test] - fn info_once_allows_different_keys() { - let pp = PP::new(false, false); - pp.info_once("key_a", "", "msg a"); - pp.info_once("key_b", "", "msg b"); - let seen = pp.seen.lock().unwrap(); - assert!(seen.contains("key_a")); - assert!(seen.contains("key_b")); - assert_eq!(seen.len(), 2); - } - - #[test] - fn info_once_skipped_when_quiet() { - let pp = PP::new(false, true); - pp.info_once("quiet_key", "", "should not register"); - // Because verbosity is Quiet, info_once should not even insert the key - assert!(!pp.seen.lock().unwrap().contains("quiet_key")); - } - - #[test] - fn notice_once_suppresses_duplicates() { - let pp = PP::new(false, false); - pp.notice_once("notice_dup", "", "first"); - assert!(pp.seen.lock().unwrap().contains("notice_dup")); - - let size_before = pp.seen.lock().unwrap().len(); - pp.notice_once("notice_dup", "", "second"); - let size_after = pp.seen.lock().unwrap().len(); - assert_eq!(size_before, size_after); - } - - #[test] - fn notice_once_skipped_when_quiet() { - let pp = PP::new(false, true); - pp.notice_once("quiet_notice", "", "should not register"); - assert!(!pp.seen.lock().unwrap().contains("quiet_notice")); - } - - #[test] - fn info_once_shared_via_indent() { - let pp = PP::new(false, false); - let child = pp.indent(); - - // Mark a key via the parent - pp.info_once("shared_key", "", "parent"); - assert!(pp.seen.lock().unwrap().contains("shared_key")); - - // Child should see it as already present, so set size stays the same - let size_before = child.seen.lock().unwrap().len(); - child.info_once("shared_key", "", "child duplicate"); - let size_after = child.seen.lock().unwrap().len(); - assert_eq!(size_before, size_after); - - // Child can add a new key visible to parent - child.info_once("child_key", "", "child new"); - assert!(pp.seen.lock().unwrap().contains("child_key")); - } - - // ---- english_join ---- - - #[test] - fn english_join_empty() { - let items: Vec = vec![]; - assert_eq!(english_join(&items), ""); - } - - #[test] - fn english_join_single() { - let items = vec!["alpha".to_string()]; - assert_eq!(english_join(&items), "alpha"); - } - - #[test] - fn english_join_two() { - let items = vec!["alpha".to_string(), "beta".to_string()]; - assert_eq!(english_join(&items), "alpha and beta"); - } - - #[test] - fn english_join_three() { - let items = vec![ - "alpha".to_string(), - "beta".to_string(), - "gamma".to_string(), - ]; - assert_eq!(english_join(&items), "alpha, beta, and gamma"); - } - - #[test] - fn english_join_four() { - let items = vec![ - "a".to_string(), - "b".to_string(), - "c".to_string(), - "d".to_string(), - ]; - assert_eq!(english_join(&items), "a, b, c, and d"); - } - - // ---- default_pp ---- - #[test] fn default_pp_is_verbose_no_emoji() { let pp = PP::default_pp(); diff --git a/src/provider.rs b/src/provider.rs index 2c14ff6..ee7b891 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -26,10 +26,6 @@ impl IpType { } } - #[allow(dead_code)] - pub fn all() -> &'static [IpType] { - &[IpType::V4, IpType::V6] - } } /// All supported provider types @@ -879,7 +875,7 @@ mod tests { .mount(&server) .await; - let client = Client::new(); + let client = crate::test_client(); let ppfmt = PP::default_pp(); let url = format!("{}/cdn-cgi/trace", server.uri()); let timeout = Duration::from_secs(5); @@ -919,7 +915,7 @@ mod tests { // We can't override the hardcoded primary/fallback URLs, but we can test // the custom URL path: first with a failing URL, then a succeeding one. - let client = Client::new(); + let client = crate::test_client(); let ppfmt = PP::default_pp(); let timeout = Duration::from_secs(5); @@ -1012,7 +1008,7 @@ mod tests { .mount(&server) .await; - let client = Client::new(); + let client = crate::test_client(); let ppfmt = PP::default_pp(); let timeout = Duration::from_secs(5); @@ -1035,7 +1031,7 @@ mod tests { .mount(&server) .await; - let client = Client::new(); + let client = crate::test_client(); let ppfmt = PP::default_pp(); let timeout = Duration::from_secs(5); @@ -1056,7 +1052,7 @@ mod tests { .mount(&server) .await; - let client = Client::new(); + let client = crate::test_client(); let ppfmt = PP::default_pp(); let timeout = Duration::from_secs(5); let url = format!("{}/my-ip", server.uri()); @@ -1076,7 +1072,7 @@ mod tests { .mount(&server) .await; - let client = Client::new(); + let client = crate::test_client(); let ppfmt = PP::default_pp(); let timeout = Duration::from_secs(5); let url = format!("{}/my-ip", server.uri()); @@ -1140,7 +1136,7 @@ mod tests { .mount(&server) .await; - let client = Client::new(); + let client = crate::test_client(); let ppfmt = PP::default_pp(); let timeout = Duration::from_secs(5); let url = format!("{}/my-ip", server.uri()); @@ -1351,7 +1347,7 @@ mod tests { "5.6.7.8".parse().unwrap(), ], }; - let client = Client::new(); + let client = crate::test_client(); let ppfmt = PP::default_pp(); let timeout = Duration::from_secs(5); @@ -1369,7 +1365,7 @@ mod tests { "2001:db8::1".parse().unwrap(), ], }; - let client = Client::new(); + let client = crate::test_client(); let ppfmt = PP::default_pp(); let timeout = Duration::from_secs(5); @@ -1383,7 +1379,7 @@ mod tests { #[tokio::test] async fn test_none_detect_ips_returns_empty() { let provider = ProviderType::None; - let client = Client::new(); + let client = crate::test_client(); let ppfmt = PP::default_pp(); let timeout = Duration::from_secs(5); diff --git a/src/updater.rs b/src/updater.rs index cf64fd1..8c028fb 100644 --- a/src/updater.rs +++ b/src/updater.rs @@ -849,7 +849,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &crate::test_client()).await; assert!(ok); } @@ -902,12 +902,12 @@ mod tests { let mut noop_reported = HashSet::new(); // First call: noop_reported is empty, so "up to date" is reported and key is inserted - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut noop_reported, &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut noop_reported, &crate::test_client()).await; assert!(ok); assert!(noop_reported.contains("home.example.com:A"), "noop_reported should contain the domain key after first noop"); // Second call: noop_reported already has the key, so the message is suppressed - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut noop_reported, &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut noop_reported, &crate::test_client()).await; assert!(ok); assert_eq!(noop_reported.len(), 1, "noop_reported should still have exactly one entry"); } @@ -980,7 +980,7 @@ mod tests { noop_reported.insert("home.example.com:A".to_string()); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut noop_reported, &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut noop_reported, &crate::test_client()).await; assert!(ok); assert!(!noop_reported.contains("home.example.com:A"), "noop_reported should be cleared after an update"); } @@ -1026,7 +1026,7 @@ mod tests { // all_ok = true because no zone-level errors occurred (empty ips just noop or warn) let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &crate::test_client()).await; // Providers with None are not inserted in loop, so no IP detection warning is emitted, // no detected_ips entry is created, and set_ips is called with empty slice -> Noop. assert!(ok); @@ -1076,7 +1076,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &crate::test_client()).await; assert!(!ok, "Expected false when zone is not found"); } @@ -1126,7 +1126,7 @@ mod tests { // dry_run returns Updated from set_ips (it signals intent), all_ok should be true let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &crate::test_client()).await; assert!(ok); } @@ -1192,7 +1192,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &crate::test_client()).await; assert!(ok); } @@ -1246,7 +1246,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &crate::test_client()).await; assert!(ok); } @@ -1286,7 +1286,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &crate::test_client()).await; assert!(!ok, "Expected false when WAF list is not found"); } @@ -1371,7 +1371,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &crate::test_client()).await; assert!(ok); } @@ -1388,7 +1388,7 @@ mod tests { let ppfmt = pp(); let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &crate::test_client()).await; assert!(ok); } @@ -1773,7 +1773,7 @@ mod tests { // set_ips with empty ips and no existing records = Noop; all_ok = true let mut cf_cache = CachedCloudflareFilter::new(); - let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &Client::new()).await; + let ok = update_once(&config, &cf, ¬ifier, &heartbeat, &mut cf_cache, &ppfmt, &mut HashSet::new(), &crate::test_client()).await; assert!(ok); } // ------------------------------------------------------- @@ -1792,7 +1792,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: false, }; @@ -1824,7 +1824,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: false, }; @@ -1853,7 +1853,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: false, }; @@ -1875,7 +1875,7 @@ mod tests { #[tokio::test] async fn test_legacy_cf_api_unknown_method() { let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: "http://localhost".to_string(), dry_run: false, }; @@ -1905,7 +1905,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: false, }; @@ -1961,7 +1961,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: false, }; @@ -2017,7 +2017,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: false, }; @@ -2059,7 +2059,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: true, }; @@ -2110,7 +2110,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: false, }; @@ -2165,7 +2165,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: false, }; @@ -2214,7 +2214,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: false, }; @@ -2258,7 +2258,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: false, }; @@ -2290,7 +2290,7 @@ mod tests { .await; let ddns = LegacyDdnsClient { - client: Client::new(), + client: crate::test_client(), cf_api_base: server.uri(), dry_run: true, };