From 9158de1890e59994c33503cf4c2d86e3009fb320 Mon Sep 17 00:00:00 2001 From: buckn Date: Sun, 12 Oct 2025 18:04:13 -0400 Subject: [PATCH] update --- Cargo.lock | 379 +++++++++++++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 122 +++++++++-------- 2 files changed, 435 insertions(+), 66 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a8fe6a5..28ee105 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "core-foundation" version = "0.9.4" @@ -235,8 +241,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -246,9 +254,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasi 0.14.7+wasi-0.2.4", + "wasm-bindgen", ] [[package]] @@ -282,6 +292,12 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + [[package]] name = "http" version = "1.3.1" @@ -322,9 +338,12 @@ version = "0.1.0" dependencies = [ "anyhow", "async-trait", + "num_cpus", + "once_cell", "reqwest", "serde", "serde_json", + "tokio", "toml", "urlencoding", ] @@ -383,6 +402,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", + "webpki-roots", ] [[package]] @@ -605,12 +625,27 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "memchr" version = "2.7.6" @@ -660,6 +695,16 @@ dependencies = [ "tempfile", ] +[[package]] +name = "num_cpus" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91df4bbde75afed763b708b7eee1e8e7651e02d97f6d5dd763e89367e957b23b" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.37.3" @@ -719,6 +764,29 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + [[package]] name = "percent-encoding" version = "2.3.2" @@ -752,6 +820,15 @@ dependencies = [ "zerovec", ] +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.101" @@ -761,6 +838,61 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.41" @@ -776,6 +908,44 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + [[package]] name = "reqwest" version = "0.12.23" @@ -800,6 +970,8 @@ dependencies = [ "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", "serde", "serde_json", @@ -807,6 +979,7 @@ dependencies = [ "sync_wrapper", "tokio", "tokio-native-tls", + "tokio-rustls", "tower", "tower-http", "tower-service", @@ -814,6 +987,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", ] [[package]] @@ -836,6 +1010,12 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "1.1.2" @@ -856,6 +1036,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -868,6 +1049,7 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ + "web-time", "zeroize", ] @@ -903,6 +1085,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "security-framework" version = "2.11.1" @@ -996,6 +1184,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signal-hook-registry" +version = "1.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.11" @@ -1095,6 +1292,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tinystr" version = "0.8.1" @@ -1105,6 +1322,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.47.1" @@ -1116,12 +1348,26 @@ dependencies = [ "io-uring", "libc", "mio", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "slab", "socket2", + "tokio-macros", "windows-sys 0.59.0", ] +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tokio-native-tls" version = "0.3.1" @@ -1421,6 +1667,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "windows-link" version = "0.1.3" @@ -1468,7 +1733,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]] @@ -1477,7 +1742,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]] @@ -1495,14 +1769,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]] @@ -1511,48 +1802,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.13" @@ -1595,6 +1934,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zerofrom" version = "0.1.6" diff --git a/src/lib.rs b/src/lib.rs index 566b0c8..bbc2565 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,10 +11,12 @@ use syn::{ /// #[http(...)] attribute macro /// Generates HasHttp + Queryable impls (reqwest-based). /// -/// Defaults: -/// - method = "GET" -/// - live_url = "" -/// - sandbox_url = "" +/// Required: +/// - api = "alpaca_trading" +/// - url = "/v2/account" +/// +/// Optional: +/// - method = "GET" (default) /// - response = "Resp" /// --------------------------------------------------------------------------- #[proc_macro_attribute] @@ -23,7 +25,9 @@ pub fn http(attr: TokenStream, item: TokenStream) -> TokenStream { let struct_ident = &input.ident; // defaults + let mut api_s = String::new(); let mut method_s = "GET".to_string(); + let mut url_s = "".to_string(); let mut live_url_s = "".to_string(); let mut sandbox_url_s = "".to_string(); let mut response_s = format!("{}Resp", struct_ident); @@ -44,6 +48,8 @@ pub fn http(attr: TokenStream, item: TokenStream) -> TokenStream { if let Lit::Str(ls) = &expr_lit.lit { let val = ls.value(); match key.as_str() { + "api" => api_s = val, + "url" => url_s = val, "method" => method_s = val, "live_url" => live_url_s = val, "sandbox_url" => sandbox_url_s = val, @@ -79,11 +85,13 @@ pub fn http(attr: TokenStream, item: TokenStream) -> TokenStream { let response_ty: Type = syn::parse_str(&response_s) .unwrap_or_else(|_| syn::parse_str("serde_json::Value").unwrap()); + let api_lit = syn::LitStr::new(&api_s, Span::call_site()); let method_lit = syn::LitStr::new(&method_s, Span::call_site()); + let url_lit = syn::LitStr::new(&url_s, Span::call_site()); let live_lit = syn::LitStr::new(&live_url_s, Span::call_site()); let sandbox_lit = syn::LitStr::new(&sandbox_url_s, Span::call_site()); - // ✅ Final merged + polished implementation + // ✅ Final implementation let expanded = quote! { #input @@ -97,23 +105,23 @@ pub fn http(attr: TokenStream, item: TokenStream) -> TokenStream { impl http_core::Queryable for #struct_ident { type Response = #response_ty; - async fn send( + async fn send_with_keys( &self, client: std::sync::Arc, + keys: std::sync::Arc, override_url: Option<&str>, sandbox: bool, method_override: Option<&str>, - headers: Option>, + headers: Option>, // ✅ forwarded ) -> anyhow::Result { - use http_core::{HasHttp, replace_path_params, append_query_params}; - use anyhow::{Context, anyhow}; - use serde_json::to_vec; + use http_core::{HasHttp, replace_path_params, append_query_params, send_request}; + use anyhow::Context; let mut query_params: Vec<(String, String)> = Vec::new(); #(#qparam_snippets)* - // base URL resolution - let mut url = override_url.unwrap_or_else(|| { + // choose URL base + let mut base = override_url.unwrap_or_else(|| { if sandbox { ::sandbox_url() } else { @@ -121,47 +129,34 @@ pub fn http(attr: TokenStream, item: TokenStream) -> TokenStream { } }).to_string(); - // replace path params if any exist in the template - url = replace_path_params(&url, &[], self) + // replace path params (safe no-op) + base = replace_path_params(&base, &[], self) .context("Failed to replace path params")?; - // append query params if present - url = append_query_params(&url, &query_params.iter().map(|(k, _)| k.clone()).collect::>(), self) - .context("Failed to append query params")?; + // append query params (safe no-op) + base = append_query_params( + &base, + &query_params.iter().map(|(k, _)| k.clone()).collect::>(), + self + ).context("Failed to append query params")?; let method_str = method_override.unwrap_or(#method_lit); - let method = reqwest::Method::from_bytes(method_str.as_bytes()) - .unwrap_or(reqwest::Method::GET); - // build request - let mut req = client.request(method.clone(), &url); + // ✅ ensure headers are moved safely + let hdrs = headers.clone(); - if let Some(hs) = headers { - for (k, v) in hs { - req = req.header(k, v); - } - } - - // send body if applicable - let resp = if matches!(method, reqwest::Method::POST | reqwest::Method::PUT | reqwest::Method::PATCH) { - let body = to_vec(self).context("Failed to serialize request body")?; - req = req.body(body); - req.send().await.context("HTTP request failed")? - } else { - req.send().await.context("HTTP request failed")? - }; - - // handle response - let status = resp.status(); - let bytes = resp.bytes().await.context("Failed to read response body")?; - - if !status.is_success() { - return Err(anyhow!("HTTP {}: {}", status, String::from_utf8_lossy(&bytes))); - } - - let parsed: Self::Response = serde_json::from_slice(&bytes) - .context("Failed to deserialize response")?; - Ok(parsed) + // ✅ pass headers into send_request + send_request::( + client, + keys, + #api_lit, + &base, + #url_lit, + override_url, + method_str, + self, + hdrs, // ✅ now forwarded properly + ).await } } }; @@ -169,6 +164,9 @@ pub fn http(attr: TokenStream, item: TokenStream) -> TokenStream { TokenStream::from(expanded) } +/// --------------------------------------------------------------------------- +/// dispatch! macro — async parallel enum dispatcher +/// --------------------------------------------------------------------------- #[proc_macro] pub fn dispatch(input: TokenStream) -> TokenStream { use quote::format_ident; @@ -184,7 +182,9 @@ pub fn dispatch(input: TokenStream) -> TokenStream { let top_enum_ident = match &input.elems[0] { syn::Expr::Path(p) => &p.path.segments.last().unwrap().ident, other => { - return syn::Error::new_spanned(other, "First element must be a type path").to_compile_error().into(); + return syn::Error::new_spanned(other, "First element must be a type path") + .to_compile_error() + .into(); } }; @@ -193,7 +193,9 @@ pub fn dispatch(input: TokenStream) -> TokenStream { let map_macro = match map_expr { syn::Expr::Macro(m) if m.mac.path.is_ident("hashmap") => m, other => { - return syn::Error::new_spanned(other, "Second element must be a hashmap! macro").to_compile_error().into(); + return syn::Error::new_spanned(other, "Second element must be a hashmap! macro") + .to_compile_error() + .into(); } }; @@ -245,10 +247,9 @@ pub fn dispatch(input: TokenStream) -> TokenStream { use std::sync::Arc; use tokio::sync::Semaphore; - // Limit concurrency to 10 by default - let semaphore = Arc::new(Semaphore::new(10)); - + let semaphore = Arc::new(Semaphore::new(16)); let mut tasks = FuturesUnordered::new(); + for req in reqs { let client = client.clone(); let permit = semaphore.clone().acquire_owned().await.unwrap(); @@ -259,15 +260,24 @@ pub fn dispatch(input: TokenStream) -> TokenStream { tasks.push(tokio::spawn(async move { let _permit = permit; - req.send(client, override_url.as_deref(), sandbox, method_override.as_deref(), None) - .await - .map(|r| serde_json::to_value(r).unwrap_or(serde_json::Value::Null)) + req.send_with_keys( + client, + keys, + override_url.as_deref(), + sandbox, + method_override.as_deref(), + None // ✅ default headers still None here + ) + .await + .map(|r| serde_json::to_value(r) + .unwrap_or(serde_json::Value::Null)) })); } let mut results = Vec::new(); while let Some(res) = tasks.next().await { - results.push(res.unwrap_or_else(|e| Err(anyhow::anyhow!("JoinError: {}", e)))); + results.push(res + .unwrap_or_else(|e| Err(anyhow::anyhow!("JoinError: {}", e)))); } Ok(results) }