diff options
33 files changed, 4456 insertions, 0 deletions
@@ -0,0 +1 @@ +use flake diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2af821e --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.pre-commit-config.yaml +/result +/target/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..5c31fb2 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1330 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[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 = "anstream" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.60.2", +] + +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + +[[package]] +name = "apple_silicon" +version = "0.1.0" +dependencies = [ + "thiserror 2.0.16", +] + +[[package]] +name = "asn1-rs" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5493c3bedbacf7fd7382c6346bbd66687d12bbaad3a89a2d2c303ee6cf20b048" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "965c2d33e53cb6b267e148a4cb0760bc01f4904c1cd4bb4002a085bb016d1490" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c953fe1ba023e6b7730c0d4b031d06f267f23a46167dcbd40316644b10a17ba" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbfd150b5dbdb988bcc8fb1fe787eb6b7ee6180ca24da683b61ea5405f3d43ff" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "bindgen" +version = "0.69.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "2.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" + +[[package]] +name = "cc" +version = "1.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" + +[[package]] +name = "chrono" +version = "0.4.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-link", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" + +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "data-encoding" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] + +[[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 = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "errno" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.3+wasi-0.2.4", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "home" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +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 = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.3", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.175" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" + +[[package]] +name = "libloading" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +dependencies = [ + "cfg-if", + "windows-targets 0.53.3", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "oid-registry" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d8034d9489cdaf79228eb9f6a3b8d7bb32ba00d6645ebd48eef4077ceb5bd9" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +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 = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl 2.0.16", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasi" +version = "0.14.3+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51ae83037bdd272a9e28ce236db8c07016dd0d50c27038b3f407533c030c95" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "webpki-roots" +version = "0.26.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" +dependencies = [ + "webpki-roots 1.0.2", +] + +[[package]] +name = "webpki-roots" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.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.3", +] + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + +[[package]] +name = "wit-bindgen" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "052283831dbae3d879dc7f51f3d92703a316ca49f91540417d38591826127814" + +[[package]] +name = "x509-info" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "rustls", + "rustls-native-certs", + "webpki-roots 0.26.11", + "x509-parser", +] + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror 1.0.69", + "time", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c1ce08a --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["src/apple_silicon/", "src/x509-info/"] +resolver = "3" diff --git a/README.org b/README.org new file mode 100644 index 0000000..19764b6 --- /dev/null +++ b/README.org @@ -0,0 +1 @@ +My experimental monorepo for Go tools and packages. diff --git a/cmd/git-leaderboard/main.go b/cmd/git-leaderboard/main.go new file mode 100644 index 0000000..7c846a5 --- /dev/null +++ b/cmd/git-leaderboard/main.go @@ -0,0 +1,128 @@ +package main + +import ( + "flag" + "fmt" + "os" + "os/exec" + "sort" + "strings" +) + +type Author struct { + Name string + Count int +} + +func main() { + lineFlag := flag.Bool("line", false, "Count lines changed instead of commits") + flag.Parse() + + if !isGitRepository() { + fmt.Println("Error: Not in a git repository") + os.Exit(1) + } + + authors, err := getAuthors(*lineFlag) + if err != nil { + fmt.Printf("Error getting authors: %v\n", err) + os.Exit(1) + } + + if len(authors) == 0 { + fmt.Println("No authors found. The repository might be empty or have no commits.") + os.Exit(0) + } + + sort.Slice(authors, func(i, j int) bool { + return authors[i].Count > authors[j].Count + }) + + maxCount := authors[0].Count + + fmt.Printf("%-30s %-10s %s\n", "Author", "Count", "Contribution") + fmt.Println(strings.Repeat("-", 80)) + + for _, author := range authors { + bar := generateBar(author.Count, maxCount) + fmt.Printf("%-30s %-10d %s\n", author.Name, author.Count, bar) + } +} + +func isGitRepository() bool { + cmd := exec.Command("git", "rev-parse", "--is-inside-work-tree") + err := cmd.Run() + return err == nil +} + +func getAuthors(countLines bool) ([]Author, error) { + var cmd *exec.Cmd + + if countLines { + cmd = exec.Command("git", "log", "--pretty=format:%aN", "--numstat") + } else { + cmd = exec.Command("git", "shortlog", "-sn", "--no-merges", "HEAD") + } + + output, err := cmd.Output() + if err != nil { + if exitErr, ok := err.(*exec.ExitError); ok { + return nil, fmt.Errorf("git command failed: %s. Error output: %s", err, exitErr.Stderr) + } + return nil, fmt.Errorf("error executing git command: %v", err) + } + + lines := strings.Split(string(output), "\n") + authors := make(map[string]int) + + if countLines { + currentAuthor := "" + for _, line := range lines { + if line == "" { + currentAuthor = "" + continue + } + if currentAuthor == "" { + currentAuthor = line + continue + } else { + fields := strings.Fields(line) + if len(fields) >= 2 { + added, _ := parseInt(fields[0]) + deleted, _ := parseInt(fields[1]) + authors[currentAuthor] += added + deleted + } + } + } + } else { + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) >= 2 { + count, _ := parseInt(fields[0]) + name := strings.Join(fields[1:], " ") + authors[name] = count + } + } + } + + var result []Author + for name, count := range authors { + result = append(result, Author{Name: name, Count: count}) + } + return result, nil +} + +func parseInt(s string) (int, error) { + if s == "-" { + return 0, nil + } + var result int + _, err := fmt.Sscanf(s, "%d", &result) + return result, err +} + +func generateBar(count, maxCount int) string { + maxWidth := 38 + width := int(float64(count) / float64(maxCount) * float64(maxWidth)) + return strings.Repeat("▆", width) +} diff --git a/cmd/goget/main.go b/cmd/goget/main.go new file mode 100644 index 0000000..3f17448 --- /dev/null +++ b/cmd/goget/main.go @@ -0,0 +1,165 @@ +package main + +import ( + "context" + "fmt" + "log" + "net/http" + "net/url" + "os" + "os/signal" + "slices" + "strings" + "syscall" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" + "golang.org/x/mod/semver" +) + +const ( + forgeDomain = "code.fcuny.net" + forgeUser = "fcuny" + goPkgDomain = "go.fcuny.net" +) + +var ( + goGetReqs = promauto.NewCounterVec(prometheus.CounterOpts{ + Name: "goget_requests_total", + Help: "go get requests processed, by repository name.", + }, []string{"name"}) + + modules = []string{"x"} +) + +func main() { + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() + + metricsMux := http.NewServeMux() + metricsMux.Handle("/metrics", promhttp.Handler()) + metricsServer := &http.Server{ + Addr: ":9091", Handler: metricsMux, + ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, + } + + go func() { + log.Println("Starting metrics server on :9091") + if err := metricsServer.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Printf("Metrics server error: %v", err) + } + }() + + s := &http.Server{ + Addr: ":8070", + Handler: handler(), + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + IdleTimeout: 1 * time.Minute, + } + + go func() { + log.Println("Starting main server on :8080") + if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed { + log.Printf("Main server error: %v", err) + } + }() + + <-ctx.Done() + log.Println("Shutdown signal received, starting graceful shutdown...") + + shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second) + defer shutdownCancel() + + var shutdownErr error + + log.Println("Shutting down main server...") + if err := s.Shutdown(shutdownCtx); err != nil { + log.Printf("Main server shutdown error: %v", err) + shutdownErr = err + } + + log.Println("Shutting down metrics server...") + if err := metricsServer.Shutdown(shutdownCtx); err != nil { + log.Printf("Metrics server shutdown error: %v", err) + if shutdownErr == nil { + shutdownErr = err + } + } + + if shutdownErr != nil { + log.Printf("Shutdown completed with errors") + os.Exit(1) + } + + log.Println("Shutdown completed successfully") +} + +func handler() http.Handler { + mux := http.NewServeMux() + + mux.Handle(goPkgDomain+"/{name}", siteHandler(modules)) + mux.Handle(goPkgDomain+"/{name}/", siteHandler(modules)) + + goGetMux := http.NewServeMux() + for _, name := range modules { + module := goPkgDomain + "/" + name + goGetMux.Handle( + module+"/", + goImportHandler(module, "https://"+forgeDomain+"/"+forgeUser+"/"+name), + ) + } + + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodGet { + status := http.StatusMethodNotAllowed + http.Error(rw, http.StatusText(status), status) + return + } + + if r.URL.Query().Get("go-get") == "1" { + goGetMux.ServeHTTP(rw, r) + return + } + mux.ServeHTTP(rw, r) + }) +} + +func goImportHandler(module, repo string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + goGetReqs.WithLabelValues(module).Inc() + w.Header().Set("Content-Type", "text/html; charset=UTF-8") + if _, err := fmt.Fprintf(w, `<head><meta name="go-import" content="%s git %s">`, module, repo); err != nil { + log.Printf("Error writing response: %v", err) + } + }) +} + +func siteHandler(names []string) http.Handler { + return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { + name := r.PathValue("name") + name, version, hasV := strings.Cut(name, "@") + if !hasV { + name, _, _ = strings.Cut(name, ".") + } + if hasV && !semver.IsValid(version) || !slices.Contains(names, name) { + http.NotFound(rw, r) + return + } + u := &url.URL{ + Scheme: "https", + Host: forgeDomain, + Path: forgeUser + r.URL.Path, + } + if !hasV { + path, symbol, hasSymbol := strings.Cut(r.URL.Path, ".") + if hasSymbol { + u.Path = forgeUser + "/" + path + u.Fragment = symbol + } + } + http.Redirect(rw, r, u.String(), http.StatusFound) + }) +} diff --git a/cmd/pviz/main.go b/cmd/pviz/main.go new file mode 100644 index 0000000..89bd78d --- /dev/null +++ b/cmd/pviz/main.go @@ -0,0 +1,266 @@ +package main + +import ( + "fmt" + "os" + "strconv" + "text/tabwriter" + + "golang.org/x/text/cases" + "golang.org/x/text/language" +) + +type Period string + +const ( + PeriodDay Period = "day" + PeriodWeek Period = "week" + PeriodMonth Period = "month" + PeriodQuarter Period = "quarter" + PeriodYear Period = "year" +) + +var periodMinutes = map[Period]float64{ + PeriodDay: 24 * 60, + PeriodWeek: 7 * 24 * 60, + PeriodMonth: 30 * 24 * 60, + PeriodQuarter: 90 * 24 * 60, + PeriodYear: 365 * 24 * 60, +} + +var periodOrder = []Period{PeriodDay, PeriodWeek, PeriodMonth, PeriodQuarter, PeriodYear} + +type TimeUnit struct { + Minutes float64 + Formatted string +} + +type AvailabilityCalculator struct { + availability float64 +} + +func NewAvailabilityCalculator(availabilityPercentage float64) (*AvailabilityCalculator, error) { + if availabilityPercentage < 0 || availabilityPercentage > 100 { + return nil, fmt.Errorf("availability must be between 0 and 100") + } + return &AvailabilityCalculator{ + availability: availabilityPercentage / 100.0, + }, nil +} + +func (ac *AvailabilityCalculator) CalculateDowntime(period Period) TimeUnit { + totalMinutes := periodMinutes[period] + downtimeMinutes := totalMinutes * (1 - ac.availability) + return TimeUnit{ + Minutes: downtimeMinutes, + Formatted: formatDuration(downtimeMinutes), + } +} + +func (ac *AvailabilityCalculator) CalculateAllPeriods() map[Period]TimeUnit { + result := make(map[Period]TimeUnit) + for _, period := range periodOrder { + result[period] = ac.CalculateDowntime(period) + } + return result +} + +func formatDuration(minutes float64) string { + if minutes < 1 { + return fmt.Sprintf("%.1f seconds", minutes*60) + } + + hours := int(minutes / 60) + remainingMinutes := minutes - float64(hours*60) + + if hours == 0 { + return fmt.Sprintf("%.1f minutes", remainingMinutes) + } else if remainingMinutes == 0 { + return fmt.Sprintf("%d hours", hours) + } else { + return fmt.Sprintf("%d hours %.1f minutes", hours, remainingMinutes) + } +} + +func printTable(headers []string, rows [][]string) { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0) + + for i, header := range headers { + if i > 0 { + _, _ = fmt.Fprint(w, "\t") + } + _, _ = fmt.Fprint(w, header) + } + _, _ = fmt.Fprintln(w) + + for _, row := range rows { + for i, cell := range row { + if i > 0 { + _, _ = fmt.Fprint(w, "\t") + } + _, _ = fmt.Fprint(w, cell) + } + _, _ = fmt.Fprintln(w) + } + + _ = w.Flush() +} + +func analyzeSingle(availability float64, focusPeriod string) error { + calc, err := NewAvailabilityCalculator(availability) + if err != nil { + return err + } + + var periods []Period + if focusPeriod != "" { + periods = []Period{Period(focusPeriod)} + } else { + periods = periodOrder + } + + headers := []string{"Time Period", "Maximum Downtime"} + var rows [][]string + + caser := cases.Title(language.English) + for _, period := range periods { + downtime := calc.CalculateDowntime(period) + rows = append(rows, []string{ + caser.String(string(period)), + downtime.Formatted, + }) + } + + printTable(headers, rows) + return nil +} + +func compareAvailabilities(availability1, availability2 float64) error { + if availability1 == availability2 { + return fmt.Errorf("cannot compare identical availability values") + } + + calc1, err := NewAvailabilityCalculator(availability1) + if err != nil { + return err + } + + calc2, err := NewAvailabilityCalculator(availability2) + if err != nil { + return err + } + + improvement := ((100 - availability1) - (100 - availability2)) / (100 - availability1) * 100 + fmt.Printf("Downtime reduction: %.1f%%\n\n", improvement) + + headers := []string{ + "Time Period", + fmt.Sprintf("%.2f%%", availability1), + fmt.Sprintf("%.2f%%", availability2), + } + var rows [][]string + + caser := cases.Title(language.English) + for _, period := range periodOrder { + downtime1 := calc1.CalculateDowntime(period) + downtime2 := calc2.CalculateDowntime(period) + rows = append(rows, []string{ + caser.String(string(period)), + downtime1.Formatted, + downtime2.Formatted, + }) + } + + printTable(headers, rows) + return nil +} + +func printUsage() { + fmt.Println("Usage:") + fmt.Println(" pviz analyze <availability> - Analyze a single availability target") + fmt.Println(" pviz analyze <availability> --focus <period> - Focus on a specific time period") + fmt.Println(" pviz compare <availability1> <availability2> - Compare two availability targets") + fmt.Println() + fmt.Println("Examples:") + fmt.Println(" pviz analyze 99.99") + fmt.Println(" pviz analyze 99.99 --focus quarter") + fmt.Println(" pviz compare 99.9 99.95") + fmt.Println() + fmt.Println("Valid periods: day, week, month, quarter, year") +} + +func main() { + if len(os.Args) < 2 { + printUsage() + os.Exit(1) + } + + command := os.Args[1] + + switch command { + case "analyze": + if len(os.Args) < 3 { + fmt.Fprintf(os.Stderr, "Error: analyze command requires availability argument\n") + os.Exit(1) + } + + availability, err := strconv.ParseFloat(os.Args[2], 64) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: invalid availability value: %v\n", err) + os.Exit(1) + } + + var focusPeriod string + if len(os.Args) >= 5 && os.Args[3] == "--focus" { + focusPeriod = os.Args[4] + validPeriod := false + for _, p := range periodOrder { + if string(p) == focusPeriod { + validPeriod = true + break + } + } + if !validPeriod { + fmt.Fprintf( + os.Stderr, + "Error: invalid period '%s'. Valid periods: day, week, month, quarter, year\n", + focusPeriod, + ) + os.Exit(1) + } + } + + if err := analyzeSingle(availability, focusPeriod); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + case "compare": + if len(os.Args) < 4 { + fmt.Fprintf(os.Stderr, "Error: compare command requires two availability arguments\n") + os.Exit(1) + } + + availability1, err := strconv.ParseFloat(os.Args[2], 64) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: invalid first availability value: %v\n", err) + os.Exit(1) + } + + availability2, err := strconv.ParseFloat(os.Args[3], 64) + if err != nil { + fmt.Fprintf(os.Stderr, "Error: invalid second availability value: %v\n", err) + os.Exit(1) + } + + if err := compareAvailabilities(availability1, availability2); err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + default: + fmt.Fprintf(os.Stderr, "Error: unknown command '%s'\n", command) + printUsage() + os.Exit(1) + } +} diff --git a/cmd/seq-stat/main.go b/cmd/seq-stat/main.go new file mode 100644 index 0000000..75bb0a3 --- /dev/null +++ b/cmd/seq-stat/main.go @@ -0,0 +1,115 @@ +package main + +import ( + "bufio" + "fmt" + "os" + "strconv" + "strings" +) + +var ticks = []rune{'▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'} + +func histogram(sequence []float64) []rune { + if len(sequence) == 0 { + return []rune{} + } + + minVal := sequence[0] + maxVal := sequence[0] + + for _, v := range sequence { + if v < minVal { + minVal = v + } + if v > maxVal { + maxVal = v + } + } + + scale := (maxVal - minVal) * 256 / float64(len(ticks)-1) + if scale < 1 { + scale = 1 + } + + result := make([]rune, len(sequence)) + for i, v := range sequence { + index := int(((v - minVal) * 256) / scale) + if index >= len(ticks) { + index = len(ticks) - 1 + } + result[i] = ticks[index] + } + + return result +} + +func parseNumbers(input string) ([]float64, error) { + fields := strings.Fields(input) + numbers := make([]float64, 0, len(fields)) + + for _, field := range fields { + num, err := strconv.ParseFloat(field, 64) + if err != nil { + return nil, fmt.Errorf("invalid number: %s", field) + } + numbers = append(numbers, num) + } + + return numbers, nil +} + +func readFromStdin() ([]float64, error) { + var numbers []float64 + scanner := bufio.NewScanner(os.Stdin) + + for scanner.Scan() { + line := strings.TrimSpace(scanner.Text()) + if line == "" { + continue + } + + lineNumbers, err := parseNumbers(line) + if err != nil { + return nil, err + } + numbers = append(numbers, lineNumbers...) + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error reading from stdin: %v", err) + } + + return numbers, nil +} + +func main() { + var numbers []float64 + var err error + + if len(os.Args) > 1 { + numbers = make([]float64, 0, len(os.Args)-1) + for _, arg := range os.Args[1:] { + num, parseErr := strconv.ParseFloat(arg, 64) + if parseErr != nil { + fmt.Fprintf(os.Stderr, "Invalid number: %s\n", arg) + os.Exit(1) + } + numbers = append(numbers, num) + } + } else { + numbers, err = readFromStdin() + if err != nil { + fmt.Fprintf(os.Stderr, "Error: %v\n", err) + os.Exit(1) + } + + if len(numbers) == 0 { + fmt.Fprintln(os.Stderr, "No numbers provided") + os.Exit(1) + } + } + + h := histogram(numbers) + fmt.Println(string(h)) +} diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..6d1a15e --- /dev/null +++ b/flake.lock @@ -0,0 +1,142 @@ +{ + "nodes": { + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1747046372, + "narHash": "sha256-CIVLLkVgvHYbgI2UpXvIIBJ12HWgX+fjA8Xf8PUmqCY=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1755274400, + "narHash": "sha256-rTInmnp/xYrfcMZyFMH3kc8oko5zYfxsowaLv1LVobY=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "ad7196ae55c295f53a7d1ec39e4a06d922f3b899", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-25.05", + "repo": "nixpkgs", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": "flake-compat", + "gitignore": "gitignore", + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1755446520, + "narHash": "sha256-I0Ok1OGDwc1jPd8cs2VvAYZsHriUVFGIUqW+7uSsOUM=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "4b04db83821b819bbbe32ed0a025b31e7971f22e", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs", + "pre-commit-hooks": "pre-commit-hooks", + "treefmt-nix": "treefmt-nix" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "treefmt-nix": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1754847726, + "narHash": "sha256-2vX8QjO5lRsDbNYvN9hVHXLU6oMl+V/PsmIiJREG4rE=", + "owner": "numtide", + "repo": "treefmt-nix", + "rev": "7d81f6fb2e19bf84f1c65135d1060d829fae2408", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "treefmt-nix", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..0833885 --- /dev/null +++ b/flake.nix @@ -0,0 +1,135 @@ +{ + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-25.05"; + flake-utils.url = "github:numtide/flake-utils"; + treefmt-nix = { + url = "github:numtide/treefmt-nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + pre-commit-hooks = { + url = "github:cachix/git-hooks.nix"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + outputs = + { + self, + nixpkgs, + treefmt-nix, + flake-utils, + pre-commit-hooks, + }: + let + # Import our packages and overlay + overlay = import ./nix/overlay.nix; + + # Import NixOS modules + nixosModules = { + goget = import ./nix/modules/goget.nix; + default = { + imports = [ + ./nix/modules/goget.nix + ]; + }; + }; + in + { + # Export the overlay for others to use + overlays.default = overlay; + + # Export NixOS modules + inherit nixosModules; + nixosModule = nixosModules.default; + } + // flake-utils.lib.eachDefaultSystem ( + system: + let + pkgs = import nixpkgs { + inherit system; + overlays = [ overlay ]; + }; + + packages = import ./nix/packages { inherit pkgs; }; + + treefmtEval = treefmt-nix.lib.evalModule pkgs ./treefmt.nix; + + ciPackages = with pkgs; [ + go # Need that obviously + gofumpt # Go formatter + golangci-lint # Local/CI linter + gotestsum # Pretty tester + goperf # Go performance suite + ]; + + devPackages = with pkgs; [ + gopls + gotools + ]; + in + { + packages = packages // { + default = packages.goget; + }; + + formatter = treefmtEval.config.build.wrapper; + + checks = { + # Throws an error if any of the source files are not correctly formatted + # when you run `nix flake check --print-build-logs`. Useful for CI + treefmt = treefmtEval.config.build.check self; + pre-commit-check = pre-commit-hooks.lib.${system}.run { + src = ./.; + hooks = { + format = { + enable = true; + name = "Format with treefmt"; + pass_filenames = true; + entry = "${treefmtEval.config.build.wrapper}/bin/treefmt"; + stages = [ + "pre-commit" + "pre-push" + ]; + }; + golangci-lint = { + enable = true; + package = pkgs.golangci-lint; + pass_filenames = true; + stages = [ "pre-push" ]; + }; + unit-tests-go = { + enable = true; + name = "Run unit tests"; + entry = "gotestsum --format testdox ./..."; + pass_filenames = false; + stages = [ "pre-push" ]; + types = [ "go" ]; + }; + }; + }; + }; + + devShells = { + default = pkgs.mkShell { + buildInputs = + [ ] ++ ciPackages ++ devPackages ++ self.checks.${system}.pre-commit-check.enabledPackages; + + shellHook = '' + ${self.checks.${system}.pre-commit-check.shellHook} + export GOTOOLCHAIN=local + export GOPRIVATE=go.fcuny.net/* + ''; + }; + + ci = pkgs.mkShell { + buildInputs = [ ] ++ ciPackages; + CI = true; + shellHook = '' + export GOTOOLCHAIN=local + export GOPRIVATE=go.fcuny.net/* + echo "Entering CI shell. Only essential CI tools available." + ''; + }; + }; + } + ); +} @@ -0,0 +1,20 @@ +module go.fcuny.net/x + +go 1.24.5 + +require ( + github.com/prometheus/client_golang v1.23.0 + golang.org/x/mod v0.27.0 + golang.org/x/text v0.28.0 +) + +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.65.0 // indirect + github.com/prometheus/procfs v0.16.1 // indirect + golang.org/x/sys v0.33.0 // indirect + google.golang.org/protobuf v1.36.6 // indirect +) @@ -0,0 +1,38 @@ +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.23.0 h1:ust4zpdl9r4trLY/gSjlm07PuiBq2ynaXXlptpfy8Uc= +github.com/prometheus/client_golang v1.23.0/go.mod h1:i/o0R9ByOnHX0McrTMTyhYvKE4haaf2mW08I+jGAjEE= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE= +github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8= +github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= +github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/mod v0.27.0 h1:kb+q2PyFnEADO2IEF935ehFUXlWiNjJWtRNgBLSfbxQ= +golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= +golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= +golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= +google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/nix/modules/goget.nix b/nix/modules/goget.nix new file mode 100644 index 0000000..3ed5e04 --- /dev/null +++ b/nix/modules/goget.nix @@ -0,0 +1,66 @@ +{ + config, + lib, + pkgs, + ... +}: + +with lib; + +let + cfg = config.services.goget; +in +{ + options.services.goget = { + enable = mkEnableOption "goget service"; + + package = mkPackageOption pkgs "goget" { }; + + port = mkOption { + type = types.port; + default = 8070; + description = "Port to listen on"; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = "Whether to open the firewall for the goget service"; + }; + }; + + config = mkIf cfg.enable { + systemd.services.goget = { + description = "goget service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + wants = [ "network.target" ]; + + serviceConfig = { + Type = "exec"; + DynamicUser = true; + ExecStart = "${cfg.package}/bin/goget"; + Restart = "always"; + RestartSec = "5"; + + # Security settings + NoNewPrivileges = true; + ProtectSystem = "strict"; + ProtectHome = true; + PrivateTmp = true; + ProtectKernelTunables = true; + ProtectKernelModules = true; + ProtectControlGroups = true; + RestrictSUIDSGID = true; + RestrictRealtime = true; + RestrictNamespaces = true; + LockPersonality = true; + MemoryDenyWriteExecute = true; + }; + }; + + networking.firewall = mkIf cfg.openFirewall { + allowedTCPPorts = [ cfg.port ]; + }; + }; +} diff --git a/nix/overlay.nix b/nix/overlay.nix new file mode 100644 index 0000000..6789627 --- /dev/null +++ b/nix/overlay.nix @@ -0,0 +1,5 @@ +final: prev: +let + packages = import ./packages { pkgs = final; }; +in +packages diff --git a/nix/packages/default.nix b/nix/packages/default.nix new file mode 100644 index 0000000..e598fa1 --- /dev/null +++ b/nix/packages/default.nix @@ -0,0 +1,5 @@ +{ pkgs }: + +{ + goget = pkgs.callPackage ./goget.nix { }; +} diff --git a/nix/packages/goget.nix b/nix/packages/goget.nix new file mode 100644 index 0000000..c767740 --- /dev/null +++ b/nix/packages/goget.nix @@ -0,0 +1,31 @@ +{ + lib, + buildGoModule, +}: + +buildGoModule rec { + pname = "goget"; + version = "0.1.0"; # Consider deriving from git tags: version = builtins.substring 0 8 self.rev; + + src = ../..; + + vendorHash = "sha256-pStRgjhjjZdsYSnYMcWNbHSF7CJ3+7ZQradZgBfi5Gw="; + + subPackages = [ "cmd/goget" ]; + + ldflags = [ + "-s" + "-w" + ]; + + doCheck = false; + + meta = with lib; { + description = "A Go tool for getting things"; # Update with actual description + homepage = "https://github.com/yourusername/yourrepo"; # Update with your repo + license = licenses.mit; + maintainers = with maintainers; [ fcuny ]; + platforms = platforms.unix; + mainProgram = "goget"; + }; +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..a978b3e --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.89" +components = ["rustfmt", "clippy", "rust-analyzer"] diff --git a/src/apple_silicon/Cargo.lock b/src/apple_silicon/Cargo.lock new file mode 100644 index 0000000..1ee0f1d --- /dev/null +++ b/src/apple_silicon/Cargo.lock @@ -0,0 +1,65 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "apple_silicon" +version = "0.1.0" +dependencies = [ + "thiserror", +] + +[[package]] +name = "proc-macro2" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/src/apple_silicon/Cargo.toml b/src/apple_silicon/Cargo.toml new file mode 100644 index 0000000..65dcf5e --- /dev/null +++ b/src/apple_silicon/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "apple_silicon" +version = "0.1.0" +edition = "2021" +description = "A common line tool for Apple Silicon" + +license = "MIT" +authors = ["Franck Cuny <franck@fcuny.net>"] +repository = "https://github.com/fcuny/apple_silicon" +homepage = "https://github.com/fcuny/apple_silicon" +categories = ["command-line-utilities"] + +[[bin]] +name = "apple_silicon" +path = "src/bin/apple_silicon.rs" + +[dependencies] +thiserror = "2.0.11" diff --git a/src/apple_silicon/README.md b/src/apple_silicon/README.md new file mode 100644 index 0000000..7d83327 --- /dev/null +++ b/src/apple_silicon/README.md @@ -0,0 +1 @@ +A command line utility to report information about Apple Silicon (inspired by [`asitop`](https://github.com/tlkh/asitop/tree/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd)). diff --git a/src/apple_silicon/src/bin/apple_silicon.rs b/src/apple_silicon/src/bin/apple_silicon.rs new file mode 100644 index 0000000..0f71eeb --- /dev/null +++ b/src/apple_silicon/src/bin/apple_silicon.rs @@ -0,0 +1,12 @@ +use apple_silicon::soc::SocInfo; + +fn main() { + let cpu_info = SocInfo::new().unwrap(); + println!( + "our CPU is an {}, and we have {} CPU cores, and {} GPU cores. The TDP is {}.", + cpu_info.cpu_brand_name, + cpu_info.num_cpu_cores, + cpu_info.num_gpu_cores, + cpu_info.cpu_max_power.unwrap(), + ); +} diff --git a/src/apple_silicon/src/error.rs b/src/apple_silicon/src/error.rs new file mode 100644 index 0000000..a70b9b5 --- /dev/null +++ b/src/apple_silicon/src/error.rs @@ -0,0 +1,23 @@ +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("socinfo parsing error: `{0}`")] + Parse(String), + + #[error("I/O error: `{source}`")] + Io { + #[from] + source: std::io::Error, + }, + + #[error("utf8 conversion error: `{source}`")] + Utf8Conversion { + #[from] + source: std::string::FromUtf8Error, + }, + + #[error("integer parsing error: `{source}`")] + ParseInt { + #[from] + source: std::num::ParseIntError, + }, +} diff --git a/src/apple_silicon/src/lib.rs b/src/apple_silicon/src/lib.rs new file mode 100644 index 0000000..74bd62c --- /dev/null +++ b/src/apple_silicon/src/lib.rs @@ -0,0 +1,2 @@ +pub mod error; +pub mod soc; diff --git a/src/apple_silicon/src/soc.rs b/src/apple_silicon/src/soc.rs new file mode 100644 index 0000000..0e2bf5c --- /dev/null +++ b/src/apple_silicon/src/soc.rs @@ -0,0 +1,302 @@ +use crate::error::Error; + +use std::process::{Command, Output}; + +pub type Result<T> = std::result::Result<T, Error>; +pub type Watts = u32; +pub type Bandwidth = u32; +pub type CoreCount = u16; + +/// Information about the Silicon chip +pub struct SocInfo { + /// The CPU brand name string + pub cpu_brand_name: String, + /// Number of CPU cores + pub num_cpu_cores: CoreCount, + /// Number of GPU cores + pub num_gpu_cores: CoreCount, + /// Maximum CPU power in watts (if available) + pub cpu_max_power: Option<Watts>, + /// Maximum GPU power in watts (if available) + pub gpu_max_power: Option<Watts>, + /// Maximum CPU bandwidth in GB/s (if available) + pub cpu_max_bw: Option<Bandwidth>, + /// Maximum GPU bandwidth in GB/s (if available) + pub gpu_max_bw: Option<Bandwidth>, + /// Number of efficiency cores + pub e_core_count: CoreCount, + /// Number of performance cores + pub p_core_count: CoreCount, +} + +#[derive(Debug, PartialEq)] +enum AppleChip { + M1, + M1Pro, + M1Max, + M1Ultra, + M2, + M2Pro, + M2Max, + M2Ultra, + M3, + M3Pro, + M3Max, + Unknown, +} + +struct ChipSpecs { + cpu_tdp: Watts, + gpu_tdp: Watts, + cpu_bw: Bandwidth, + gpu_bw: Bandwidth, +} + +impl AppleChip { + fn from_brand_string(brand: &str) -> Self { + match brand { + s if s.contains("M1 Pro") => AppleChip::M1Pro, + s if s.contains("M1 Max") => AppleChip::M1Max, + s if s.contains("M1 Ultra") => AppleChip::M1Ultra, + s if s.contains("M1") => AppleChip::M1, + s if s.contains("M2 Pro") => AppleChip::M2Pro, + s if s.contains("M2 Max") => AppleChip::M2Max, + s if s.contains("M2 Ultra") => AppleChip::M2Ultra, + s if s.contains("M2") => AppleChip::M2, + s if s.contains("M3 Pro") => AppleChip::M3Pro, + s if s.contains("M3 Max") => AppleChip::M3Max, + s if s.contains("M3") => AppleChip::M3, + _ => AppleChip::Unknown, + } + } + + fn get_specs(&self) -> ChipSpecs { + match self { + AppleChip::M1 => ChipSpecs { + cpu_tdp: 20, + gpu_tdp: 20, + cpu_bw: 70, + gpu_bw: 70, + }, + AppleChip::M1Pro => ChipSpecs { + cpu_tdp: 30, + gpu_tdp: 30, + cpu_bw: 200, + gpu_bw: 200, + }, + AppleChip::M1Max => ChipSpecs { + cpu_tdp: 30, + gpu_tdp: 60, + cpu_bw: 250, + gpu_bw: 400, + }, + AppleChip::M1Ultra => ChipSpecs { + cpu_tdp: 60, + gpu_tdp: 120, + cpu_bw: 500, + gpu_bw: 800, + }, + AppleChip::M2 => ChipSpecs { + cpu_tdp: 25, + gpu_tdp: 15, + cpu_bw: 100, + gpu_bw: 100, + }, + AppleChip::M2Pro => ChipSpecs { + cpu_tdp: 30, + gpu_tdp: 35, + cpu_bw: 0, + gpu_bw: 0, + }, + AppleChip::M2Max => ChipSpecs { + cpu_tdp: 30, + gpu_tdp: 40, + cpu_bw: 0, + gpu_bw: 0, + }, + // Add more variants as needed + _ => ChipSpecs { + cpu_tdp: 0, + gpu_tdp: 0, + cpu_bw: 0, + gpu_bw: 0, + }, + } + } +} + +impl SocInfo { + pub fn new() -> Result<SocInfo> { + let (cpu_brand_name, num_cpu_cores, e_core_count, p_core_count) = cpu_info(&RealCommand)?; + let num_gpu_cores = gpu_info(&RealCommand)?; + + let chip = AppleChip::from_brand_string(&cpu_brand_name); + let specs = chip.get_specs(); + + Ok(SocInfo { + cpu_brand_name, + num_cpu_cores, + num_gpu_cores, + cpu_max_power: Some(specs.cpu_tdp), + gpu_max_power: Some(specs.gpu_tdp), + cpu_max_bw: Some(specs.cpu_bw), + gpu_max_bw: Some(specs.gpu_bw), + e_core_count, + p_core_count, + }) + } +} + +// https://github.com/tlkh/asitop/blob/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd/asitop/utils.py#L94 +const SYSCTL_PATH: &str = "/usr/sbin/sysctl"; + +fn cpu_info(cmd: &impl SystemCommand) -> Result<(String, u16, u16, u16)> { + let binary = SYSCTL_PATH; + let args = &[ + // don't display the variable name + "-n", + "machdep.cpu.brand_string", + "machdep.cpu.core_count", + "hw.perflevel0.logicalcpu", + "hw.perflevel1.logicalcpu", + ]; + + let output = cmd.execute(binary, args)?; + let buffer = String::from_utf8(output.stdout)?; + + let mut iter = buffer.split('\n'); + let cpu_brand_name = match iter.next() { + Some(s) => s.to_string(), + None => return Err(Error::Parse(buffer.to_string())), + }; + + let num_cpu_cores = match iter.next() { + Some(s) => s.parse::<u16>()?, + None => return Err(Error::Parse(buffer.to_string())), + }; + + let num_performance_cores = match iter.next() { + Some(s) => s.parse::<u16>()?, + None => return Err(Error::Parse(buffer.to_string())), + }; + + let num_efficiency_cores = match iter.next() { + Some(s) => s.parse::<u16>()?, + None => return Err(Error::Parse(buffer.to_string())), + }; + + Ok(( + cpu_brand_name, + num_cpu_cores, + num_performance_cores, + num_efficiency_cores, + )) +} + +// https://github.com/tlkh/asitop/blob/74ebe2cbc23d5b1eec874aebb1b9bacfe0e670cd/asitop/utils.py#L120 +fn gpu_info(cmd: &impl SystemCommand) -> Result<u16> { + let binary = "/usr/sbin/system_profiler"; + let args = &["-detailLevel", "basic", "SPDisplaysDataType"]; + + let output = cmd.execute(binary, args)?; + let buffer = String::from_utf8(output.stdout)?; + + let num_gpu_cores_line = buffer + .lines() + .find(|&line| line.trim_start().starts_with("Total Number of Cores")); + + let num_gpu_cores = match num_gpu_cores_line { + Some(s) => match s.split(": ").last() { + Some(s) => s.parse::<u16>()?, + None => return Err(Error::Parse(buffer.to_string())), + }, + None => return Err(Error::Parse(buffer.to_string())), + }; + + Ok(num_gpu_cores) +} + +/// Trait for system command execution +pub trait SystemCommand { + fn execute(&self, binary: &str, args: &[&str]) -> Result<Output>; +} + +/// Real command executor +pub struct RealCommand; + +impl SystemCommand for RealCommand { + fn execute(&self, binary: &str, args: &[&str]) -> Result<Output> { + Ok(Command::new(binary).args(args).output()?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::os::unix::process::ExitStatusExt; + + struct MockCommand { + output: Vec<u8>, + } + + impl MockCommand { + fn new(output: &str) -> Self { + Self { + output: output.as_bytes().to_vec(), + } + } + } + + impl SystemCommand for MockCommand { + fn execute(&self, _binary: &str, _args: &[&str]) -> Result<Output> { + Ok(Output { + status: std::process::ExitStatus::from_raw(0), + stdout: self.output.clone(), + stderr: Vec::new(), + }) + } + } + + #[test] + fn test_gpu_info() { + let mock_output = r#"Graphics/Displays: + Apple M2: + Total Number of Cores: 10"#; + let cmd = MockCommand::new(mock_output); + + let result = gpu_info(&cmd); + assert_eq!(result.unwrap(), 10); + } + + #[test] + fn test_cpu_info_success() { + let mock_output = "Apple M2\n8\n4\n4\n"; + let cmd = MockCommand::new(mock_output); + + let result = cpu_info(&cmd); + assert!(result.is_ok()); + let (brand, cores, p_cores, e_cores) = result.unwrap(); + assert_eq!(brand, "Apple M2"); + assert_eq!(cores, 8); + assert_eq!(p_cores, 4); + assert_eq!(e_cores, 4); + } + + #[test] + fn test_cpu_info_missing_core_count() { + let mock_output = "Apple M2\n"; + let cmd = MockCommand::new(mock_output); + + let result = cpu_info(&cmd); + assert!(matches!(result, Err(Error::ParseInt { .. }))); + } + + #[test] + fn test_cpu_info_invalid_core_count() { + let mock_output = "Apple M2\ninvalid\n"; + let cmd = MockCommand::new(mock_output); + + let result = cpu_info(&cmd); + assert!(matches!(result, Err(Error::ParseInt { .. }))); + } +} diff --git a/src/x509-info/Cargo.lock b/src/x509-info/Cargo.lock new file mode 100644 index 0000000..6e8a026 --- /dev/null +++ b/src/x509-info/Cargo.lock @@ -0,0 +1,1135 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[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 = "anstream" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" + +[[package]] +name = "asn1-rs" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ad1373757efa0f70ec53939aabc7152e1591cb485208052993070ac8d2429d" +dependencies = [ + "asn1-rs-derive", + "asn1-rs-impl", + "displaydoc", + "nom", + "num-traits", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "asn1-rs-derive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7378575ff571966e99a744addeff0bff98b8ada0dedf1956d59e634db95eaac1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "asn1-rs-impl" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + +[[package]] +name = "aws-lc-rs" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5509d663b2c00ee421bda8d6a24d6c42e15970957de1701b8df9f6fbe5707df1" +dependencies = [ + "aws-lc-sys", + "mirai-annotations", + "paste", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d5d317212c2a78d86ba6622e969413c38847b62f48111f8b763af3dac2f9840" +dependencies = [ + "bindgen", + "cc", + "cmake", + "dunce", + "fs_extra", + "libc", + "paste", +] + +[[package]] +name = "base64" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9475866fec1451be56a3c2400fd081ff546538961565ccb5b7142cbd22bc7a51" + +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.5.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + +[[package]] +name = "cc" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32a725bc159af97c3e629873bb9f88fb8cf8a4867175f76dc987815ea07c83b" +dependencies = [ + "jobserver", + "libc", + "once_cell", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "num-traits", + "windows-targets", +] + +[[package]] +name = "clang-sys" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" + +[[package]] +name = "data-encoding" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" + +[[package]] +name = "der-parser" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cd0a5c643689626bec213c4d8bd4d96acc8ffdb4ad4bb6bc16abf27d5f4b553" +dependencies = [ + "asn1-rs", + "displaydoc", + "nom", + "num-bigint", + "num-traits", + "rusticata-macros", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "displaydoc" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "either" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "getrandom" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "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 = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "jobserver" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" +dependencies = [ + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.153" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libloading" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +dependencies = [ + "cfg-if", + "windows-targets", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" + +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + +[[package]] +name = "memchr" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mirai-annotations" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9be0862c1b3f26a88803c4a49de6889c10e608b3ee9344e6ef5b45fb37ad3d1" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "oid-registry" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c958dd45046245b9c3c2547369bb634eb461670b2e7e0de552905801a648d1d" +dependencies = [ + "asn1-rs", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "prettyplease" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.81" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rusticata-macros" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" +dependencies = [ + "nom", +] + +[[package]] +name = "rustix" +version = "0.38.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustls" +version = "0.23.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79adb16721f56eb2d843e67676896a61ce7a0fa622dc18d3e372477a029d2740" +dependencies = [ + "aws-lc-rs", + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fb85efa936c42c6d5fc28d2629bb51e4b2f4b8a5211e297d599cc5a093792" +dependencies = [ + "openssl-probe", + "rustls-pemfile", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" + +[[package]] +name = "rustls-webpki" +version = "0.102.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff448f7e92e913c4b7d4c6d8e4540a1724b319b4152b8aef6d4cf8339712b33e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "schannel" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "security-framework" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.199" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c9f6e76df036c77cd94996771fb40db98187f096dd0b9af39c6c6e452ba966a" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.199" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11bd257a6541e141e42ca6d24ae26f7714887b47e89aa739099104c7e4d3b7fc" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" + +[[package]] +name = "syn" +version = "2.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + +[[package]] +name = "webpki-roots" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + +[[package]] +name = "x509-info" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "rustls", + "rustls-native-certs", + "webpki-roots", + "x509-parser", +] + +[[package]] +name = "x509-parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcbc162f30700d6f3f82a24bf7cc62ffe7caea42c0b2cba8bf7f3ae50cf51f69" +dependencies = [ + "asn1-rs", + "data-encoding", + "der-parser", + "lazy_static", + "nom", + "oid-registry", + "rusticata-macros", + "thiserror", + "time", +] + +[[package]] +name = "zeroize" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" diff --git a/src/x509-info/Cargo.toml b/src/x509-info/Cargo.toml new file mode 100644 index 0000000..24f0478 --- /dev/null +++ b/src/x509-info/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "x509-info" +version = "0.1.0" +edition = "2021" +authors = ["Franck Cuny <franck@fcuny.net>"] +description = "Print summary of x509 certificates." +homepage = "https://github.com/fcuny/x509-info/" +readme = "README.md" + +[dependencies] +anyhow = "1.0.82" +chrono = {version = "0.4.38", features = ["clock"], default-features = false } +clap = {version = "4.5.8", features = ["derive", "cargo"]} +rustls = {version = "0.23.8", features = ["ring"]} +rustls-native-certs = "0.7.0" +webpki-roots = "0.26.3" +x509-parser = "0.16" diff --git a/src/x509-info/README.md b/src/x509-info/README.md new file mode 100644 index 0000000..0369ba2 --- /dev/null +++ b/src/x509-info/README.md @@ -0,0 +1,72 @@ +# x509-info + +At this point it's pretty clear that I'll never remember the syntax for `openssl` to show various information about a certificate. At last I will not have to google for that syntax ever again. + +## Usage + +```shell +$ Usage: x509-info [OPTIONS] <DOMAIN> + +Arguments: + <DOMAIN> + Domain to check + +Options: + -p, --port <PORT> + Port to check + + [default: 443] + + -i, --insecure + Accept invalid certificate + + -f, --format <FORMAT> + [default: short] + + Possible values: + - short: Format the output as one line of plain text + - long: Format the output as plain text + + -h, --help + Print help information (use `-h` for a summary) + + -V, --version + Print version information +``` + +The default format will print a short message: + +```shell +$ x509-info twitter.com +twitter.com: Mon, 11 Dec 2023 15:59:59 -0800 (257 days left) +``` + +It's possible to get more details: + +```shell +$ x509-info --format=long twitter.com +certificate + version: V3 + serial: 0a:2c:01:b8:2b:5d:47:73:9a:5a:01:1a:6f:dc:1a:20 + subject: C=US, ST=California, L=San Francisco, O=Twitter, Inc., CN=twitter.com + issuer: C=US, O=DigiCert Inc, CN=DigiCert TLS RSA SHA256 2020 CA1 + validity + not before : Sat, 10 Dec 2022 16:00:00 -0800 + not after : Mon, 11 Dec 2023 15:59:59 -0800 + validity days : 365 + remaining days: 257 + SANs: + DNS:twitter.com + DNS:www.twitter.com +``` + +You can also check expired certificates: + +```shell +$ x509-info --insecure expired.badssl.com +<no name>: Sun, 12 Apr 2015 16:59:59 -0700 (it expired -2907 days ago) +``` + +## Notes + +Could the same be achieved with a wrapper around `openssl` ? yes. diff --git a/src/x509-info/deny.toml b/src/x509-info/deny.toml new file mode 100644 index 0000000..fd95cdb --- /dev/null +++ b/src/x509-info/deny.toml @@ -0,0 +1,46 @@ +[advisories] +db-path = "~/.cargo/advisory-db" +db-urls = ["https://github.com/rustsec/advisory-db"] +vulnerability = "deny" +unmaintained = "warn" +yanked = "warn" +notice = "warn" +ignore = [] + +[licenses] +unlicensed = "deny" +allow = ["MIT", "Apache-2.0", "ISC", "Unicode-DFS-2016", "OpenSSL"] +deny = [] +copyleft = "allow" +default = "deny" +confidence-threshold = 0.8 +exceptions = [] + +[licenses.private] +ignore = false +registries = [] + +# see https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html#example +[[licenses.clarify]] +name = "ring" +expression = "MIT AND ISC AND OpenSSL" +license-files = [ + { path = "LICENSE", hash = 0xbd0eed23 } +] + +[bans] +multiple-versions = "warn" +wildcards = "allow" +highlight = "all" +allow = [] +deny = [] +skip = [] +skip-tree = [] + +[sources] +unknown-registry = "warn" +unknown-git = "warn" +allow-registry = ["https://github.com/rust-lang/crates.io-index"] +allow-git = [] + +[sources.allow-org] diff --git a/src/x509-info/src/client.rs b/src/x509-info/src/client.rs new file mode 100644 index 0000000..83e010c --- /dev/null +++ b/src/x509-info/src/client.rs @@ -0,0 +1,110 @@ +use anyhow::{anyhow, Error}; +use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier}; +use rustls::crypto::{ring, verify_tls12_signature, verify_tls13_signature, CryptoProvider}; +use rustls::pki_types::{self, CertificateDer, ServerName, UnixTime}; +use rustls::{ClientConfig, ClientConnection}; +use rustls::{DigitallySignedStruct, SignatureScheme}; + +use std::io::Write; +use std::sync::Arc; + +#[derive(Debug)] +struct NoCertificateVerification {} + +impl ServerCertVerifier for NoCertificateVerification { + fn verify_server_cert( + &self, + _end_entity: &CertificateDer<'_>, + _intermediates: &[CertificateDer<'_>], + _server_name: &pki_types::ServerName<'_>, + _ocsp_response: &[u8], + _now: UnixTime, + ) -> Result<ServerCertVerified, rustls::Error> { + Ok(ServerCertVerified::assertion()) + } + + fn verify_tls12_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, rustls::Error> { + verify_tls12_signature( + message, + cert, + dss, + &ring::default_provider().signature_verification_algorithms, + ) + } + + fn verify_tls13_signature( + &self, + message: &[u8], + cert: &CertificateDer<'_>, + dss: &DigitallySignedStruct, + ) -> Result<HandshakeSignatureValid, rustls::Error> { + verify_tls13_signature( + message, + cert, + dss, + &ring::default_provider().signature_verification_algorithms, + ) + } + + fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { + ring::default_provider() + .signature_verification_algorithms + .supported_schemes() + } +} + +pub fn get_certificates( + domain: String, + port: u16, + insecure: bool, +) -> Result<Vec<CertificateDer<'static>>, Error> { + let mut tcp_stream = std::net::TcpStream::connect(format!("{}:{}", domain, port))?; + + let config = if insecure { + ClientConfig::builder() + .dangerous() + .with_custom_certificate_verifier(Arc::new(NoCertificateVerification {})) + .with_no_client_auth() + } else { + let root_store = rustls::RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), + }; + ClientConfig::builder_with_provider( + CryptoProvider { + cipher_suites: vec![ring::cipher_suite::TLS13_CHACHA20_POLY1305_SHA256], + kx_groups: vec![ring::kx_group::X25519], + ..ring::default_provider() + } + .into(), + ) + .with_protocol_versions(&[&rustls::version::TLS13]) + .unwrap() + .with_root_certificates(root_store) + .with_no_client_auth() + }; + + let server_name = ServerName::try_from(domain.clone()) + .expect("invalid DNS name") + .to_owned(); + + let mut conn = ClientConnection::new(Arc::new(config), server_name)?; + while conn.wants_write() { + conn.write_tls(&mut tcp_stream)?; + } + + tcp_stream.flush()?; + while conn.is_handshaking() && conn.peer_certificates().is_none() { + conn.read_tls(&mut tcp_stream)?; + conn.process_new_packets()?; + } + + match conn.peer_certificates() { + Some(c) => Ok(c.to_vec()), + None => Err(anyhow!("no certificate found for {}", domain))?, + } +} diff --git a/src/x509-info/src/main.rs b/src/x509-info/src/main.rs new file mode 100644 index 0000000..d37ce0c --- /dev/null +++ b/src/x509-info/src/main.rs @@ -0,0 +1,41 @@ +extern crate webpki_roots; + +mod client; +mod output; + +use clap::Parser; +use x509_parser::prelude::*; + +#[derive(Parser, Debug)] +#[clap(author, version, about, long_about = None)] +struct Args { + /// Domain to check + domain: String, + + /// Port to check + #[clap(short, long, default_value_t = 443)] + port: u16, + + /// Accept invalid certificate + #[clap(short, long, default_value_t = false)] + insecure: bool, + + #[clap(short, long,value_enum, default_value_t = output::OutputFormat::Short)] + format: output::OutputFormat, +} + +fn main() { + let args = Args::parse(); + + match client::get_certificates(args.domain, args.port, args.insecure) { + Ok(certs) => { + let (_, cert) = + x509_parser::certificate::X509Certificate::from_der(certs[0].as_ref()).unwrap(); + output::OutputFormat::print(args.format, cert); + } + Err(e) => { + println!("error: {}", e); + std::process::exit(1); + } + }; +} diff --git a/src/x509-info/src/output.rs b/src/x509-info/src/output.rs new file mode 100644 index 0000000..963ef20 --- /dev/null +++ b/src/x509-info/src/output.rs @@ -0,0 +1,120 @@ +use chrono::prelude::*; +use std::net::{Ipv4Addr, Ipv6Addr}; +use x509_parser::prelude::*; + +/// How to format the output data. +#[derive(PartialEq, Eq, Debug, Copy, Clone, clap::ValueEnum)] +pub enum OutputFormat { + /// Format the output as one line of plain text. + Short, + + /// Format the output as plain text. + Long, +} + +impl OutputFormat { + pub fn print(self, cert: X509Certificate) { + match self { + Self::Short => { + self.short(cert); + } + Self::Long => { + self.to_text(cert); + } + } + } + + fn short(self, cert: X509Certificate) { + let not_after = chrono::Local + .timestamp_opt(cert.validity().not_after.timestamp(), 0) + .unwrap(); + let now: DateTime<Local> = Local::now(); + let remaining = not_after - now; + + if remaining >= chrono::Duration::zero() { + println!( + "{}: {} ({} days left)", + cert.subject() + .iter_common_name() + .next() + .and_then(|cn| cn.as_str().ok()) + .unwrap_or("<no name>"), + not_after.to_rfc2822(), + remaining.num_days(), + ); + } else { + println!( + "{}: {} (it expired {} days ago)", + cert.subject() + .iter_common_name() + .next() + .and_then(|cn| cn.as_str().ok()) + .unwrap_or("<no name>"), + not_after.to_rfc2822(), + remaining.num_days(), + ); + } + } + + fn to_text(self, cert: X509Certificate) { + let not_before = chrono::Local + .timestamp_opt(cert.validity().not_before.timestamp(), 0) + .unwrap(); + let not_after = chrono::Local + .timestamp_opt(cert.validity().not_after.timestamp(), 0) + .unwrap(); + let now: DateTime<Local> = Local::now(); + let remaining = not_after - now; + let validity_duration = not_after - not_before; + + println!("certificate"); + println!(" version: {}", cert.version); + println!(" serial: {}", cert.tbs_certificate.raw_serial_as_string()); + println!(" subject: {}", cert.subject()); + println!(" issuer: {}", cert.issuer()); + + println!(" validity"); + println!(" not before : {}", not_before.to_rfc2822()); + println!(" not after : {}", not_after.to_rfc2822()); + println!(" validity days : {}", validity_duration.num_days()); + println!(" remaining days: {}", remaining.num_days()); + + println!(" SANs:"); + if let Some(subnames) = subject_alternative_name(cert) { + for name in subnames { + println!(" {}", name); + } + } + } +} + +fn subject_alternative_name(cert: X509Certificate) -> Option<Vec<String>> { + let mut subnames = Vec::new(); + if let Ok(Some(san)) = cert.subject_alternative_name() { + let san = san.value; + for name in &san.general_names { + let s = match name { + GeneralName::DNSName(s) => format!("DNS:{}", s), + GeneralName::IPAddress(b) => { + let ip = match b.len() { + 4 => { + let b = <[u8; 4]>::try_from(*b).unwrap(); + let ip = Ipv4Addr::from(b); + format!("{}", ip) + } + 16 => { + let b = <[u8; 16]>::try_from(*b).unwrap(); + let ip = Ipv6Addr::from(b); + format!("{}", ip) + } + l => format!("invalid (len={})", l), + }; + format!("IP address:{}", ip) + } + _ => format!("{:?}", name), + }; + subnames.push(s); + } + } + Some(subnames) +} diff --git a/treefmt.nix b/treefmt.nix new file mode 100644 index 0000000..b2e04da --- /dev/null +++ b/treefmt.nix @@ -0,0 +1,35 @@ +{ pkgs, ... }: +{ + # See https://github.com/numtide/treefmt-nix#supported-programs + projectRootFile = ".git/config"; + settings.global.includes = [ + "*.go" + "*.md" + "*.nix" + "*.rs" + "*.toml" + "*.yaml" + "*.yml" + ]; + settings.global.fail-on-change = true; + settings.global.no-cache = true; + programs.gofumpt = { + enable = true; + package = pkgs.gofumpt; + }; + programs.goimports.enable = true; + programs.golines.enable = true; + # GitHub Actions + programs.yamlfmt.enable = true; + programs.actionlint.enable = true; + # Markdown + programs.mdformat.enable = true; + # Rust + programs.rustfmt.enable = true; + programs.taplo.enable = true; + # Nix + programs.nixfmt = { + enable = true; + package = pkgs.nixfmt-rfc-style; + }; +} |
