From 254f547719c0dbeb3fbdbdb6004ae3debac6038c Mon Sep 17 00:00:00 2001 From: dzu Date: Thu, 19 Feb 2026 10:04:36 +0000 Subject: [PATCH 01/10] initial common version --- .gitignore | 1 + Cargo.lock | 1086 ++++++++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 11 + src/lib.rs | 171 +++++++++ 4 files changed, 1269 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..167839a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1086 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anode-cache-common" +version = "0.1.0" +dependencies = [ + "anyhow", + "isahc", + "secrecy", + "serde_json", + "thiserror", +] + +[[package]] +name = "anyhow" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "castaway" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "curl" +version = "0.4.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79fc3b6dd0b87ba36e565715bf9a2ced221311db47bd18011676f24a6066edbc" +dependencies = [ + "curl-sys", + "libc", + "openssl-probe", + "openssl-sys", + "schannel", + "socket2", + "windows-sys 0.59.0", +] + +[[package]] +name = "curl-sys" +version = "0.4.85+curl-8.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0efa6142b5ecc05f6d3eaa39e6af4888b9d3939273fb592c92b7088a8cf3fdb" +dependencies = [ + "cc", + "libc", + "libnghttp2-sys", + "libz-sys", + "openssl-sys", + "pkg-config", + "vcpkg", + "windows-sys 0.59.0", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "event-listener" +version = "2.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" + +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "isahc" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" +dependencies = [ + "async-channel", + "castaway", + "crossbeam-utils", + "curl", + "curl-sys", + "encoding_rs", + "event-listener", + "futures-lite", + "http", + "log", + "mime", + "once_cell", + "polling", + "slab", + "sluice", + "tracing", + "tracing-futures", + "url", + "waker-fn", +] + +[[package]] +name = "itoa" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libnghttp2-sys" +version = "0.1.12+1.68.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d4c51112b381b39a072a6010bc5d3e5557996c5d0150abacd1d349b2636299" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "libz-sys" +version = "1.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-src" +version = "300.5.5+3.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" +dependencies = [ + "cc", +] + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "openssl-src", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys 0.48.0", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "sluice" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" +dependencies = [ + "async-channel", + "futures-core", + "futures-io", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "syn" +version = "2.0.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "waker-fn" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +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", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[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.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[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 = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..97c3582 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "anode-cache-common" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.101" +isahc = { version = "1.7.2", features = ["static-ssl", "static-curl"] } +serde_json = "1.0.149" +secrecy = "0.10.3" +thiserror = "2.0.18" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..270ae97 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,171 @@ +use std::{ffi::OsString, io::Read, path::PathBuf, str::FromStr, string::FromUtf8Error}; + +use anyhow::{Context, Result, bail}; +use isahc::{ + HttpClient, HttpClientBuilder, Request, + auth::{Authentication, Credentials}, + config::Configurable, + http::{Method, StatusCode, header}, +}; +use secrecy::{ExposeSecret, SecretString}; + +pub fn paths_parser(input: OsString) -> Result, FromUtf8Error> { + let input = String::from_utf8(input.into_encoded_bytes())?; + let mut paths = Vec::new(); + for path in input.split('\n').filter(|s| !s.is_empty()) { + paths.push(match PathBuf::from_str(path) { + Ok(ok) => ok, + }); + } + Ok(paths) +} + +#[derive(Debug)] +pub struct CacheKey(String); + +#[derive(Debug, thiserror::Error)] +pub enum CacheKeyError { + #[error(transparent)] + Utf8(#[from] FromUtf8Error), + #[error("Cache key is at most 512 characters long. {len} characters long key provided")] + Length { len: usize }, + #[error("Cache key cannot contain commas. Comma found at offset {offset}")] + Comma { offset: usize }, +} + +pub fn key_parser(input: OsString) -> Result { + let input = String::from_utf8(input.into_encoded_bytes())?; + let mut len = 0; + for (offset, ch) in input.char_indices() { + if ch == ',' { + return Err(CacheKeyError::Comma { offset }); + } + len += 1; + } + if len > 512 { + return Err(CacheKeyError::Length { len }); + } + Ok(CacheKey(input)) +} + +#[derive(Debug, thiserror::Error)] +pub enum ActionsTokenError { + #[error("Could not find action runtime token")] + NotPresent, + #[error("Action runtime token was not UTF-8 -encoded")] + NotUtf8, +} + +pub fn actions_token() -> Result { + let os_str = std::env::var_os("ACTIONS_RUNTIME_TOKEN").ok_or(ActionsTokenError::NotPresent)?; + let str = + String::from_utf8(os_str.into_encoded_bytes()).map_err(|_| ActionsTokenError::NotUtf8)?; + Ok(SecretString::new(str.into_boxed_str())) +} + +pub struct Client { + http_client: HttpClient, + base_url: String, +} + +#[derive(Debug, Clone, Copy)] +pub struct CacheId(u64); + +impl Client { + pub fn new(actions_token: SecretString, base_url: String) -> Result { + let http_client = HttpClientBuilder::new() + .authentication(Authentication::basic()) + .credentials(Credentials::new("Bearer", actions_token.expose_secret())) + .build() + .context("initializing the client")?; + Ok(Self { + http_client, + base_url, + }) + } + + pub fn save_cache(&self, key: &CacheKey, cache: Vec) -> Result<()> { + let cache_id = self.reserve(key, cache.len()).context("reserving cache")?; + + self.save(cache_id, cache).context("saving the cache")?; + + Ok(()) + } + + fn reserve(&self, key: &CacheKey, cache_size: usize) -> Result { + let uri = format!("{}_apis/artifactcache/caches", self.base_url); + let request = Request::builder() + .uri(uri) + .method(Method::POST) + .body( + serde_json::json! {{ + "key": &key.0, + "version": "0", + "cacheSize": cache_size, + }} + .to_string(), + ) + .context("building the request")?; + let mut response = self + .http_client + .send(request) + .context("sending the request")?; + let status = response.status(); + let mut body = Vec::with_capacity(100); + response + .body_mut() + .read_to_end(&mut body) + .context("reading response body")?; + if status != StatusCode::OK { + bail!( + "Failed reserving a cache (status {status}): {:?}", + String::from_utf8_lossy(&body) + ); + } + + let body_value = + serde_json::from_slice::(&body).context("parsing response body")?; + + println!("{body_value:#?}"); + + let cache_id = body_value + .get("cacheId") + .context("response.cacheId")? + .as_u64() + .context("cacheId as u64")?; + + Ok(CacheId(cache_id)) + } + + fn save(&self, id: CacheId, archive: Vec) -> Result<()> { + let uri = format!("{}_apis/artifactcache/caches/{}", self.base_url, id.0); + let request = Request::builder() + .uri(uri) + .method(Method::PATCH) + .header(header::CONTENT_TYPE, "application/octet-stream") + .header( + header::CONTENT_RANGE, + format!("bytes {}-{}/*", 0, archive.len()), + ) + .body(archive) + .context("building the request")?; + let mut response = self + .http_client + .send(request) + .context("sending the request")?; + let status = response.status(); + let mut body = Vec::with_capacity(100); + response + .body_mut() + .read_to_end(&mut body) + .context("reading response body")?; + if status != StatusCode::OK { + bail!( + "Failed saving a cache (status {status}): {:?}", + String::from_utf8_lossy(&body) + ); + } + + Ok(()) + } +} -- 2.54.0 From 9de30e2308eedaf79384260c8c915573cad011b4 Mon Sep 17 00:00:00 2001 From: dzu Date: Thu, 19 Feb 2026 10:09:42 +0000 Subject: [PATCH 02/10] move flexible `Client` input types --- src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 270ae97..602b61e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,7 +7,7 @@ use isahc::{ config::Configurable, http::{Method, StatusCode, header}, }; -use secrecy::{ExposeSecret, SecretString}; +use secrecy::SecretString; pub fn paths_parser(input: OsString) -> Result, FromUtf8Error> { let input = String::from_utf8(input.into_encoded_bytes())?; @@ -72,15 +72,15 @@ pub struct Client { pub struct CacheId(u64); impl Client { - pub fn new(actions_token: SecretString, base_url: String) -> Result { + pub fn new(actions_token: impl AsRef, base_url: impl Into) -> Result { let http_client = HttpClientBuilder::new() .authentication(Authentication::basic()) - .credentials(Credentials::new("Bearer", actions_token.expose_secret())) + .credentials(Credentials::new("Bearer", actions_token.as_ref())) .build() .context("initializing the client")?; Ok(Self { http_client, - base_url, + base_url: base_url.into(), }) } -- 2.54.0 From d2e683de2828b1c7aa8e03cc80de79ae693b1829 Mon Sep 17 00:00:00 2001 From: dzu Date: Fri, 20 Feb 2026 08:01:37 +0000 Subject: [PATCH 03/10] add: cache downloading --- src/lib.rs | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 602b61e..076e75e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -168,4 +168,83 @@ impl Client { Ok(()) } + + pub fn load_cache(&self, key: &CacheKey) -> Result>> { + let Some(cache_location) = self.get_cache_entry(key).context("getting cache entry")? else { + // cache miss + return Ok(None); + }; + + let cache = self + .download_cache(cache_location) + .context("downloading the cache")?; + + Ok(Some(cache)) + } + + fn get_cache_entry(&self, key: &CacheKey) -> Result> { + let uri = format!("{}_apis/artifactcache/cache/{}", self.base_url, key.0); + let request = Request::builder() + .uri(uri) + .method(Method::GET) + .body(()) + .context("building the request")?; + let mut response = self + .http_client + .send(request) + .context("sending the request")?; + let status = response.status(); + if status == StatusCode::NO_CONTENT { + // cache miss + return Ok(None); + } + let mut body = Vec::with_capacity(100); + response + .body_mut() + .read_to_end(&mut body) + .context("reading response body")?; + if status != StatusCode::OK { + bail!( + "Failed reading a cache entry (status {status}): {:?}", + str::from_utf8(&body) + ); + } + + let body_val = + serde_json::from_slice::(&body).context("parsing response body")?; + + let location = body_val + .get("archiveLocation") + .context("not cache location field")? + .as_str() + .context("archiveLocation is not a string")?; + + Ok(Some(location.to_string())) + } + + fn download_cache(&self, location: String) -> Result> { + let request = Request::builder() + .uri(location) + .method(Method::GET) + .body(()) + .context("building the request")?; + let mut response = self + .http_client + .send(request) + .context("sending the request")?; + let status = response.status(); + let mut body = Vec::with_capacity(100); + response + .body_mut() + .read_to_end(&mut body) + .context("reading response body")?; + if status == StatusCode::OK { + bail!( + "Failed downloading the cache (status {status}): {:?}", + str::from_utf8(&body) + ); + } + + Ok(body.into_boxed_slice()) + } } -- 2.54.0 From a50d6cb49c9357d7d138ee1126c2e83d44646333 Mon Sep 17 00:00:00 2001 From: dzu Date: Fri, 20 Feb 2026 08:44:26 +0000 Subject: [PATCH 04/10] fix: getCacheEntry url parameters --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 076e75e..bcd0588 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -183,7 +183,10 @@ impl Client { } fn get_cache_entry(&self, key: &CacheKey) -> Result> { - let uri = format!("{}_apis/artifactcache/cache/{}", self.base_url, key.0); + let uri = format!( + "{}_apis/artifactcache/cache/keys={}&version=0", + self.base_url, key.0 + ); let request = Request::builder() .uri(uri) .method(Method::GET) -- 2.54.0 From a3c38e595c11e9c44860a3fc9aade67eeda528f8 Mon Sep 17 00:00:00 2001 From: dzu Date: Fri, 20 Feb 2026 08:49:14 +0000 Subject: [PATCH 05/10] add: uri logging --- src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index bcd0588..3297fb5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -187,6 +187,7 @@ impl Client { "{}_apis/artifactcache/cache/keys={}&version=0", self.base_url, key.0 ); + println!("cache entry uri: {uri}"); let request = Request::builder() .uri(uri) .method(Method::GET) @@ -226,6 +227,7 @@ impl Client { } fn download_cache(&self, location: String) -> Result> { + println!("cache data uri: {location}"); let request = Request::builder() .uri(location) .method(Method::GET) -- 2.54.0 From 8be075e61ba11ada87b282fccdb869913b2eecaf Mon Sep 17 00:00:00 2001 From: dzu Date: Fri, 20 Feb 2026 09:12:28 +0000 Subject: [PATCH 06/10] fix: use correct Uri syntax --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 3297fb5..d8972a0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -184,7 +184,7 @@ impl Client { fn get_cache_entry(&self, key: &CacheKey) -> Result> { let uri = format!( - "{}_apis/artifactcache/cache/keys={}&version=0", + "{}_apis/artifactcache/cache?keys={}&version=0", self.base_url, key.0 ); println!("cache entry uri: {uri}"); -- 2.54.0 From c685a1db3e5c4bc821146d9cd3b5212f7a0922e3 Mon Sep 17 00:00:00 2001 From: dzu Date: Fri, 20 Feb 2026 09:35:58 +0000 Subject: [PATCH 07/10] fix: commit cache --- src/lib.rs | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d8972a0..0a96623 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -85,10 +85,14 @@ impl Client { } pub fn save_cache(&self, key: &CacheKey, cache: Vec) -> Result<()> { - let cache_id = self.reserve(key, cache.len()).context("reserving cache")?; + let cache_len = cache.len(); + let cache_id = self.reserve(key, cache_len).context("reserving cache")?; self.save(cache_id, cache).context("saving the cache")?; + self.commit_cache(cache_id, cache_len) + .context("committing the cache")?; + Ok(()) } @@ -169,6 +173,38 @@ impl Client { Ok(()) } + fn commit_cache(&self, id: CacheId, cache_size: usize) -> Result<()> { + let uri = format!("{}_apis/artifactcache/caches/{}", self.base_url, id.0); + let request = Request::builder() + .uri(uri) + .method(Method::POST) + .body( + serde_json::json! {{ + "size": cache_size, + }} + .to_string(), + ) + .context("building the request")?; + let mut response = self + .http_client + .send(request) + .context("sending the request")?; + let status = response.status(); + if status != StatusCode::OK { + let mut body = Vec::with_capacity(100); + response + .body_mut() + .read_to_end(&mut body) + .context("reading response body")?; + bail!( + "Failed reserving a cache (status {status}): {:?}", + String::from_utf8_lossy(&body) + ); + } + + Ok(()) + } + pub fn load_cache(&self, key: &CacheKey) -> Result>> { let Some(cache_location) = self.get_cache_entry(key).context("getting cache entry")? else { // cache miss -- 2.54.0 From 03a207d32a9b72647a5a1015b3cf63adb6d24b36 Mon Sep 17 00:00:00 2001 From: dzu Date: Fri, 20 Feb 2026 09:44:36 +0000 Subject: [PATCH 08/10] fix: 200OK is not an error --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 0a96623..dfea874 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -279,7 +279,7 @@ impl Client { .body_mut() .read_to_end(&mut body) .context("reading response body")?; - if status == StatusCode::OK { + if status != StatusCode::OK { bail!( "Failed downloading the cache (status {status}): {:?}", str::from_utf8(&body) -- 2.54.0 From 479d6fe4149b1cc4c81486fdf0eb6f6a56fc9282 Mon Sep 17 00:00:00 2001 From: dzu Date: Fri, 20 Feb 2026 13:43:31 +0000 Subject: [PATCH 09/10] ench: use `tracing` for logging --- Cargo.lock | 67 ++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ src/lib.rs | 89 +++++++++++++++++++++++++++++++++++++----------------- 3 files changed, 130 insertions(+), 28 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 167839a..d2ccad6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,8 @@ dependencies = [ "secrecy", "serde_json", "thiserror", + "tracing", + "tracing-subscriber", ] [[package]] @@ -354,6 +356,12 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "libc" version = "0.2.182" @@ -406,6 +414,15 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -587,6 +604,15 @@ dependencies = [ "zmij", ] +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + [[package]] name = "shlex" version = "1.3.0" @@ -674,6 +700,15 @@ dependencies = [ "syn", ] +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + [[package]] name = "tinystr" version = "0.8.2" @@ -714,6 +749,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", + "valuable", ] [[package]] @@ -726,6 +762,31 @@ dependencies = [ "tracing", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + [[package]] name = "unicode-ident" version = "1.0.24" @@ -750,6 +811,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 97c3582..18238b9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,5 @@ isahc = { version = "1.7.2", features = ["static-ssl", "static-curl"] } serde_json = "1.0.149" secrecy = "0.10.3" thiserror = "2.0.18" +tracing-subscriber = { version = "0.3.22" } +tracing = { version = "0.1.44", features = ["attributes"] } diff --git a/src/lib.rs b/src/lib.rs index dfea874..43d5ed5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ use std::{ffi::OsString, io::Read, path::PathBuf, str::FromStr, string::FromUtf8Error}; -use anyhow::{Context, Result, bail}; +use anyhow::bail; +use anyhow::{Context, Result}; use isahc::{ HttpClient, HttpClientBuilder, Request, auth::{Authentication, Credentials}, @@ -8,7 +9,20 @@ use isahc::{ http::{Method, StatusCode, header}, }; use secrecy::SecretString; +use tracing::{Level, debug, error}; +use tracing_subscriber::fmt::format::FmtSpan; +pub fn setup_logger(debug: bool) { + tracing_subscriber::fmt() + .with_ansi(true) + .with_level(true) + .with_max_level(if debug { Level::DEBUG } else { Level::INFO }) + .with_span_events(FmtSpan::CLOSE) + .init(); + debug!("Logger set up"); +} + +#[tracing::instrument(level = "debug")] pub fn paths_parser(input: OsString) -> Result, FromUtf8Error> { let input = String::from_utf8(input.into_encoded_bytes())?; let mut paths = Vec::new(); @@ -17,6 +31,7 @@ pub fn paths_parser(input: OsString) -> Result, FromUtf8Error> { Ok(ok) => ok, }); } + debug!(?paths, "Parsed paths"); Ok(paths) } @@ -33,6 +48,7 @@ pub enum CacheKeyError { Comma { offset: usize }, } +#[tracing::instrument(level = "debug", fields(return))] pub fn key_parser(input: OsString) -> Result { let input = String::from_utf8(input.into_encoded_bytes())?; let mut len = 0; @@ -56,6 +72,7 @@ pub enum ActionsTokenError { NotUtf8, } +#[tracing::instrument(level = "debug")] pub fn actions_token() -> Result { let os_str = std::env::var_os("ACTIONS_RUNTIME_TOKEN").ok_or(ActionsTokenError::NotPresent)?; let str = @@ -63,6 +80,7 @@ pub fn actions_token() -> Result { Ok(SecretString::new(str.into_boxed_str())) } +#[derive(Debug)] pub struct Client { http_client: HttpClient, base_url: String, @@ -72,18 +90,22 @@ pub struct Client { pub struct CacheId(u64); impl Client { + #[tracing::instrument(level = "debug", fields(base_url = tracing::field::Empty), skip_all)] pub fn new(actions_token: impl AsRef, base_url: impl Into) -> Result { let http_client = HttpClientBuilder::new() .authentication(Authentication::basic()) .credentials(Credentials::new("Bearer", actions_token.as_ref())) .build() .context("initializing the client")?; + let base_url = base_url.into(); + tracing::span::Span::current().record("base_url", &base_url); Ok(Self { http_client, - base_url: base_url.into(), + base_url, }) } + #[tracing::instrument(level = "info", skip(cache))] pub fn save_cache(&self, key: &CacheKey, cache: Vec) -> Result<()> { let cache_len = cache.len(); let cache_id = self.reserve(key, cache_len).context("reserving cache")?; @@ -96,8 +118,10 @@ impl Client { Ok(()) } + #[tracing::instrument(level = "debug", fields(return))] fn reserve(&self, key: &CacheKey, cache_size: usize) -> Result { let uri = format!("{}_apis/artifactcache/caches", self.base_url); + debug!(uri); let request = Request::builder() .uri(uri) .method(Method::POST) @@ -114,6 +138,7 @@ impl Client { .http_client .send(request) .context("sending the request")?; + debug!(?response); let status = response.status(); let mut body = Vec::with_capacity(100); response @@ -121,16 +146,13 @@ impl Client { .read_to_end(&mut body) .context("reading response body")?; if status != StatusCode::OK { - bail!( - "Failed reserving a cache (status {status}): {:?}", - String::from_utf8_lossy(&body) - ); + error!(?status, "failed sending a request"); + bail!("failed sending a request"); } let body_value = serde_json::from_slice::(&body).context("parsing response body")?; - - println!("{body_value:#?}"); + debug!(body = ?body_value); let cache_id = body_value .get("cacheId") @@ -141,8 +163,10 @@ impl Client { Ok(CacheId(cache_id)) } + #[tracing::instrument(level = "debug", skip(archive))] fn save(&self, id: CacheId, archive: Vec) -> Result<()> { let uri = format!("{}_apis/artifactcache/caches/{}", self.base_url, id.0); + debug!(uri); let request = Request::builder() .uri(uri) .method(Method::PATCH) @@ -153,28 +177,33 @@ impl Client { ) .body(archive) .context("building the request")?; + let mut response = self .http_client .send(request) .context("sending the request")?; + debug!(?response); + let status = response.status(); let mut body = Vec::with_capacity(100); response .body_mut() .read_to_end(&mut body) .context("reading response body")?; + if status != StatusCode::OK { - bail!( - "Failed saving a cache (status {status}): {:?}", - String::from_utf8_lossy(&body) - ); + error!(?status, body = ?String::from_utf8_lossy(&body), "failed sending a request"); + bail!("failed sending a request"); } Ok(()) } + #[tracing::instrument(level = "debug")] fn commit_cache(&self, id: CacheId, cache_size: usize) -> Result<()> { let uri = format!("{}_apis/artifactcache/caches/{}", self.base_url, id.0); + debug!(uri); + let request = Request::builder() .uri(uri) .method(Method::POST) @@ -189,6 +218,8 @@ impl Client { .http_client .send(request) .context("sending the request")?; + debug!(?response); + let status = response.status(); if status != StatusCode::OK { let mut body = Vec::with_capacity(100); @@ -196,15 +227,14 @@ impl Client { .body_mut() .read_to_end(&mut body) .context("reading response body")?; - bail!( - "Failed reserving a cache (status {status}): {:?}", - String::from_utf8_lossy(&body) - ); + error!(?status, body = ?String::from_utf8_lossy(&body), "failed sending a request"); + bail!("failed sending a request"); } Ok(()) } + #[tracing::instrument(level = "info")] pub fn load_cache(&self, key: &CacheKey) -> Result>> { let Some(cache_location) = self.get_cache_entry(key).context("getting cache entry")? else { // cache miss @@ -218,12 +248,14 @@ impl Client { Ok(Some(cache)) } + #[tracing::instrument(level = "debug", fields(return))] fn get_cache_entry(&self, key: &CacheKey) -> Result> { let uri = format!( "{}_apis/artifactcache/cache?keys={}&version=0", self.base_url, key.0 ); - println!("cache entry uri: {uri}"); + debug!(uri); + let request = Request::builder() .uri(uri) .method(Method::GET) @@ -233,6 +265,8 @@ impl Client { .http_client .send(request) .context("sending the request")?; + debug!(?response); + let status = response.status(); if status == StatusCode::NO_CONTENT { // cache miss @@ -244,16 +278,15 @@ impl Client { .read_to_end(&mut body) .context("reading response body")?; if status != StatusCode::OK { - bail!( - "Failed reading a cache entry (status {status}): {:?}", - str::from_utf8(&body) - ); + error!(?status, body = ?String::from_utf8_lossy(&body), "failed sending a request"); + bail!("failed sending a request"); } - let body_val = + let body_value = serde_json::from_slice::(&body).context("parsing response body")?; + debug!(body = ?body_value); - let location = body_val + let location = body_value .get("archiveLocation") .context("not cache location field")? .as_str() @@ -262,8 +295,8 @@ impl Client { Ok(Some(location.to_string())) } + #[tracing::instrument(level = "debug")] fn download_cache(&self, location: String) -> Result> { - println!("cache data uri: {location}"); let request = Request::builder() .uri(location) .method(Method::GET) @@ -273,6 +306,8 @@ impl Client { .http_client .send(request) .context("sending the request")?; + debug!(?response); + let status = response.status(); let mut body = Vec::with_capacity(100); response @@ -280,10 +315,8 @@ impl Client { .read_to_end(&mut body) .context("reading response body")?; if status != StatusCode::OK { - bail!( - "Failed downloading the cache (status {status}): {:?}", - str::from_utf8(&body) - ); + error!(?status, body = ?String::from_utf8_lossy(&body), "failed sending a request"); + bail!("failed sending a request"); } Ok(body.into_boxed_slice()) -- 2.54.0 From 9940c26771b2528834c2ade121fdf53690d9733f Mon Sep 17 00:00:00 2001 From: dzu Date: Fri, 20 Feb 2026 15:10:04 +0000 Subject: [PATCH 10/10] upd: `anyhow` and `syn` --- Cargo.lock | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d2ccad6..14f33a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,9 +17,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.101" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "async-channel" @@ -660,9 +660,9 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "syn" -version = "2.0.116" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", -- 2.54.0