From 56ccc6a31f2813c4f41e874377d82b9ac92d0de8 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 27 Sep 2025 11:47:46 +0100 Subject: [PATCH 1/9] feat: cli as lib --- Cargo.lock | 300 ++++++++++++++++++---------------------------------- Cargo.toml | 13 ++- src/bin.rs | 9 ++ src/main.rs | 65 +++++++----- 4 files changed, 162 insertions(+), 225 deletions(-) create mode 100644 src/bin.rs diff --git a/Cargo.lock b/Cargo.lock index f89fa58..fd75387 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,7 +271,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -292,12 +292,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - [[package]] name = "bitflags" version = "2.9.1" @@ -1218,7 +1212,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.5.9", "tokio", "tower-service", "tracing", @@ -1283,15 +1277,18 @@ version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710" dependencies = [ + "base64 0.22.1", "bytes", "futures-channel", "futures-util", "http 1.3.1", "http-body 1.0.1", "hyper 1.6.0", + "ipnet", "libc", + "percent-encoding", "pin-project-lite", - "socket2", + "socket2 0.5.9", "tokio", "tower-service", "tracing", @@ -1477,7 +1474,7 @@ dependencies = [ "quote", "strum", "syn", - "thiserror 2.0.12", + "thiserror", ] [[package]] @@ -1495,9 +1492,9 @@ dependencies = [ [[package]] name = "infer" -version = "0.16.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" dependencies = [ "cfb", ] @@ -1511,6 +1508,17 @@ dependencies = [ "generic-array", ] +[[package]] +name = "io-uring" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" +dependencies = [ + "bitflags", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -1655,7 +1663,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.9.1", + "bitflags", "libc", "redox_syscall", ] @@ -1931,11 +1939,11 @@ checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "onig" -version = "6.4.0" +version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c4b31c8722ad9171c6d77d3557db078cab2bd50afcc9d09c8b315c59df8ca4f" +checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags 1.3.2", + "bitflags", "libc", "once_cell", "onig_sys", @@ -1943,9 +1951,9 @@ dependencies = [ [[package]] name = "onig_sys" -version = "69.8.1" +version = "69.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b829e3d7e9cc74c7e315ee8edb185bf4190da5acde74afd7fc59c35b1f086e7" +checksum = "c7f86c6eef3d6df15f23bcfb6af487cbd2fed4e5581d58d5bf1f5f8b7f6727dc" dependencies = [ "cc", "pkg-config", @@ -2053,7 +2061,7 @@ dependencies = [ "test-log", "tokio", "tower 0.4.13", - "tower-http", + "tower-http 0.4.4", "tower-service", "tracing", "tracing-core", @@ -2063,9 +2071,9 @@ dependencies = [ [[package]] name = "pact_matching" -version = "2.0.0-beta.1" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb7056d9f060df24290009baaaeb77311ff73eea22bd176882b42ca5403ae8a" +checksum = "53025d152918a0dbe4df6c7773b20ecb098e90f49e7a720ff91e490ba761fd3a" dependencies = [ "ansi_term", "anyhow", @@ -2095,7 +2103,6 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "snailquote", "sxd-document", "tokio", "tracing", @@ -2106,9 +2113,9 @@ dependencies = [ [[package]] name = "pact_models" -version = "1.3.1" +version = "1.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dab72b0e00fd38e4c8ee9275898c9e2c73b7eaf0fe1066d5a99b7bc282255c4c" +checksum = "cca6eddec95d1d3ef89449ab28c5d5c966e848897717007813845b7e909d56a2" dependencies = [ "anyhow", "ariadne", @@ -2147,9 +2154,9 @@ dependencies = [ [[package]] name = "pact_verifier" -version = "1.3.0" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7297846557c882bd339ddf8346907e8dfa43c5faa85a4dcc50dce8d687ea92bd" +checksum = "95e5ce9ac40d5c70b1c29fddd57ae5be043960b058dc96653bb249d339231310" dependencies = [ "ansi_term", "anyhow", @@ -2172,7 +2179,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.12", + "thiserror", "tokio", "tracing", "tracing-core", @@ -2200,7 +2207,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -2454,8 +2461,8 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2", - "thiserror 2.0.12", + "socket2 0.5.9", + "thiserror", "tokio", "tracing", "web-time", @@ -2476,7 +2483,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror", "tinyvec", "tracing", "web-time", @@ -2491,7 +2498,7 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2", + "socket2 0.5.9", "tracing", "windows-sys 0.59.0", ] @@ -2606,7 +2613,7 @@ version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 2.9.1", + "bitflags", ] [[package]] @@ -2655,9 +2662,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.15" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "async-compression", "base64 0.22.1", @@ -2671,17 +2678,13 @@ dependencies = [ "hyper 1.6.0", "hyper-rustls", "hyper-util", - "ipnet", "js-sys", "log", - "mime", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", "rustls", "rustls-native-certs", - "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -2691,13 +2694,13 @@ dependencies = [ "tokio-rustls", "tokio-util", "tower 0.5.2", + "tower-http 0.6.6", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "wasm-streams", "web-sys", - "windows-registry", ] [[package]] @@ -2741,7 +2744,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags 2.9.1", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -2774,15 +2777,6 @@ dependencies = [ "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" @@ -2846,7 +2840,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags 2.9.1", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -3031,16 +3025,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" -[[package]] -name = "snailquote" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec62a949bda7f15800481a711909f946e1204f2460f89210eaf7f57730f88f86" -dependencies = [ - "thiserror 1.0.69", - "unicode_categories", -] - [[package]] name = "snapbox" version = "0.4.17" @@ -3082,6 +3066,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + [[package]] name = "spin" version = "0.9.8" @@ -3239,33 +3233,13 @@ dependencies = [ "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.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ - "thiserror-impl 2.0.12", -] - -[[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", + "thiserror-impl", ] [[package]] @@ -3347,20 +3321,22 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.0" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2513ca694ef9ede0fb23fe71a4ee4107cb102b9dc1930f6d0fd77aae068ae165" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "slab", + "socket2 0.6.0", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3470,7 +3446,7 @@ dependencies = [ "percent-encoding", "pin-project", "prost", - "socket2", + "socket2 0.5.9", "tokio", "tokio-stream", "tower 0.4.13", @@ -3537,7 +3513,7 @@ checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" dependencies = [ "async-compression", "base64 0.21.7", - "bitflags 2.9.1", + "bitflags", "bytes", "futures-core", "futures-util", @@ -3559,6 +3535,24 @@ dependencies = [ "uuid", ] +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http 1.3.1", + "http-body 1.0.1", + "iri-string", + "pin-project-lite", + "tower 0.5.2", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -3596,9 +3590,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ "once_cell", "valuable", @@ -3704,12 +3698,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - [[package]] name = "untrusted" version = "0.9.0" @@ -3747,11 +3735,13 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.16.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -3951,7 +3941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core 0.52.0", - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3960,7 +3950,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -3973,7 +3963,7 @@ dependencies = [ "windows-interface", "windows-link", "windows-result", - "windows-strings 0.4.2", + "windows-strings", ] [[package]] @@ -4004,17 +3994,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" -[[package]] -name = "windows-registry" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" -dependencies = [ - "windows-result", - "windows-strings 0.3.1", - "windows-targets 0.53.0", -] - [[package]] name = "windows-result" version = "0.3.4" @@ -4024,15 +4003,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-strings" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" -dependencies = [ - "windows-link", -] - [[package]] name = "windows-strings" version = "0.4.2" @@ -4048,7 +4018,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -4057,7 +4027,7 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets 0.52.6", + "windows-targets", ] [[package]] @@ -4066,30 +4036,14 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - -[[package]] -name = "windows-targets" -version = "0.53.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" -dependencies = [ - "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", + "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]] @@ -4098,96 +4052,48 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.53.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 = "winnow" version = "0.7.10" @@ -4203,7 +4109,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags", ] [[package]] @@ -4377,7 +4283,7 @@ dependencies = [ "memchr", "pbkdf2", "sha1", - "thiserror 2.0.12", + "thiserror", "time", "xz2", "zeroize", diff --git a/Cargo.toml b/Cargo.toml index 11e5b70..10516ce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,9 +23,9 @@ hyper = { version = "0.14.26", features = ["full"] } itertools = "0.10.5" maplit = "1.0.2" mimalloc = "0.1.46" -pact_matching = "~2.0.0-beta.1" -pact_verifier = "~1.3.0" -pact_models = "~1.3.1" +pact_matching = "~2.0.1" +pact_verifier = "1.3.3" +pact_models = "1.3.7" regex = "1.8.2" reqwest = { version = "0.12.12", default-features = false, features = ["json", "rustls-tls-native-roots"] } serde = "1.0.163" @@ -56,3 +56,10 @@ lto = true # https://doc.rust-lang.org/stable/rustc/codegen-options/ # https://doc.rust-lang.org/rustc/profile-guided-optimization.html # https://github.com/johnthagen/min-sized-rust + +[[bin]] +name = "pact-stub-server" +path = "src/bin.rs" +[lib] +name = "pact_stub_server_cli" +path = "src/main.rs" \ No newline at end of file diff --git a/src/bin.rs b/src/bin.rs new file mode 100644 index 0000000..2b81a32 --- /dev/null +++ b/src/bin.rs @@ -0,0 +1,9 @@ +use std::env; +use std::process::ExitCode; +use pact_stub_server_cli::handle_command_args; + +#[tokio::main] +async fn main() -> Result<(), ExitCode> { + let args: Vec = env::args().collect(); + handle_command_args(args).await +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 5157ba8..eb7bbff 100644 --- a/src/main.rs +++ b/src/main.rs @@ -107,13 +107,8 @@ mod loading; #[global_allocator] static GLOBAL: MiMalloc = MiMalloc; -#[tokio::main] -async fn main() -> Result<(), ExitCode> { - let args: Vec = env::args().collect(); - handle_command_args(args).await -} -fn print_version() { +pub fn print_version() { println!("pact stub server version : v{}", env!("CARGO_PKG_VERSION")); println!("pact specification version: v{}", PactSpecification::V4.version_str()); } @@ -197,10 +192,44 @@ fn pact_source(matches: &ArgMatches) -> Vec { sources } -async fn handle_command_args(args: Vec) -> Result<(), ExitCode> { +/// Handles the command line arguments and runs the stub server accordingly. +/// +/// Used by the binary crate. Parses the provided arguments, sets up logging, loads pact files, and starts the server. +pub async fn handle_command_args(args: Vec) -> Result<(), ExitCode> { let app = build_args(); match app.try_get_matches_from(args) { - Ok(ref matches) => { + Ok(results) => handle_matches(&results).await, + + Err(ref err) => match err.kind() { + ErrorKind::DisplayHelp => { + println!("{}", err); + Ok(()) + } + ErrorKind::DisplayVersion => { + print_version(); + println!(); + Ok(()) + } + _ => err.exit(), + }, + } +} + +/// Handles the command line arguments and runs the stub server accordingly. +/// +/// Used by library consumers. Creates a new Tokio runtime, handle the matches +/// and starts the server. +pub fn process_stub_command(args: &ArgMatches) -> Result<(), ExitCode> { + tokio::runtime::Runtime::new().unwrap().block_on(async { + let res = handle_matches(args).await; + match res { + Ok(()) => Ok(()), + Err(code) => Err(code), + } + }) +} + +async fn handle_matches(matches: &ArgMatches) -> Result<(), ExitCode> { let level = matches.get_one::("loglevel").cloned() .unwrap_or_else(|| "info".to_string()); setup_logger(level.as_str()); @@ -246,25 +275,11 @@ async fn handle_command_args(args: Vec) -> Result<(), ExitCode> { server_handler.start_server(port) }).await.unwrap() } - }, - Err(ref err) => { - match err.kind() { - ErrorKind::DisplayHelp => { - println!("{}", err); - Ok(()) - }, - ErrorKind::DisplayVersion => { - print_version(); - println!(); - Ok(()) - }, - _ => err.exit() - } - } - } } -fn build_args() -> Command { +/// Creates a new clap Command instance with the command line arguments for the stub server. +/// This function defines the command line interface for the stub server, including options for logging, pact file sources, and server configuration. +pub fn build_args() -> Command { command!() .about(format!("Pact Stub Server {}", crate_version!())) .arg_required_else_help(true) From a36631cb37604d6447983e14ee09535dbd016969 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Sat, 27 Sep 2025 11:50:22 +0100 Subject: [PATCH 2/9] ci: switch windows-2019 to windows-latest --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 950d4fc..552b19b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -20,7 +20,7 @@ jobs: include: - operating-system: ubuntu-22.04 targets: x86_64-unknown-linux-gnu,x86_64-unknown-linux-musl,aarch64-unknown-linux-gnu,aarch64-unknown-linux-musl - - operating-system: windows-2019 + - operating-system: windows-latest targets: aarch64-pc-windows-msvc,x86_64-pc-windows-msvc - operating-system: macos-13 targets: aarch64-apple-darwin,x86_64-apple-darwin From a4f02ae0cb9e18f01a27f469c1d85371684d07d9 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Wed, 8 Oct 2025 22:19:35 +0100 Subject: [PATCH 3/9] chore: rename main.rs to lib.rs to align with rust conventions --- Cargo.toml | 2 +- src/{main.rs => lib.rs} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename src/{main.rs => lib.rs} (100%) diff --git a/Cargo.toml b/Cargo.toml index 10516ce..262fa69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,4 +62,4 @@ name = "pact-stub-server" path = "src/bin.rs" [lib] name = "pact_stub_server_cli" -path = "src/main.rs" \ No newline at end of file +path = "src/lib.rs" \ No newline at end of file diff --git a/src/main.rs b/src/lib.rs similarity index 100% rename from src/main.rs rename to src/lib.rs From eebe3fe0bddd2f213c25bf1e4ff8749497a4d428 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Thu, 23 Oct 2025 23:35:30 +0100 Subject: [PATCH 4/9] feat: update to hyper 1.x / latest tower --- Cargo.lock | 398 ++++++++++++++++++++------------------------ Cargo.toml | 28 ++-- src/pact_support.rs | 22 ++- src/server.rs | 144 ++++++++++------ 4 files changed, 301 insertions(+), 291 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fd75387..59142f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -128,9 +128,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "arbitrary" @@ -222,8 +222,8 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "itoa", "matchit", @@ -248,8 +248,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", @@ -274,12 +274,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -412,7 +406,7 @@ dependencies = [ "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.1.1", ] [[package]] @@ -507,6 +501,16 @@ dependencies = [ "memchr", ] +[[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" version = "0.10.0" @@ -1000,25 +1004,6 @@ dependencies = [ "libc", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.9.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "h2" version = "0.4.10" @@ -1030,7 +1015,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.3.1", + "http", "indexmap 2.9.0", "slab", "tokio", @@ -1105,17 +1090,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.3.1" @@ -1127,17 +1101,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -1145,7 +1108,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.3.1", + "http", ] [[package]] @@ -1156,16 +1119,17 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", + "tokio", ] [[package]] name = "http-range-header" -version = "0.3.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add0ab9360ddbd88cfeb3bd9574a1d85cfdfa14db10b3e21d3700dbc4328758f" +checksum = "9171a2ea8a68358193d15dd5d70c1c10a2afc3e7e4c5bc92bc9f025cebd7359c" [[package]] name = "httparse" @@ -1197,44 +1161,22 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.32" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.5.9", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" -dependencies = [ - "bytes", - "futures-channel", - "futures-util", - "h2 0.4.10", - "http 1.3.1", - "http-body 1.0.1", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -1247,8 +1189,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 1.3.1", - "hyper 1.6.0", + "http", + "hyper", "hyper-util", "rustls", "rustls-native-certs", @@ -1264,7 +1206,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" dependencies = [ - "hyper 1.6.0", + "hyper", "hyper-util", "pin-project-lite", "tokio", @@ -1273,25 +1215,28 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.12" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9f1e950e0d9d1d3c47184416723cf29c0d1f93bd8cccf37e4beb6b44f31710" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "futures-channel", + "futures-core", "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "hyper 1.6.0", + "http", + "http-body", + "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.5.9", + "socket2 0.6.0", + "system-configuration", "tokio", "tower-service", "tracing", + "windows-registry", ] [[package]] @@ -1508,17 +1453,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags", - "cfg-if", - "libc", -] - [[package]] name = "ipnet" version = "2.11.0" @@ -1649,9 +1583,9 @@ checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] name = "libmimalloc-sys" -version = "0.1.42" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec9d6fac27761dabcd4ee73571cdb06b7022dc99089acbe5435691edffaac0f4" +checksum = "667f4fec20f29dfc6bc7357c582d91796c169ad7e2fce709468aefeb2c099870" dependencies = [ "cc", "libc", @@ -1716,7 +1650,7 @@ dependencies = [ "lazy_static", "proc-macro2", "quote", - "regex-syntax 0.8.5", + "regex-syntax", "rustc_version", "syn", ] @@ -1765,11 +1699,11 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] @@ -1792,9 +1726,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mimalloc" -version = "0.1.46" +version = "0.1.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "995942f432bbb4822a7e9c3faa87a695185b0d09273ba85f097b54f4e458f2af" +checksum = "e1ee66a4b64c74f4ef288bcbb9192ad9c3feaad75193129ac8509af543894fd8" dependencies = [ "libmimalloc-sys", ] @@ -1850,7 +1784,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.3.1", + "http", "httparse", "memchr", "mime", @@ -1893,12 +1827,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "overload", - "winapi", + "windows-sys 0.61.2", ] [[package]] @@ -1986,12 +1919,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "pact-plugin-driver" version = "0.7.4" @@ -2039,13 +1966,17 @@ name = "pact-stub-server" version = "0.6.3" dependencies = [ "anyhow", - "base64 0.21.7", + "base64", + "bytes", "clap", "env_logger 0.10.2", "expectest", "futures", - "http 0.2.12", - "hyper 0.14.32", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", "itertools 0.10.5", "maplit", "mimalloc", @@ -2060,8 +1991,8 @@ dependencies = [ "serde_json", "test-log", "tokio", - "tower 0.4.13", - "tower-http 0.4.4", + "tower 0.5.2", + "tower-http", "tower-service", "tracing", "tracing-core", @@ -2077,13 +2008,13 @@ checksum = "53025d152918a0dbe4df6c7773b20ecb098e90f49e7a720ff91e490ba761fd3a" dependencies = [ "ansi_term", "anyhow", - "base64 0.22.1", + "base64", "bytes", "chrono", "difference", "futures", "hex", - "http 1.3.1", + "http", "infer", "itertools 0.14.0", "kiss_xml", @@ -2119,7 +2050,7 @@ checksum = "cca6eddec95d1d3ef89449ab28c5d5c966e848897717007813845b7e909d56a2" dependencies = [ "anyhow", "ariadne", - "base64 0.22.1", + "base64", "bytes", "chrono", "chrono-tz", @@ -2141,7 +2072,7 @@ dependencies = [ "rand 0.8.5", "rand_regex", "regex", - "regex-syntax 0.8.5", + "regex-syntax", "reqwest", "semver", "serde", @@ -2161,11 +2092,11 @@ dependencies = [ "ansi_term", "anyhow", "async-trait", - "base64 0.22.1", + "base64", "bytes", "difference", "futures", - "http 1.3.1", + "http", "humantime", "itertools 0.14.0", "lazy_static", @@ -2584,7 +2515,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bfbd599a8c757f89100e3ae559fb1ef9efa1cfd9276136862e3089dec627b31" dependencies = [ "rand 0.8.5", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] @@ -2618,42 +2549,27 @@ dependencies = [ [[package]] name = "regex" -version = "1.11.1" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.5" @@ -2667,15 +2583,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "async-compression", - "base64 0.22.1", + "base64", "bytes", "futures-channel", "futures-core", "futures-util", - "http 1.3.1", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", - "hyper 1.6.0", + "hyper", "hyper-rustls", "hyper-util", "js-sys", @@ -2694,7 +2610,7 @@ dependencies = [ "tokio-rustls", "tokio-util", "tower 0.5.2", - "tower-http 0.6.6", + "tower-http", "tower-service", "url", "wasm-bindgen", @@ -2841,7 +2757,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ "bitflags", - "core-foundation", + "core-foundation 0.10.0", "core-foundation-sys", "libc", "security-framework-sys", @@ -2865,18 +2781,28 @@ checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -2885,14 +2811,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] @@ -2922,7 +2849,7 @@ version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" dependencies = [ - "base64 0.22.1", + "base64", "chrono", "hex", "indexmap 1.9.3", @@ -3178,6 +3105,27 @@ dependencies = [ "windows", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tar" version = "0.4.44" @@ -3321,29 +3269,26 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", "socket2 0.6.0", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", @@ -3434,13 +3379,13 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.22.1", + "base64", "bytes", - "h2 0.4.10", - "http 1.3.1", - "http-body 1.0.1", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.6.0", + "hyper", "hyper-timeout", "hyper-util", "percent-encoding", @@ -3477,7 +3422,6 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "hdrhistogram", "indexmap 1.9.3", "pin-project", "pin-project-lite", @@ -3498,27 +3442,33 @@ checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", + "hdrhistogram", + "indexmap 2.9.0", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", + "tracing", ] [[package]] name = "tower-http" -version = "0.4.4" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c5bb1d698276a2443e5ecfabc1008bf15a36c12e6a7176e7bf089ea9131140" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "async-compression", - "base64 0.21.7", + "base64", "bitflags", "bytes", "futures-core", "futures-util", - "http 0.2.12", - "http-body 0.4.6", + "http", + "http-body", + "http-body-util", "http-range-header", "httpdate", "iri-string", @@ -3528,31 +3478,13 @@ dependencies = [ "pin-project-lite", "tokio", "tokio-util", - "tower 0.4.13", + "tower 0.5.2", "tower-layer", "tower-service", "tracing", "uuid", ] -[[package]] -name = "tower-http" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" -dependencies = [ - "bitflags", - "bytes", - "futures-util", - "http 1.3.1", - "http-body 1.0.1", - "iri-string", - "pin-project-lite", - "tower 0.5.2", - "tower-layer", - "tower-service", -] - [[package]] name = "tower-layer" version = "0.3.3" @@ -3611,14 +3543,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -3961,7 +3893,7 @@ checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.1.1", "windows-result", "windows-strings", ] @@ -3994,13 +3926,30 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" +dependencies = [ + "windows-link 0.1.1", + "windows-result", + "windows-strings", +] + [[package]] name = "windows-result" version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.1", ] [[package]] @@ -4009,7 +3958,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.1", ] [[package]] @@ -4030,6 +3979,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 262fa69..dac5153 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,28 +14,32 @@ exclude = [ ] [dependencies] -anyhow = "1.0.71" -base64 = "0.21.1" +anyhow = "1.0.100" +base64 = "0.22.1" clap = { version = "4.3.0", features = ["env", "std", "cargo"] } futures = "0.3.28" -http = "0.2.9" -hyper = { version = "0.14.26", features = ["full"] } +http = "1.3.1" +hyper = { version = "1.7.0", features = ["full"] } +hyper-util = {version = "0.1.17", features = ["full"] } itertools = "0.10.5" maplit = "1.0.2" -mimalloc = "0.1.46" +mimalloc = "0.1.48" pact_matching = "~2.0.1" pact_verifier = "1.3.3" pact_models = "1.3.7" -regex = "1.8.2" +regex = "1.12.2" reqwest = { version = "0.12.12", default-features = false, features = ["json", "rustls-tls-native-roots"] } -serde = "1.0.163" -serde_json = "1.0.96" -tokio = { version = "1.28.1", features = ["full"] } -tower = { version = "0.4.13", features = [ "full" ] } -tower-service = { version = "0.3.2" } -tower-http = { version = "0.4.0", features = [ "full" ] } +serde = "1.0.228" +serde_json = "1.0.145" +tokio = { version = "1.48.0", features = ["full"] } +tower = { version = "0.5.2", features = [ "full" ] } +tower-service = { version = "0.3.3" } +tower-http = { version = "0.6.6", features = [ "full" ] } tracing = { version = "0.1.37", features = [ "log" ] } tracing-core = "0.1.31" +http-body-util = { version = "0.1.3", features = ["full"] } +http-body = "1.0.1" +bytes = "1" tracing-subscriber = "0.3.17" [dev-dependencies] diff --git a/src/pact_support.rs b/src/pact_support.rs index 8c7e1d3..e2bcb40 100644 --- a/src/pact_support.rs +++ b/src/pact_support.rs @@ -1,10 +1,13 @@ use std::collections::HashMap; +use std::convert::Infallible; use http::{Error, HeaderMap, Uri}; use http::header::{ACCESS_CONTROL_ALLOW_ORIGIN, CONTENT_TYPE}; use http::header::HeaderValue; use http::request::Parts; -use hyper::{Body, Response as HyperResponse}; +use http_body_util::BodyExt; +use hyper::{Response as HyperResponse}; +use hyper::body::Bytes; use pact_models::content_types::TEXT; use pact_models::http_parts::HttpPart; use pact_models::prelude::*; @@ -12,6 +15,8 @@ use pact_models::query_strings::parse_query_string; use pact_models::v4::http_parts::{HttpRequest, HttpResponse}; use tracing::{debug, info, warn}; +type BoxBody = http_body_util::combinators::BoxBody; + fn extract_query_string(uri: &Uri) -> Option>>> { match uri.query() { Some(q) => parse_query_string(q), @@ -54,7 +59,7 @@ pub fn hyper_request_to_pact_request(req: Parts, body: OptionalBody) -> HttpRequ } } -pub fn pact_response_to_hyper_response(response: &HttpResponse) -> Result, Error> { +pub fn pact_response_to_hyper_response(response: &HttpResponse) -> Result, Error>{ info!("<=== Sending {}", response); debug!(" body: '{}'", response.body.display_string()); debug!(" matching_rules: {:?}", response.matching_rules); @@ -82,9 +87,16 @@ pub fn pact_response_to_hyper_response(response: &HttpResponse) -> Result res.body(Body::empty()) + let body_bytes = Bytes::copy_from_slice(body); + let box_body = http_body_util::Full::from(body_bytes).boxed(); + res.body(box_body) + .map_err(|e| Error::from(e)) + }, + _ => { + let box_body = http_body_util::Full::from(Bytes::new()).boxed(); + res.body(box_body) + .map_err(|e| Error::from(e)) + } } } diff --git a/src/server.rs b/src/server.rs index aba10fa..abbb6dd 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,4 +1,4 @@ -use std::future::{Ready, ready}; +use std::convert::Infallible; use std::pin::Pin; use std::process::ExitCode; use std::sync::Arc; @@ -8,9 +8,10 @@ use futures::executor::block_on; use futures::future::Future; use futures::stream::StreamExt; use futures::task::{Context, Poll}; -use http::{Error, StatusCode}; -use hyper::{Body, Request as HyperRequest, Response as HyperResponse, Server}; -use hyper::server::conn::AddrStream; +use http::{StatusCode}; +use http_body_util::BodyExt; +use hyper::{Request as HyperRequest}; +use hyper::body::{Bytes, Incoming}; use itertools::Itertools; use maplit::hashmap; use pact_matching::{CoreMatchingContext, DiffConfig, Mismatch}; @@ -20,11 +21,19 @@ use pact_models::prelude::v4::*; use pact_models::v4::http_parts::{HttpRequest, HttpResponse}; use pact_models::v4::V4InteractionType; use regex::Regex; -use tower::ServiceBuilder; use tower_http::classify::{ServerErrorsAsFailures, SharedClassifier}; use tower_http::trace::{DefaultMakeSpan, Trace, TraceLayer}; use tower_service::Service; use tracing::{debug, error, info, warn}; +use hyper::Response; +use hyper_util::{ + rt::{TokioExecutor, TokioIo}, + server::conn::auto::Builder, +}; +use tokio::net::TcpListener; +use tower::Layer; + +type BoxBody = http_body_util::combinators::BoxBody; use crate::{pact_support, PactSource}; @@ -42,35 +51,23 @@ pub struct Shared { empty_provider_states: bool } -#[derive(Clone)] -struct ServerHandlerFactory { - inner: ServerHandler -} - -impl ServerHandlerFactory { - pub fn new(handler: ServerHandler) -> Self { - ServerHandlerFactory { - inner: handler, - } +pub trait TraceLayerProvider { + fn create_trace_layer() -> TraceLayer> { + TraceLayer::new_for_http() + .make_span_with(DefaultMakeSpan::new().include_headers(true)) } } -impl Service<&AddrStream> for ServerHandlerFactory { - type Response = Trace>; - type Error = anyhow::Error; - type Future = Ready>; +#[derive(Clone)] +pub struct ServerHandlerLayer; - fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll> { - Poll::Ready(Ok(())) - } +impl TraceLayerProvider for ServerHandlerLayer {} + +impl Layer for ServerHandlerLayer { + type Service = Trace>; - fn call(&mut self, req: &AddrStream) -> Self::Future { - debug!("Accepting a new connection from {}", req.remote_addr()); - let service = ServiceBuilder::new() - .layer(TraceLayer::new_for_http() - .make_span_with(DefaultMakeSpan::new().include_headers(true))) - .service(self.inner.clone()); - ready(Ok(service)) + fn layer(&self, inner: S) -> Self::Service { + Self::create_trace_layer().layer(inner) } } @@ -82,7 +79,7 @@ impl ServerHandler { provider_state: Option, provider_state_header_name: Option, empty_provider_states: bool - ) -> ServerHandler { + ) -> ServerHandler { ServerHandler { shared: Arc::new(Shared { sources, @@ -96,35 +93,68 @@ impl ServerHandler { } pub fn start_server(self, port: u16) -> Result<(), ExitCode> { - let addr = ([0, 0, 0, 0], port).into(); - match Server::try_bind(&addr) { - Ok(builder) => { - let server = builder.serve(ServerHandlerFactory::new(self)); - info!("Server started on port {}", server.local_addr().port()); - block_on(server).map_err(|err| { - error!("error occurred scheduling server future on Tokio runtime: {}", err); - ExitCode::from(2) - })?; - Ok(()) - }, - Err(err) => { - error!("could not start server: {}", err); - Err(ExitCode::FAILURE) + let addr = std::net::SocketAddrV4::new(std::net::Ipv4Addr::new(0, 0, 0, 0), port); + let addr = std::net::SocketAddr::V4(addr); + + let handler = self.clone(); + + block_on(async move { + let listener = match TcpListener::bind(addr).await { + Ok(l) => l, + Err(err) => { + error!("tcp listener failed to bind address: {}", err); + return Err(ExitCode::FAILURE); + } + }; + + let local_addr = match listener.local_addr() { + Ok(a) => a, + Err(err) => { + error!("failed to get local address: {}", err); + return Err(ExitCode::FAILURE); + } + }; + info!("Server started on port {}", local_addr.port()); + + loop { + match listener.accept().await { + Ok((stream, _)) => { + let handler = handler.clone(); + tokio::spawn(async move { + let io = TokioIo::new(stream); + let handler = handler.clone(); + let tower_service = tower::ServiceBuilder::new() + .layer(ServerHandlerLayer) + .service(handler); + let hyper_service = hyper_util::service::TowerToHyperService::new(tower_service); + if let Err(err) = Builder::new(TokioExecutor::new()) + .serve_connection(io, hyper_service) + .await + { + error!("error serving connection: {:?}", err); + } + }); + } + Err(err) => { + error!("tcp listener failed to accept: {}", err); + return Err(ExitCode::FAILURE); + } + } } - } + }) } } -impl Service> for ServerHandler { - type Response = HyperResponse; - type Error = Error; +impl Service> for ServerHandler { + type Response = Response; + type Error = hyper::http::Error; type Future = Pin> + Send>>; fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll> { Poll::Ready(Ok(())) } - fn call(&mut self, req: HyperRequest) -> Self::Future { + fn call(&mut self, req: HyperRequest) -> Self::Future { let shared = self.shared.as_ref(); let auto_cors = shared.auto_cors; let cors_referer = shared.cors_referer; @@ -147,12 +177,18 @@ impl Service> for ServerHandler { None => provider_state }; - let bytes = hyper::body::to_bytes(body).await; + let bytes = body + .boxed() + .collect() + .await; let body = match bytes { - Ok(contents) => if contents.is_empty() { - OptionalBody::Empty - } else { - OptionalBody::Present(contents, None, None) + Ok(contents) => { + let bytes = contents.to_bytes(); + if bytes.is_empty() { + OptionalBody::Empty + } else { + OptionalBody::Present(bytes, None, None) + } }, Err(err) => { warn!("Failed to read request body: {}", err); From 45e0056e23463a5f747f2f908598e3435b217b28 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Thu, 23 Oct 2025 23:43:13 +0100 Subject: [PATCH 5/9] feat: add file watch mode for hot reloading pact files/dirs --- Cargo.lock | 212 +++++++++++++++++++++++++++++++++++++----- Cargo.toml | 2 + README.md | 44 +++++++++ scripts/test_watch.sh | 58 ++++++++++++ src/lib.rs | 152 +++++++++++++++++++++++++++--- src/server.rs | 51 +++++++++- tests/cmd/main.stderr | 2 + 7 files changed, 485 insertions(+), 36 deletions(-) create mode 100755 scripts/test_watch.sh diff --git a/Cargo.lock b/Cargo.lock index 59142f4..c110bb8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,7 +271,7 @@ dependencies = [ "miniz_oxide", "object", "rustc-demangle", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -286,6 +286,12 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.9.1" @@ -848,6 +854,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + [[package]] name = "futures" version = "0.3.31" @@ -1444,6 +1459,26 @@ dependencies = [ "cfb", ] +[[package]] +name = "inotify" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" +dependencies = [ + "bitflags 2.9.1", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "inout" version = "0.1.4" @@ -1540,6 +1575,26 @@ dependencies = [ "regex", ] +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1597,7 +1652,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags", + "bitflags 2.9.1", "libc", "redox_syscall", ] @@ -1771,6 +1826,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", + "log", "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -1816,6 +1872,42 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.9.1", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-debouncer-mini" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17849edfaabd9a5fef1c606d99cfc615a8e99f7ac4366406d86c7942a3184cf2" +dependencies = [ + "log", + "notify", + "notify-types", + "tempfile", +] + +[[package]] +name = "notify-types" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" + [[package]] name = "ntapi" version = "0.4.1" @@ -1876,7 +1968,7 @@ version = "6.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" dependencies = [ - "bitflags", + "bitflags 2.9.1", "libc", "once_cell", "onig_sys", @@ -1980,6 +2072,8 @@ dependencies = [ "itertools 0.10.5", "maplit", "mimalloc", + "notify", + "notify-debouncer-mini", "pact_matching", "pact_models", "pact_verifier", @@ -2138,7 +2232,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -2544,7 +2638,7 @@ version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags", + "bitflags 2.9.1", ] [[package]] @@ -2660,7 +2754,7 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" dependencies = [ - "bitflags", + "bitflags 2.9.1", "errno", "libc", "linux-raw-sys", @@ -2756,7 +2850,7 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" dependencies = [ - "bitflags", + "bitflags 2.9.1", "core-foundation 0.10.0", "core-foundation-sys", "libc", @@ -3111,7 +3205,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags", + "bitflags 2.9.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -3462,7 +3556,7 @@ checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ "async-compression", "base64", - "bitflags", + "bitflags 2.9.1", "bytes", "futures-core", "futures-util", @@ -3873,7 +3967,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" dependencies = [ "windows-core 0.52.0", - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3882,7 +3976,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3967,7 +4061,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3976,7 +4070,16 @@ version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", ] [[package]] @@ -3994,14 +4097,31 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -4010,48 +4130,96 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + [[package]] name = "winnow" version = "0.7.10" @@ -4067,7 +4235,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags", + "bitflags 2.9.1", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index dac5153..45ea372 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,8 @@ http-body-util = { version = "0.1.3", features = ["full"] } http-body = "1.0.1" bytes = "1" tracing-subscriber = "0.3.17" +notify = "8.2.0" +notify-debouncer-mini = "0.7.0" [dev-dependencies] expectest = "0.12.0" diff --git a/README.md b/README.md index 384f08e..28a230d 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,50 @@ The running server can be controlled with the following options: |--------|-------------| | `-p, --port ` | The port to bind to. If not specified, a random port will be allocated by the operating system. | +### Watch mode + +The Pact Stub Server now supports a watch mode that automatically monitors pact files and directories for changes and reloads them without restarting the server. This feature is particularly useful during development when pact files are frequently updated. + +#### Usage + +To enable watch mode, use the `--watch` or `-w` flag: + +```bash +# Watch a single pact file +pact-stub-server --file path/to/pact.json --watch --port 8080 + +# Watch a directory of pact files +pact-stub-server --dir path/to/pacts --watch --port 8080 + +# Watch multiple files and directories +pact-stub-server --file pact1.json --file pact2.json --dir pacts_dir --watch --port 8080 +``` + +#### Supported Source Types + +Watch mode supports the following pact source types: + +- **File sources** (`--file`): Individual pact files are monitored for changes +- **Directory sources** (`--dir`): Entire directories are monitored recursively for changes to pact files + +**Note**: URL sources (`--url`) and Pact Broker sources (`--broker-url`) are not supported for watching as they are remote resources. + +#### File System Events + +The watcher responds to the following types of file system events: + +- File modifications (content changes) +- File creation (new pact files added) +- File deletion (pact files removed) +- Directory changes (new files added to watched directories) + +#### Limitations + +- Only works with local file and directory sources +- URL and Pact Broker sources cannot be watched +- Requires file system notifications to be available on the host system +- Memory usage may be slightly higher due to shared state management + ## Docker ### Usage diff --git a/scripts/test_watch.sh b/scripts/test_watch.sh new file mode 100755 index 0000000..58e2f5a --- /dev/null +++ b/scripts/test_watch.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +# Test script for the watch mode functionality + +echo "Testing Pact Stub Server Watch Mode" +echo "====================================" + +# Create a temporary test pact file +TEST_PACT_FILE="/tmp/test-watch-pact.json" +cat > "$TEST_PACT_FILE" << 'EOF' +{ + "consumer": { + "name": "test-consumer" + }, + "provider": { + "name": "test-provider" + }, + "interactions": [ + { + "description": "A test interaction", + "request": { + "method": "GET", + "path": "/test" + }, + "response": { + "status": 200, + "headers": { + "Content-Type": "application/json" + }, + "body": { + "message": "Hello from initial pact!" + } + } + } + ], + "metadata": { + "pactSpecification": { + "version": "2.0.0" + } + } +} +EOF + +echo "Created test pact file at: $TEST_PACT_FILE" +echo "" +echo "Starting pact-stub-server in watch mode..." +echo "The server will watch for changes to the pact file and reload automatically." +echo "" +echo "To test:" +echo "1. Start the server (this script will start it)" +echo "2. In another terminal, run: curl http://localhost:8080/test" +echo "3. Modify the pact file and curl again to see changes" +echo "" +echo "Press Ctrl+C to stop the server" +echo "" + +# Run the pact-stub-server with watch mode +cargo run --bin pact-stub-server -- --file "$TEST_PACT_FILE" --port 8080 --watch --loglevel info \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index eb7bbff..9767fd0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,19 +87,121 @@ use std::env; use std::process::ExitCode; use std::str::FromStr; +use std::path::PathBuf; +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use std::sync::mpsc::channel; use clap::{Command, Arg, ArgMatches, ArgAction, command, crate_version}; use clap::error::ErrorKind; use mimalloc::MiMalloc; use pact_models::prelude::*; +use pact_models::prelude::v4::*; use regex::Regex; use tracing::{debug, error, info, warn}; use tracing_core::LevelFilter; use tracing_subscriber::FmtSubscriber; +use tokio::sync::broadcast; +use notify::RecursiveMode; +use notify_debouncer_mini::{DebouncedEventKind, new_debouncer}; use crate::loading::load_pacts; use crate::server::ServerHandler; +/// Setup file watcher for watch mode +fn setup_file_watcher( + sources: Vec, + matches: &ArgMatches, + shared_pacts: Arc>>, + reload_tx: broadcast::Sender<()> +) { + let watch_paths = get_watch_paths(&sources); + if watch_paths.is_empty() { + warn!("No file or directory sources found for watching"); + return; + } + + let insecure_tls = matches.get_flag("insecure-tls"); + let ext = matches.get_one::("ext").cloned(); + + std::thread::spawn(move || { + let (debounce_tx, debounce_rx) = channel(); + let mut debouncer = match new_debouncer(Duration::from_secs(1), debounce_tx) { + Ok(debouncer) => debouncer, + Err(e) => { + error!("Failed to create file debouncer: {}", e); + return; + } + }; + + // Watch all file and directory sources + for path in &watch_paths { + if let Err(e) = debouncer.watcher().watch(path, RecursiveMode::Recursive) { + error!("Failed to watch path {:?}: {}", path, e); + } else { + info!("Watching for changes in: {:?}", path); + } + } + + let runtime = tokio::runtime::Runtime::new().unwrap(); + + loop { + match debounce_rx.recv() { + Ok(Ok(events)) => { + for event in events.iter() { + match &event.kind { + DebouncedEventKind::Any => { + info!("File change detected in watched directory"); + + // Reload pacts + let pacts_result = runtime.block_on(load_pacts(sources.clone(), insecure_tls, ext.as_ref())); + if pacts_result.iter().any(|p| p.is_err()) { + error!("Error reloading pacts:"); + for error in pacts_result.iter().filter_map(|p| p.as_ref().err()) { + error!(" - {}", error); + } + } else { + let new_pacts = pacts_result.iter() + .filter_map(|result| result.as_ref().ok()) + .map(|(p, s)| (p.as_v4_pact().unwrap(), s.clone())) + .collect::>(); + + let interactions: usize = new_pacts.iter().map(|(p, _)| p.interactions.len()).sum(); + info!("Reloaded {} pacts ({} total interactions)", new_pacts.len(), interactions); + + *shared_pacts.lock().unwrap() = new_pacts; + let _ = reload_tx.send(()); + } + break; + } + _ => {} + } + } + } + Ok(Err(e)) => { + error!("Watch error: {:?}", e); + break; + } + Err(e) => { + error!("Debouncer channel error: {:?}", e); + break; + } + } + } + }); +} + +/// Extract file and directory paths from pact sources for watching +fn get_watch_paths(sources: &[PactSource]) -> Vec { + sources.iter() + .filter_map(|source| match source { + PactSource::File(path) => Some(PathBuf::from(path)), + PactSource::Dir(path) => Some(PathBuf::from(path)), + _ => None, // URLs and Broker sources are not watchable + }) + .collect() +} + mod pact_support; mod server; mod loading; @@ -234,8 +336,9 @@ async fn handle_matches(matches: &ArgMatches) -> Result<(), ExitCode> { .unwrap_or_else(|| "info".to_string()); setup_logger(level.as_str()); let sources = pact_source(matches); + let watch_mode = matches.get_flag("watch"); - let pacts = load_pacts(sources, matches.get_flag("insecure-tls"), + let pacts = load_pacts(sources.clone(), matches.get_flag("insecure-tls"), matches.get_one("ext")).await; if pacts.iter().any(|p| p.is_err()) { error!("There were errors loading the pact files."); @@ -264,16 +367,38 @@ async fn handle_matches(matches: &ArgMatches) -> Result<(), ExitCode> { info!("Loaded {} pacts ({} total interactions)", pacts.len(), interactions); let auto_cors = matches.get_flag("cors"); let referer = matches.get_flag("cors-referer"); - let server_handler = ServerHandler::new( - pacts, - auto_cors, - referer, - provider_state, - provider_state_header_name, - empty_provider_states); - tokio::task::spawn_blocking(move || { - server_handler.start_server(port) - }).await.unwrap() + + if watch_mode { + // Setup shared state for pacts when in watch mode + let shared_pacts = Arc::new(Mutex::new(pacts.clone())); + let (reload_tx, reload_rx) = broadcast::channel::<()>(1); + + // Setup file watching if in watch mode + setup_file_watcher(sources, matches, shared_pacts.clone(), reload_tx.clone()); + + let server_handler = ServerHandler::new_with_watch( + shared_pacts, + reload_tx, + auto_cors, + referer, + provider_state, + provider_state_header_name, + empty_provider_states); + tokio::task::spawn_blocking(move || { + server_handler.start_server(port) + }).await.unwrap() + } else { + let server_handler = ServerHandler::new( + pacts, + auto_cors, + referer, + provider_state, + provider_state_header_name, + empty_provider_states); + tokio::task::spawn_blocking(move || { + server_handler.start_server(port) + }).await.unwrap() + } } } @@ -384,6 +509,11 @@ pub fn build_args() -> Command { .action(ArgAction::Append) .value_parser(regex_value) .help("Provider name or regex to use to filter the Pacts fetched from the Pact broker (can be repeated)")) + .arg(Arg::new("watch") + .short('w') + .long("watch") + .action(ArgAction::SetTrue) + .help("Watch for changes in pact files and reload automatically")) .arg(Arg::new("version") .short('v') .long("version") diff --git a/src/server.rs b/src/server.rs index abbb6dd..9dabcc1 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,7 +1,8 @@ use std::convert::Infallible; use std::pin::Pin; use std::process::ExitCode; -use std::sync::Arc; +use std::sync::{Arc, Mutex}; +use tokio::sync::broadcast; use anyhow::anyhow; use futures::executor::block_on; @@ -44,6 +45,8 @@ pub struct ServerHandler { pub struct Shared { sources: Vec<(V4Pact, PactSource)>, + shared_sources: Option>>>, + reload_tx: Option>, auto_cors: bool, cors_referer: bool, provider_state: Option, @@ -83,6 +86,31 @@ impl ServerHandler { ServerHandler { shared: Arc::new(Shared { sources, + shared_sources: None, + reload_tx: None, + auto_cors, + cors_referer, + provider_state, + provider_state_header_name, + empty_provider_states + }) + } + } + + pub fn new_with_watch( + shared_sources: Arc>>, + reload_tx: broadcast::Sender<()>, + auto_cors: bool, + cors_referer: bool, + provider_state: Option, + provider_state_header_name: Option, + empty_provider_states: bool + ) -> ServerHandler { + ServerHandler { + shared: Arc::new(Shared { + sources: vec![], // Empty since we use shared_sources + shared_sources: Some(shared_sources), + reload_tx: Some(reload_tx), auto_cors, cors_referer, provider_state, @@ -97,7 +125,17 @@ impl ServerHandler { let addr = std::net::SocketAddr::V4(addr); let handler = self.clone(); - + + // Start reload listener if in watch mode + if let Some(reload_tx) = &handler.shared.reload_tx { + let mut reload_rx = reload_tx.subscribe(); + tokio::spawn(async move { + while let Ok(_) = reload_rx.recv().await { + info!("Pacts reloaded - server will use updated pacts for new requests"); + } + }); + } + block_on(async move { let listener = match TcpListener::bind(addr).await { Ok(l) => l, @@ -158,7 +196,14 @@ impl Service> for ServerHandler { let shared = self.shared.as_ref(); let auto_cors = shared.auto_cors; let cors_referer = shared.cors_referer; - let sources = shared.sources.clone(); + + // Use shared sources if in watch mode, otherwise use static sources + let sources = if let Some(shared_sources) = &shared.shared_sources { + shared_sources.lock().unwrap().clone() + } else { + shared.sources.clone() + }; + let provider_state = shared.provider_state.clone(); let provider_state_header_name = shared.provider_state_header_name.clone(); let empty_provider_states = shared.empty_provider_states; diff --git a/tests/cmd/main.stderr b/tests/cmd/main.stderr index 2d6970b..f277957 100644 --- a/tests/cmd/main.stderr +++ b/tests/cmd/main.stderr @@ -37,6 +37,8 @@ Options: Consumer name or regex to use to filter the Pacts fetched from the Pact broker (can be repeated) --provider-name Provider name or regex to use to filter the Pacts fetched from the Pact broker (can be repeated) + -w, --watch + Watch for changes in pact files and reload automatically -v, --version Print version information -h, --help From 793fa3b6211d73f2c46ae38496bd0410c2f4ff14 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 24 Oct 2025 13:52:17 +0100 Subject: [PATCH 6/9] chore(ci): fix release workflow (macos) --- .github/workflows/release.yml | 2 +- release.sh | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 552b19b..bf75163 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ jobs: targets: x86_64-unknown-linux-gnu,x86_64-unknown-linux-musl,aarch64-unknown-linux-gnu,aarch64-unknown-linux-musl - operating-system: windows-latest targets: aarch64-pc-windows-msvc,x86_64-pc-windows-msvc - - operating-system: macos-13 + - operating-system: macos-latest targets: aarch64-apple-darwin,x86_64-apple-darwin fail-fast: false steps: diff --git a/release.sh b/release.sh index f13bec6..39f614a 100755 --- a/release.sh +++ b/release.sh @@ -52,6 +52,7 @@ build_linux_aarch64() { } # Build the x86_64 darwin release build_macos_x86_64() { + export MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET:-12} cargo build --target x86_64-apple-darwin "${cargo_flags[@]}" if [[ "${cargo_flags[*]}" =~ "--release" ]]; then @@ -66,6 +67,7 @@ build_macos_x86_64() { # Build the aarch64 darwin release build_macos_aarch64() { + export MACOSX_DEPLOYMENT_TARGET=${MACOSX_DEPLOYMENT_TARGET:-12} cargo build --target aarch64-apple-darwin "${cargo_flags[@]}" if [[ "${cargo_flags[*]}" =~ "--release" ]]; then From 1ddbc5471037e46122404d50119f9f65e158e61d Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 24 Oct 2025 13:53:26 +0100 Subject: [PATCH 7/9] chore(ci): fix release workflow (macos) --- README.md | 4 ++-- release.sh | 6 ------ scripts/install-stub-server-cli.sh | 6 +++--- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 28a230d..93f0282 100644 --- a/README.md +++ b/README.md @@ -228,10 +228,10 @@ Docker images are published to multiple registries | OS | Architecture | Supported | Pact Stub Server Version | | ------- | ------------ | --------- | ---------------- | -| OSX | x86_64 | ✅ | All | +| MacOS | x86_64 | ✅ | All | | Linux | x86_64 | ✅ | All | | Windows | x86_64 | ✅ | All | -| OSX | arm64 | ✅ | >=0.5.2 | +| MacOS | arm64 | ✅ | >=0.5.2 | | Linux | arm64 | ✅ | >=0.5.2 | | Windows | arm64 | ✅ | >=0.6.0 | | Alpine | x86_64 | ✅ | >=0.6.0 | diff --git a/release.sh b/release.sh index 39f614a..9a64b71 100755 --- a/release.sh +++ b/release.sh @@ -56,9 +56,6 @@ build_macos_x86_64() { cargo build --target x86_64-apple-darwin "${cargo_flags[@]}" if [[ "${cargo_flags[*]}" =~ "--release" ]]; then - gzip_and_sum \ - "$CARGO_TARGET_DIR/x86_64-apple-darwin/release/${APP}" \ - "$ARTIFACTS_DIR/${APP}-osx-x86_64.gz" gzip_and_sum \ "$CARGO_TARGET_DIR/x86_64-apple-darwin/release/${APP}" \ "$ARTIFACTS_DIR/${APP}-macos-x86_64.gz" @@ -71,9 +68,6 @@ build_macos_aarch64() { cargo build --target aarch64-apple-darwin "${cargo_flags[@]}" if [[ "${cargo_flags[*]}" =~ "--release" ]]; then - gzip_and_sum \ - "$CARGO_TARGET_DIR/aarch64-apple-darwin/release/${APP}" \ - "$ARTIFACTS_DIR/${APP}-osx-aarch64.gz" gzip_and_sum \ "$CARGO_TARGET_DIR/aarch64-apple-darwin/release/${APP}" \ "$ARTIFACTS_DIR/${APP}-macos-aarch64.gz" diff --git a/scripts/install-stub-server-cli.sh b/scripts/install-stub-server-cli.sh index 42908b0..5ab299c 100755 --- a/scripts/install-stub-server-cli.sh +++ b/scripts/install-stub-server-cli.sh @@ -9,11 +9,11 @@ mkdir -p ~/.pact/bin case "$(uname -s)" in Darwin) - echo '== Installing stub server CLI for Mac OSX ==' + echo '== Installing stub server CLI for Mac OS ==' if [ "$(uname -m)" = "arm64" ]; then - curl -L -o ~/.pact/bin/pact-stub-server.gz https://github.com/pact-foundation/pact-stub-server/releases/download/v${VERSION}/pact-stub-server-osx-aarch64.gz + curl -L -o ~/.pact/bin/pact-stub-server.gz https://github.com/pact-foundation/pact-stub-server/releases/download/v${VERSION}/pact-stub-server-macos-aarch64.gz else - curl -L -o ~/.pact/bin/pact-stub-server.gz https://github.com/pact-foundation/pact-stub-server/releases/download/v${VERSION}/pact-stub-server-osx-x86_64.gz + curl -L -o ~/.pact/bin/pact-stub-server.gz https://github.com/pact-foundation/pact-stub-server/releases/download/v${VERSION}/pact-stub-server-macos-x86_64.gz fi gunzip -N -f ~/.pact/bin/pact-stub-server.gz chmod +x ~/.pact/bin/pact-stub-server From ac5344ed4b7df986f95fe898075c21e16e83cb65 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 24 Oct 2025 13:56:27 +0100 Subject: [PATCH 8/9] fix: remove pathname from release artifact shasum --- scripts/gzip-and-sum.sh | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scripts/gzip-and-sum.sh b/scripts/gzip-and-sum.sh index 42f1f1d..c7847b5 100644 --- a/scripts/gzip-and-sum.sh +++ b/scripts/gzip-and-sum.sh @@ -15,4 +15,11 @@ gzip_and_sum() { gzip --stdout --best "$orig_file" > "$target_file" openssl dgst -sha256 -r "$target_file" > "$digest_file" -} + basenamedir=$(basename $target_file) + if [[ "$(uname -s)" == "Darwin" ]]; then + # OSX requires an empty arg passed to -i, but this doesn't work on Lin/Win + sed -Ei '' "s|\*(.*)$|\*$basenamedir|" "$digest_file" + else + sed -Ei "s|\*(.*)$|\*$basenamedir|" "$digest_file" + fi +} \ No newline at end of file From 1c42707bf10589ef7c4a8e7425b18645f1880448 Mon Sep 17 00:00:00 2001 From: Yousaf Nabi Date: Fri, 31 Oct 2025 16:01:18 +0000 Subject: [PATCH 9/9] chore: rename bin.rs to bin/pact-stub-server --- Cargo.toml | 7 ------- src/{bin.rs => bin/pact-stub-server.rs} | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) rename src/{bin.rs => bin/pact-stub-server.rs} (80%) diff --git a/Cargo.toml b/Cargo.toml index 45ea372..c7b019f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,10 +62,3 @@ lto = true # https://doc.rust-lang.org/stable/rustc/codegen-options/ # https://doc.rust-lang.org/rustc/profile-guided-optimization.html # https://github.com/johnthagen/min-sized-rust - -[[bin]] -name = "pact-stub-server" -path = "src/bin.rs" -[lib] -name = "pact_stub_server_cli" -path = "src/lib.rs" \ No newline at end of file diff --git a/src/bin.rs b/src/bin/pact-stub-server.rs similarity index 80% rename from src/bin.rs rename to src/bin/pact-stub-server.rs index 2b81a32..8e44e6f 100644 --- a/src/bin.rs +++ b/src/bin/pact-stub-server.rs @@ -1,6 +1,6 @@ use std::env; use std::process::ExitCode; -use pact_stub_server_cli::handle_command_args; +use pact_stub_server::handle_command_args; #[tokio::main] async fn main() -> Result<(), ExitCode> {