From 86465c13198bb9c8a8f5bd11719de9e72a2909e6 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 12 Dec 2023 11:20:32 -0700 Subject: [PATCH 01/79] wip --- Cargo.lock | 1172 +++++++++++++++++++++++------------- rpc-toolkit/Cargo.toml | 19 +- rpc-toolkit/src/command.rs | 336 +++++++++++ rpc-toolkit/src/context.rs | 18 +- rpc-toolkit/src/lib.rs | 1 + 5 files changed, 1130 insertions(+), 416 deletions(-) create mode 100644 rpc-toolkit/src/command.rs diff --git a/Cargo.lock b/Cargo.lock index 0aba0e4..6b3d782 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,20 +3,96 @@ version = 3 [[package]] -name = "anyhow" -version = "1.0.40" +name = "addr2line" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +dependencies = [ + "gimli", +] [[package]] -name = "atty" -version = "0.2.14" +name = "adler" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "ahash" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "anstream" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "async-trait" +version = "0.1.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.40", ] [[package]] @@ -26,34 +102,64 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] -name = "base64" -version = "0.13.0" +name = "backtrace" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +dependencies = [ + "addr2line", + "cc", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" + +[[package]] +name = "bitmaps" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403" [[package]] name = "bumpalo" -version = "3.6.1" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytes" -version = "1.0.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.67" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -63,33 +169,42 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.2.8" +version = "4.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83" +checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ - "atty", - "bitflags", + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +dependencies = [ + "anstream", + "anstyle", "clap_lex", - "indexmap", "strsim", - "termcolor", - "textwrap", ] [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "core-foundation" -version = "0.9.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -97,19 +212,41 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.2" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "encoding_rs" -version = "0.8.28" +version = "0.8.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + [[package]] name = "fnv" version = "1.0.7" @@ -133,19 +270,18 @@ checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "matches", "percent-encoding", ] [[package]] name = "futures" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" dependencies = [ "futures-channel", "futures-core", @@ -158,9 +294,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" dependencies = [ "futures-core", "futures-sink", @@ -168,15 +304,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" dependencies = [ "futures-core", "futures-task", @@ -185,38 +321,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.40", ] [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" dependencies = [ "futures-channel", "futures-core", @@ -232,27 +368,52 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.2" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] -name = "h2" -version = "0.3.13" +name = "gimli" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "h2" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" dependencies = [ "bytes", "fnv", "futures-core", "futures-sink", "futures-util", - "http", + "http 0.2.11", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d308f63daf4181410c242d34c11f928dcb3aa105852019e043c9d1f4e4368a" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.0.0", "indexmap", "slab", "tokio", @@ -262,83 +423,130 @@ dependencies = [ [[package]] name = "half" -version = "1.7.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" [[package]] name = "hashbrown" -version = "0.9.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "hermit-abi" -version = "0.1.18" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" + +[[package]] +name = "http" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" dependencies = [ - "libc", + "bytes", + "fnv", + "itoa", ] [[package]] name = "http" -version = "0.2.3" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" dependencies = [ "bytes", "fnv", - "itoa 0.4.7", + "itoa", ] [[package]] name = "http-body" -version = "0.4.1" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.11", "pin-project-lite", ] [[package]] -name = "httparse" -version = "1.7.1" +name = "http-body" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.0.0", +] + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.20" +version = "0.14.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" dependencies = [ "bytes", "futures-channel", "futures-core", "futures-util", - "h2", - "http", - "http-body", + "h2 0.3.22", + "http 0.2.11", + "http-body 0.4.6", "httparse", "httpdate", - "itoa 1.0.2", + "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.10", "tokio", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403f9214f3e703236b221f1a9cd88ec8b4adfa5296de01ab96216361f4692f56" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2 0.4.0", + "http 1.0.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", + "want", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -346,7 +554,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.27", "native-tls", "tokio", "tokio-native-tls", @@ -354,48 +562,75 @@ dependencies = [ [[package]] name = "idna" -version = "0.2.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89829a5d69c23d348314a7ac337fe39173b61149a9864deabd260983aed48c21" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] [[package]] -name = "indexmap" -version = "1.6.2" +name = "imbl" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824845a0bf897a9042383849b02c1bc219c2383772efcd5c6f9766fa4b81aef3" +checksum = "978d142c8028edf52095703af2fad11d6f611af1246685725d6b850634647085" dependencies = [ - "autocfg", - "hashbrown", + "bitmaps", + "imbl-sized-chunks", + "rand_core", + "rand_xoshiro", + "serde", + "version_check", +] + +[[package]] +name = "imbl-sized-chunks" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6957ea0b2541c5ca561d3ef4538044af79f8a05a1eb3a3b148936aaceaa1076" +dependencies = [ + "bitmaps", +] + +[[package]] +name = "imbl-value" +version = "0.1.0" +source = "git+https://github.com/Start9Labs/imbl-value.git#929395141c3a882ac366c12ac9402d0ebaa2201b" +dependencies = [ + "imbl", + "serde", + "serde_json", + "yasi", +] + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", ] [[package]] name = "ipnet" -version = "2.3.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" +checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" [[package]] name = "itoa" -version = "0.4.7" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" - -[[package]] -name = "itoa" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.50" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" +checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" dependencies = [ "wasm-bindgen", ] @@ -408,15 +643,21 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" + +[[package]] +name = "linux-raw-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" dependencies = [ "autocfg", "scopeguard", @@ -424,48 +665,47 @@ dependencies = [ [[package]] name = "log" -version = "0.4.14" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.3.4" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "mime" -version = "0.3.16" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] [[package]] name = "mio" -version = "0.8.4" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "wasi", + "windows-sys 0.48.0", ] [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" dependencies = [ "lazy_static", "libc", @@ -481,56 +721,76 @@ dependencies = [ [[package]] name = "num_cpus" -version = "1.13.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ "hermit-abi", "libc", ] [[package]] -name = "once_cell" -version = "1.7.2" +name = "object" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.33" +version = "0.10.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577" +checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" dependencies = [ - "bitflags", + "bitflags 2.4.1", "cfg-if", "foreign-types", "libc", "once_cell", + "openssl-macros", "openssl-sys", ] [[package]] -name = "openssl-probe" -version = "0.1.2" +name = "openssl-macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.40", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "111.22.0+1.1.1q" +version = "300.2.1+3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853" +checksum = "3fe476c29791a5ca0d1273c697e96085bbabbbea2ef7afd5617e78a4b40332d3" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.61" +version = "0.9.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f" +checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" dependencies = [ - "autocfg", "cc", "libc", "openssl-src", @@ -538,12 +798,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "os_str_bytes" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa" - [[package]] name = "parking_lot" version = "0.12.1" @@ -556,28 +810,28 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-targets 0.48.5", ] [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project-lite" -version = "0.2.6" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -587,119 +841,80 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.19" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" - -[[package]] -name = "ppv-lite86" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] -[[package]] -name = "rand" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d" -dependencies = [ - "ppv-lite86", - "rand_core", -] - [[package]] name = "rand_core" -version = "0.6.2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7" -dependencies = [ - "getrandom", -] +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] -name = "rand_hc" -version = "0.3.0" +name = "rand_xoshiro" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73" +checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ "rand_core", ] [[package]] name = "redox_syscall" -version = "0.2.13" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", -] - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", + "bitflags 1.3.2", ] [[package]] name = "reqwest" -version = "0.11.11" +version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ "base64", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2", - "http", - "http-body", - "hyper", + "h2 0.3.22", + "http 0.2.11", + "http-body 0.4.6", + "hyper 0.14.27", "hyper-tls", "ipnet", "js-sys", - "lazy_static", "log", "mime", "native-tls", + "once_cell", "percent-encoding", "pin-project-lite", "serde", "serde_json", "serde_urlencoded", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -712,11 +927,13 @@ dependencies = [ [[package]] name = "rpc-toolkit" -version = "0.2.2" +version = "0.2.3" dependencies = [ + "async-trait", "clap", "futures", - "hyper", + "hyper 1.0.1", + "imbl-value", "lazy_static", "openssl", "reqwest", @@ -736,7 +953,7 @@ version = "0.2.2" dependencies = [ "proc-macro2", "rpc-toolkit-macro-internals", - "syn", + "syn 1.0.109", ] [[package]] @@ -745,38 +962,56 @@ version = "0.2.2" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 1.0.109", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" + +[[package]] +name = "rustix" +version = "0.38.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +dependencies = [ + "bitflags 2.4.1", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", ] [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" dependencies = [ - "lazy_static", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.2.0" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -785,9 +1020,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.2.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -795,9 +1030,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.139" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] @@ -814,22 +1049,22 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.139" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.40", ] [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.108" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" dependencies = [ - "itoa 1.0.2", + "itoa", "ryu", "serde", ] @@ -841,42 +1076,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ "form_urlencoded", - "itoa 1.0.2", + "itoa", "ryu", "serde", ] [[package]] name = "signal-hook-registry" -version = "1.3.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" dependencies = [ "libc", ] [[package]] name = "slab" -version = "0.4.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] [[package]] name = "smallvec" -version = "1.6.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" +checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" dependencies = [ "libc", "winapi", ] +[[package]] +name = "socket2" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "strsim" version = "0.10.0" @@ -885,9 +1133,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] name = "syn" -version = "1.0.98" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", @@ -895,105 +1143,120 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.2.0" +name = "syn" +version = "2.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", - "libc", - "rand", + "fastrand", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix", + "windows-sys 0.48.0", ] -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" - [[package]] name = "thiserror" -version = "1.0.31" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.40", ] [[package]] name = "tinyvec" -version = "1.1.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" dependencies = [ "tinyvec_macros", ] [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.19.2" +version = "1.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" dependencies = [ + "backtrace", "bytes", "libc", - "memchr", "mio", "num_cpus", - "once_cell", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.5", "tokio-macros", - "winapi", + "windows-sys 0.48.0", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.40", ] [[package]] name = "tokio-native-tls" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" dependencies = [ "native-tls", "tokio", @@ -1001,9 +1264,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.2" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" dependencies = [ "bytes", "futures-core", @@ -1015,106 +1278,94 @@ dependencies = [ [[package]] name = "tower-service" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" [[package]] name = "tracing" -version = "0.1.25" +version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ebdc2bb4498ab1ab5f5b73c5803825e60199229ccba0698170e3be0e7f959f" +checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "cfg-if", "pin-project-lite", - "tracing-attributes", "tracing-core", ] -[[package]] -name = "tracing-attributes" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tracing-core" -version = "0.1.17" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f50de3927f93d202783f4513cda820ab47ef17f624b03c096e86ef00c67e6b5f" +checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ - "lazy_static", + "once_cell", ] [[package]] name = "try-lock" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "unicode-bidi" -version = "0.3.4" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -dependencies = [ - "matches", -] +checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-normalization" -version = "0.1.17" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] [[package]] name = "url" -version = "2.2.2" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] [[package]] -name = "vcpkg" -version = "0.2.11" +name = "utf8parse" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "want" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "log", "try-lock", ] -[[package]] -name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1123,9 +1374,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.73" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" +checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1133,24 +1384,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.73" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" +checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.40", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.23" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea" +checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" dependencies = [ "cfg-if", "js-sys", @@ -1160,9 +1411,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.73" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" +checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1170,28 +1421,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.73" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" +checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.40", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.73" +version = "0.2.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" +checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "web-sys" -version = "0.3.50" +version = "0.3.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" +checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" dependencies = [ "js-sys", "wasm-bindgen", @@ -1213,15 +1464,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -1230,62 +1472,186 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", + "windows-targets 0.48.5", ] [[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.0", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winreg" -version = "0.10.1" +version = "0.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" dependencies = [ - "winapi", + "cfg-if", + "windows-sys 0.48.0", ] [[package]] name = "yajrc" -version = "0.1.0" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc08b562507a1674a1ef886a1aedeeb19d41462386ae09f634995d41bbef87d3" dependencies = [ "anyhow", "serde", "serde_json", "thiserror", ] + +[[package]] +name = "yasi" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f355ab62ebe30b758c1f4ab096a306722c4b7dbfb9d8c07d18c70d71a945588" +dependencies = [ + "ahash", + "hashbrown 0.13.2", + "lazy_static", + "serde", +] + +[[package]] +name = "zerocopy" +version = "0.7.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.40", +] diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml index 5305021..c537815 100644 --- a/rpc-toolkit/Cargo.toml +++ b/rpc-toolkit/Cargo.toml @@ -16,24 +16,19 @@ cbor = ["serde_cbor"] default = ["cbor"] [dependencies] -clap = "3" +async-trait = "0.1" +clap = "4" futures = "0.3" -hyper = { version = "0.14", features = [ - "server", - "http1", - "http2", - "tcp", - "stream", - "client", -] } +hyper = { version = "1", features = ["server", "http1", "http2", "client"] } +imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } lazy_static = "1.4" openssl = { version = "0.10", features = ["vendored"] } reqwest = { version = "0.11" } -rpc-toolkit-macro = { version = "=0.2.2", path = "../rpc-toolkit-macro" } +rpc-toolkit-macro = { version = "0.2.2", path = "../rpc-toolkit-macro" } serde = { version = "1.0", features = ["derive"] } serde_cbor = { version = "0.11", optional = true } serde_json = "1.0" thiserror = "1.0" tokio = { version = "1", features = ["full"] } -url = "2.2.2" -yajrc = "0.1.1" +url = "2" +yajrc = "0.1" diff --git a/rpc-toolkit/src/command.rs b/rpc-toolkit/src/command.rs new file mode 100644 index 0000000..e49d9f8 --- /dev/null +++ b/rpc-toolkit/src/command.rs @@ -0,0 +1,336 @@ +use std::marker::PhantomData; +use std::sync::Arc; + +use clap::{ArgMatches, CommandFactory, FromArgMatches}; +use futures::future::BoxFuture; +use futures::FutureExt; +use imbl_value::Value; +use serde::de::DeserializeOwned; +use serde::ser::Serialize; +use tokio::runtime::Runtime; +use yajrc::RpcError; + +pub struct DynCommand { + name: &'static str, + implementation: Option>, + cli: Option, + subcommands: Vec, +} +impl DynCommand { + fn cli_app(&self) -> Option { + if let Some(cli) = &self.cli { + Some( + cli.cmd + .name(self.name) + .subcommands(self.subcommands.iter().filter_map(|c| c.cli_app())), + ) + } else { + None + } + } + fn impl_from_cli_matches( + &self, + matches: &ArgMatches, + parent: Value, + ) -> Result, RpcError> { + let args = combine( + parent, + (self + .cli + .as_ref() + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .parser)(matches)?, + )?; + if let Some((cmd, matches)) = matches.subcommand() { + self.subcommands + .iter() + .find(|c| c.name == cmd) + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .impl_from_cli_matches(matches, args) + } else if let Some(implementation) = self.implementation.clone() { + Ok(implementation) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } + pub fn run_cli(ctx: Context) {} +} + +struct Implementation { + async_impl: Arc BoxFuture<'static, Result>>, + sync_impl: Arc Result>, +} +impl Clone for Implementation { + fn clone(&self) -> Self { + Self { + async_impl: self.async_impl.clone(), + sync_impl: self.sync_impl.clone(), + } + } +} + +struct CliBindings { + cmd: clap::Command, + parser: Box Fn(&'a ArgMatches) -> Result + Send + Sync>, + display: Option>, +} +impl CliBindings { + fn from_parent() -> Self { + Self { + cmd: Cmd::command(), + parser: Box::new(|matches| { + imbl_value::to_value(&Cmd::from_arg_matches(matches).map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_PARAMS_ERROR + })?) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_PARAMS_ERROR + }) + }), + display: None, + } + } + fn from_leaf() -> Self { + Self { + display: Some(Box::new(|res| Cmd::display(todo!("{}", res)))), + ..Self::from_parent::() + } + } +} + +pub trait Command: DeserializeOwned + Sized { + const NAME: &'static str; + type Parent: Command; +} + +pub struct ParentChain(PhantomData); +pub struct Contains(PhantomData); +impl From<(Contains, Contains)> for Contains<(T, U)> { + fn from(value: (Contains, Contains)) -> Self { + Self(PhantomData) + } +} + +#[derive(serde::Deserialize, serde::Serialize)] +pub struct Root {} +impl Command for Root { + const NAME: &'static str = ""; + type Parent = Root; +} +impl ParentChain +where + Cmd: Command, +{ + pub fn unit(&self) -> Contains<()> { + Contains(PhantomData) + } + pub fn child(&self) -> Contains { + Contains(PhantomData) + } + pub fn parent(&self) -> ParentChain { + ParentChain(PhantomData) + } +} + +pub trait ParentCommand: Command { + fn subcommands(chain: ParentChain) -> Vec>; +} +impl DynCommand { + pub fn from_parent< + Cmd: ParentCommand + FromArgMatches + CommandFactory + Serialize, + >() -> Self { + Self { + name: Cmd::NAME, + implementation: None, + cli: Some(CliBindings::from_parent::()), + subcommands: Cmd::subcommands(ParentChain::(PhantomData)), + } + } +} + +pub trait LeafCommand: Command { + type Ok: Serialize; + type Err: Into; + fn display(res: Self::Ok); +} + +#[async_trait::async_trait] +pub trait AsyncCommand: LeafCommand { + async fn implementation( + self, + ctx: Context, + parent: Self::Parent, + ) -> Result; + fn subcommands(chain: ParentChain) -> Vec> { + Vec::new() + } +} +impl Implementation { + fn for_async>(contains: Contains) -> Self { + Self { + async_impl: Arc::new(|ctx, params| { + async move { + let parent = extract::(¶ms)?; + imbl_value::to_value( + &extract::(¶ms)? + .implementation(ctx, parent) + .await + .map_err(|e| e.into())?, + ) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::PARSE_ERROR + }) + } + .boxed() + }), + sync_impl: Arc::new(|ctx, params| { + let parent = extract::(¶ms)?; + imbl_value::to_value( + &Runtime::new() + .unwrap() + .block_on( + extract::(¶ms) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_PARAMS_ERROR + })? + .implementation(ctx, parent), + ) + .map_err(|e| e.into())?, + ) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::PARSE_ERROR + }) + }), + } + } +} +impl DynCommand { + pub fn from_async + FromArgMatches + CommandFactory + Serialize>( + contains: Contains, + ) -> Self { + Self { + name: Cmd::NAME, + implementation: Some(Implementation::for_async::(contains)), + cli: Some(CliBindings::from_leaf::()), + subcommands: Cmd::subcommands(ParentChain::(PhantomData)), + } + } +} + +pub trait SyncCommand: LeafCommand { + const BLOCKING: bool; + fn implementation(self, ctx: Context, parent: Self::Parent) -> Result; + fn subcommands(chain: ParentChain) -> Vec> { + Vec::new() + } +} +impl Implementation { + fn for_sync>(contains: Contains) -> Self { + Self { + async_impl: if Cmd::BLOCKING { + Arc::new(|ctx, params| { + tokio::task::spawn_blocking(move || { + let parent = extract::(¶ms)?; + imbl_value::to_value( + &extract::(¶ms) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_PARAMS_ERROR + })? + .implementation(ctx, parent) + .map_err(|e| e.into())?, + ) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::PARSE_ERROR + }) + }) + .map(|f| { + f.map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + })? + }) + .boxed() + }) + } else { + Arc::new(|ctx, params| { + async move { + let parent = extract::(¶ms)?; + imbl_value::to_value( + &extract::(¶ms) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_PARAMS_ERROR + })? + .implementation(ctx, parent) + .map_err(|e| e.into())?, + ) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::PARSE_ERROR + }) + } + .boxed() + }) + }, + sync_impl: Arc::new(|ctx, params| { + let parent = extract::(¶ms)?; + imbl_value::to_value( + &extract::(¶ms) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_PARAMS_ERROR + })? + .implementation(ctx, parent) + .map_err(|e| e.into())?, + ) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::PARSE_ERROR + }) + }), + } + } +} +impl DynCommand { + pub fn from_sync + FromArgMatches + CommandFactory + Serialize>( + contains: Contains, + ) -> Self { + Self { + name: Cmd::NAME, + implementation: Some(Implementation::for_sync::(contains)), + cli: Some(CliBindings::from_leaf::()), + subcommands: Cmd::subcommands(ParentChain::(PhantomData)), + } + } +} + +fn extract(value: &Value) -> Result { + imbl_value::from_value(value.clone()).map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_PARAMS_ERROR + }) +} + +fn combine(v1: Value, v2: Value) -> Result { + let (Value::Object(mut v1), Value::Object(v2)) = (v1, v2) else { + return Err(RpcError { + data: Some("params must be object".into()), + ..yajrc::INVALID_PARAMS_ERROR + }); + }; + for (key, value) in v2 { + if v1.insert(key.clone(), value).is_some() { + return Err(RpcError { + data: Some(format!("duplicate key: {key}").into()), + ..yajrc::INVALID_PARAMS_ERROR + }); + } + } + Ok(Value::Object(v1)) +} diff --git a/rpc-toolkit/src/context.rs b/rpc-toolkit/src/context.rs index 1cae28e..91e9114 100644 --- a/rpc-toolkit/src/context.rs +++ b/rpc-toolkit/src/context.rs @@ -1,12 +1,18 @@ +use std::sync::Arc; + use lazy_static::lazy_static; use reqwest::Client; +use tokio::runtime::Runtime; use url::{Host, Url}; lazy_static! { static ref DEFAULT_CLIENT: Client = Client::new(); } -pub trait Context { +pub trait Context: Send { + fn runtime(&self) -> Arc { + Arc::new(Runtime::new().unwrap()) + } fn protocol(&self) -> &str { "http" } @@ -39,3 +45,13 @@ impl<'a, T: Context + 'a> From for Box { Box::new(ctx) } } + +impl Context for (T, U) +where + T: Context, + U: Send, +{ + fn runtime(&self) -> Arc { + self.0.runtime() + } +} diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index 861eab7..89be369 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -52,6 +52,7 @@ pub use crate::context::Context; pub use crate::metadata::Metadata; pub use crate::rpc_server_helpers::RpcHandler; +mod command; pub mod command_helpers; mod context; mod metadata; From 6696da45908fbc8d16d499ca8d14465ae3174391 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 12 Dec 2023 13:52:31 -0700 Subject: [PATCH 02/79] clean up, add docs --- rpc-toolkit/src/cli.rs | 108 ++++++++++++++++++ rpc-toolkit/src/command.rs | 179 ++++++++++++++++++++---------- rpc-toolkit/src/lib.rs | 44 ++------ rpc-toolkit/src/util.rs | 60 ++++++++++ rpc-toolkit/tests/compat.rs | 214 +++++++++++++++++++++++++++++++++++ rpc-toolkit/tests/test.rs | 215 +----------------------------------- 6 files changed, 512 insertions(+), 308 deletions(-) create mode 100644 rpc-toolkit/src/cli.rs create mode 100644 rpc-toolkit/src/util.rs create mode 100644 rpc-toolkit/tests/compat.rs diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs new file mode 100644 index 0000000..c2d2aa4 --- /dev/null +++ b/rpc-toolkit/src/cli.rs @@ -0,0 +1,108 @@ +use clap::ArgMatches; +use imbl_value::Value; +use reqwest::{Client, Method}; +use serde::de::DeserializeOwned; +use serde::Serialize; +use url::Url; +use yajrc::{GenericRpcMethod, Id, RpcError, RpcRequest}; + +use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo}; +use crate::util::{combine, invalid_params, parse_error}; +use crate::ParentChain; + +pub struct CliApp { + pub(crate) command: DynCommand, + pub(crate) make_ctx: Box Result>, +} + +#[async_trait::async_trait] +pub trait CliContext { + async fn call_remote(&self, method: &str, params: Value) -> Result; +} + +pub trait CliContextHttp { + fn client(&self) -> &Client; + fn url(&self) -> Url; +} +#[async_trait::async_trait] +impl CliContext for T { + async fn call_remote(&self, method: &str, params: Value) -> Result { + let rpc_req: RpcRequest> = RpcRequest { + id: Some(Id::Number(0.into())), + method: GenericRpcMethod::new(method), + params, + }; + let mut req = self.client().request(Method::POST, self.url()); + let body; + #[cfg(feature = "cbor")] + { + req = req.header("content-type", "application/cbor"); + req = req.header("accept", "application/cbor, application/json"); + body = serde_cbor::to_vec(&rpc_req)?; + } + #[cfg(not(feature = "cbor"))] + { + req = req.header("content-type", "application/json"); + req = req.header("accept", "application/json"); + body = serde_json::to_vec(&req)?; + } + let res = req + .header("content-length", body.len()) + .body(body) + .send() + .await?; + Ok( + match res + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + { + Some("application/json") => serde_json::from_slice(&*res.bytes().await?)?, + #[cfg(feature = "cbor")] + Some("application/cbor") => serde_cbor::from_slice(&*res.bytes().await?)?, + _ => { + return Err(RpcError { + data: Some("missing content type".into()), + ..yajrc::INTERNAL_ERROR + }) + } + }, + ) + } +} + +pub trait RemoteCommand: LeafCommand { + fn subcommands(chain: ParentChain) -> Vec> { + drop(chain); + Vec::new() + } +} +#[async_trait::async_trait] +impl AsyncCommand for T +where + T: RemoteCommand + Send + Serialize, + T::Parent: Serialize, + T::Ok: DeserializeOwned, + T::Err: From, + Context: CliContext + Send + 'static, +{ + async fn implementation( + self, + ctx: Context, + parent: ParentInfo, + ) -> Result { + let mut method = parent.method; + method.push(Self::NAME); + Ok(imbl_value::from_value( + ctx.call_remote( + &method.join("."), + combine( + imbl_value::to_value(&self).map_err(invalid_params)?, + imbl_value::to_value(&parent.args).map_err(invalid_params)?, + )?, + ) + .await?, + ) + .map_err(parse_error)?) + } +} diff --git a/rpc-toolkit/src/command.rs b/rpc-toolkit/src/command.rs index e49d9f8..e6147cd 100644 --- a/rpc-toolkit/src/command.rs +++ b/rpc-toolkit/src/command.rs @@ -10,17 +10,22 @@ use serde::ser::Serialize; use tokio::runtime::Runtime; use yajrc::RpcError; +use crate::util::{combine, extract, Flat}; + +/// Stores a command's implementation for a given context +/// Can be created from anything that implements ParentCommand, AsyncCommand, or SyncCommand pub struct DynCommand { - name: &'static str, - implementation: Option>, - cli: Option, - subcommands: Vec, + pub(crate) name: &'static str, + pub(crate) implementation: Option>, + pub(crate) cli: Option, + pub(crate) subcommands: Vec, } impl DynCommand { - fn cli_app(&self) -> Option { + pub(crate) fn cli_app(&self) -> Option { if let Some(cli) = &self.cli { Some( cli.cmd + .clone() .name(self.name) .subcommands(self.subcommands.iter().filter_map(|c| c.cli_app())), ) @@ -28,7 +33,7 @@ impl DynCommand { None } } - fn impl_from_cli_matches( + pub(crate) fn impl_from_cli_matches( &self, matches: &ArgMatches, parent: Value, @@ -53,12 +58,13 @@ impl DynCommand { Err(yajrc::METHOD_NOT_FOUND_ERROR) } } - pub fn run_cli(ctx: Context) {} } struct Implementation { - async_impl: Arc BoxFuture<'static, Result>>, - sync_impl: Arc Result>, + pub(crate) async_impl: Arc< + dyn Fn(Context, Vec<&'static str>, Value) -> BoxFuture<'static, Result>, + >, + pub(crate) sync_impl: Arc, Value) -> Result>, } impl Clone for Implementation { fn clone(&self) -> Self { @@ -72,7 +78,7 @@ impl Clone for Implementation { struct CliBindings { cmd: clap::Command, parser: Box Fn(&'a ArgMatches) -> Result + Send + Sync>, - display: Option>, + display: Option Result<(), imbl_value::Error> + Send + Sync>>, } impl CliBindings { fn from_parent() -> Self { @@ -93,36 +99,50 @@ impl CliBindings { } fn from_leaf() -> Self { Self { - display: Some(Box::new(|res| Cmd::display(todo!("{}", res)))), + display: Some(Box::new(|res| { + Ok(Cmd::display(imbl_value::from_value(res)?)) + })), ..Self::from_parent::() } } } -pub trait Command: DeserializeOwned + Sized { +/// Must be implemented for all commands +/// Use `Parent = NoParent` if the implementation requires no arguments from the parent command +pub trait Command: DeserializeOwned + Sized + Send { const NAME: &'static str; type Parent: Command; } +/// Includes the parent method, and the arguments requested from the parent +/// Arguments are flattened out in the params object, so ensure that there are no collisions between the names of the arguments for your method and its parents +pub struct ParentInfo { + pub method: Vec<&'static str>, + pub args: T, +} + +/// This is automatically generated from a command based on its Parents. +/// It can be used to generate a proof that one of the parents contains the necessary arguments that a subcommand requires. pub struct ParentChain(PhantomData); pub struct Contains(PhantomData); -impl From<(Contains, Contains)> for Contains<(T, U)> { - fn from(value: (Contains, Contains)) -> Self { +impl From<(Contains, Contains)> for Contains> { + fn from(_: (Contains, Contains)) -> Self { Self(PhantomData) } } +/// Use this as a Parent if your command does not require any arguments from its parents #[derive(serde::Deserialize, serde::Serialize)] -pub struct Root {} -impl Command for Root { +pub struct NoParent {} +impl Command for NoParent { const NAME: &'static str = ""; - type Parent = Root; + type Parent = NoParent; } impl ParentChain where Cmd: Command, { - pub fn unit(&self) -> Contains<()> { + pub fn none(&self) -> Contains { Contains(PhantomData) } pub fn child(&self) -> Contains { @@ -133,6 +153,7 @@ where } } +/// Implement this for a command that has no implementation, but simply exists to organize subcommands pub trait ParentCommand: Command { fn subcommands(chain: ParentChain) -> Vec>; } @@ -147,34 +168,52 @@ impl DynCommand { subcommands: Cmd::subcommands(ParentChain::(PhantomData)), } } + pub fn from_parent_no_cli>() -> Self { + Self { + name: Cmd::NAME, + implementation: None, + cli: None, + subcommands: Cmd::subcommands(ParentChain::(PhantomData)), + } + } } +/// Implement this for any command with an implementation pub trait LeafCommand: Command { - type Ok: Serialize; - type Err: Into; + type Ok: DeserializeOwned + Serialize + Send; + type Err: From + Into + Send; fn display(res: Self::Ok); } +/// Implement this if your Command's implementation is async #[async_trait::async_trait] pub trait AsyncCommand: LeafCommand { async fn implementation( self, ctx: Context, - parent: Self::Parent, + parent: ParentInfo, ) -> Result; fn subcommands(chain: ParentChain) -> Vec> { + drop(chain); Vec::new() } } -impl Implementation { +impl Implementation { fn for_async>(contains: Contains) -> Self { + drop(contains); Self { - async_impl: Arc::new(|ctx, params| { + async_impl: Arc::new(|ctx, method, params| { async move { let parent = extract::(¶ms)?; imbl_value::to_value( &extract::(¶ms)? - .implementation(ctx, parent) + .implementation( + ctx, + ParentInfo { + method, + args: parent, + }, + ) .await .map_err(|e| e.into())?, ) @@ -185,7 +224,7 @@ impl Implementation { } .boxed() }), - sync_impl: Arc::new(|ctx, params| { + sync_impl: Arc::new(|ctx, method, params| { let parent = extract::(¶ms)?; imbl_value::to_value( &Runtime::new() @@ -196,7 +235,13 @@ impl Implementation { data: Some(e.to_string().into()), ..yajrc::INVALID_PARAMS_ERROR })? - .implementation(ctx, parent), + .implementation( + ctx, + ParentInfo { + method, + args: parent, + }, + ), ) .map_err(|e| e.into())?, ) @@ -208,7 +253,7 @@ impl Implementation { } } } -impl DynCommand { +impl DynCommand { pub fn from_async + FromArgMatches + CommandFactory + Serialize>( contains: Contains, ) -> Self { @@ -219,20 +264,35 @@ impl DynCommand { subcommands: Cmd::subcommands(ParentChain::(PhantomData)), } } + pub fn from_async_no_cli>(contains: Contains) -> Self { + Self { + name: Cmd::NAME, + implementation: Some(Implementation::for_async::(contains)), + cli: None, + subcommands: Cmd::subcommands(ParentChain::(PhantomData)), + } + } } +/// Implement this if your Command's implementation is not async pub trait SyncCommand: LeafCommand { const BLOCKING: bool; - fn implementation(self, ctx: Context, parent: Self::Parent) -> Result; + fn implementation( + self, + ctx: Context, + parent: ParentInfo, + ) -> Result; fn subcommands(chain: ParentChain) -> Vec> { + drop(chain); Vec::new() } } -impl Implementation { +impl Implementation { fn for_sync>(contains: Contains) -> Self { + drop(contains); Self { async_impl: if Cmd::BLOCKING { - Arc::new(|ctx, params| { + Arc::new(|ctx, method, params| { tokio::task::spawn_blocking(move || { let parent = extract::(¶ms)?; imbl_value::to_value( @@ -241,7 +301,13 @@ impl Implementation { data: Some(e.to_string().into()), ..yajrc::INVALID_PARAMS_ERROR })? - .implementation(ctx, parent) + .implementation( + ctx, + ParentInfo { + method, + args: parent, + }, + ) .map_err(|e| e.into())?, ) .map_err(|e| RpcError { @@ -258,7 +324,7 @@ impl Implementation { .boxed() }) } else { - Arc::new(|ctx, params| { + Arc::new(|ctx, method, params| { async move { let parent = extract::(¶ms)?; imbl_value::to_value( @@ -267,7 +333,13 @@ impl Implementation { data: Some(e.to_string().into()), ..yajrc::INVALID_PARAMS_ERROR })? - .implementation(ctx, parent) + .implementation( + ctx, + ParentInfo { + method, + args: parent, + }, + ) .map_err(|e| e.into())?, ) .map_err(|e| RpcError { @@ -278,7 +350,7 @@ impl Implementation { .boxed() }) }, - sync_impl: Arc::new(|ctx, params| { + sync_impl: Arc::new(|ctx, method, params| { let parent = extract::(¶ms)?; imbl_value::to_value( &extract::(¶ms) @@ -286,7 +358,13 @@ impl Implementation { data: Some(e.to_string().into()), ..yajrc::INVALID_PARAMS_ERROR })? - .implementation(ctx, parent) + .implementation( + ctx, + ParentInfo { + method, + args: parent, + }, + ) .map_err(|e| e.into())?, ) .map_err(|e| RpcError { @@ -297,7 +375,7 @@ impl Implementation { } } } -impl DynCommand { +impl DynCommand { pub fn from_sync + FromArgMatches + CommandFactory + Serialize>( contains: Contains, ) -> Self { @@ -308,29 +386,12 @@ impl DynCommand { subcommands: Cmd::subcommands(ParentChain::(PhantomData)), } } -} - -fn extract(value: &Value) -> Result { - imbl_value::from_value(value.clone()).map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INVALID_PARAMS_ERROR - }) -} - -fn combine(v1: Value, v2: Value) -> Result { - let (Value::Object(mut v1), Value::Object(v2)) = (v1, v2) else { - return Err(RpcError { - data: Some("params must be object".into()), - ..yajrc::INVALID_PARAMS_ERROR - }); - }; - for (key, value) in v2 { - if v1.insert(key.clone(), value).is_some() { - return Err(RpcError { - data: Some(format!("duplicate key: {key}").into()), - ..yajrc::INVALID_PARAMS_ERROR - }); + pub fn from_sync_no_cli>(contains: Contains) -> Self { + Self { + name: Cmd::NAME, + implementation: Some(Implementation::for_sync::(contains)), + cli: None, + subcommands: Cmd::subcommands(ParentChain::(PhantomData)), } } - Ok(Value::Object(v1)) } diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index 89be369..1613002 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -1,3 +1,5 @@ +pub use cli::*; +pub use command::*; /// `#[command(...)]` /// - `#[command(cli_only)]` -> executed by CLI instead of RPC server (leaf commands only) /// - `#[command(rpc_only)]` -> no CLI bindings (leaf commands only) @@ -20,40 +22,12 @@ /// /// See also: [arg](rpc_toolkit_macro::arg), [context](rpc_toolkit_macro::context) pub use rpc_toolkit_macro::command; -/// `rpc_handler!(command, context, status_fn)` -/// - returns: [RpcHandler](rpc_toolkit::RpcHandler) -/// - `command`: path to an rpc command (with the `#[command]` attribute) -/// - `context`: The [Context] for `command`. Must implement [Clone](std::clone::Clone). -/// - `status_fn` (optional): a function that takes a JSON RPC error code (`i32`) and returns a [StatusCode](hyper::StatusCode) -/// - default: `|_| StatusCode::OK` -pub use rpc_toolkit_macro::rpc_handler; -/// `rpc_server!(command, context, status_fn)` -/// - returns: [Server](hyper::Server) -/// - `command`: path to an rpc command (with the `#[command]` attribute) -/// - `context`: The [Context] for `command`. Must implement [Clone](std::clone::Clone). -/// - `status_fn` (optional): a function that takes a JSON RPC error code (`i32`) and returns a [StatusCode](hyper::StatusCode) -/// - default: `|_| StatusCode::OK` -pub use rpc_toolkit_macro::rpc_server; -/// `run_cli!(command, app_mutator, make_ctx, exit_fn)` -/// - this function does not return -/// - `command`: path to an rpc command (with the `#[command]` attribute) -/// - `app_mutator` (optional): an expression that returns a mutated app. -/// - example: `app => app.arg(Arg::with_name("port").long("port"))` -/// - default: `app => app` -/// - `make_ctx` (optional): an expression that takes [&ArgMatches](clap::ArgMatches) and returns the [Context] used by `command`. -/// - example: `matches => matches.value_of("port")` -/// - default: `matches => matches` -/// - `exit_fn` (optional): a function that takes a JSON RPC error code (`i32`) and returns an Exit code (`i32`) -/// - default: `|code| code` -pub use rpc_toolkit_macro::run_cli; pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc}; -pub use crate::context::Context; -pub use crate::metadata::Metadata; -pub use crate::rpc_server_helpers::RpcHandler; - -mod command; -pub mod command_helpers; -mod context; -mod metadata; -pub mod rpc_server_helpers; +pub(crate) mod cli; +pub(crate) mod command; +// pub mod command_helpers; +// mod context; +// mod metadata; +// pub mod rpc_server_helpers; +pub(crate) mod util; diff --git a/rpc-toolkit/src/util.rs b/rpc-toolkit/src/util.rs new file mode 100644 index 0000000..eccfad9 --- /dev/null +++ b/rpc-toolkit/src/util.rs @@ -0,0 +1,60 @@ +use imbl_value::Value; +use serde::de::DeserializeOwned; +use serde::Deserialize; +use yajrc::RpcError; + +pub fn extract(value: &Value) -> Result { + imbl_value::from_value(value.clone()).map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_PARAMS_ERROR + }) +} + +pub fn combine(v1: Value, v2: Value) -> Result { + let (Value::Object(mut v1), Value::Object(v2)) = (v1, v2) else { + return Err(RpcError { + data: Some("params must be object".into()), + ..yajrc::INVALID_PARAMS_ERROR + }); + }; + for (key, value) in v2 { + if v1.insert(key.clone(), value).is_some() { + return Err(RpcError { + data: Some(format!("duplicate key: {key}").into()), + ..yajrc::INVALID_PARAMS_ERROR + }); + } + } + Ok(Value::Object(v1)) +} + +pub fn invalid_params(e: imbl_value::Error) -> RpcError { + RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_PARAMS_ERROR + } +} + +pub fn parse_error(e: imbl_value::Error) -> RpcError { + RpcError { + data: Some(e.to_string().into()), + ..yajrc::PARSE_ERROR + } +} + +pub struct Flat(pub A, pub B); +impl<'de, A, B> Deserialize<'de> for Flat +where + A: DeserializeOwned, + B: DeserializeOwned, +{ + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let v = Value::deserialize(deserializer)?; + let a = imbl_value::from_value(v.clone()).map_err(serde::de::Error::custom)?; + let b = imbl_value::from_value(v).map_err(serde::de::Error::custom)?; + Ok(Flat(a, b)) + } +} diff --git a/rpc-toolkit/tests/compat.rs b/rpc-toolkit/tests/compat.rs new file mode 100644 index 0000000..c3d59b1 --- /dev/null +++ b/rpc-toolkit/tests/compat.rs @@ -0,0 +1,214 @@ +use std::fmt::Display; +use std::str::FromStr; +use std::sync::Arc; + +use futures::FutureExt; +use hyper::Request; +use rpc_toolkit::clap::Arg; +use rpc_toolkit::hyper::http::Error as HttpError; +use rpc_toolkit::hyper::{Body, Response}; +use rpc_toolkit::rpc_server_helpers::{ + DynMiddlewareStage2, DynMiddlewareStage3, DynMiddlewareStage4, +}; +use rpc_toolkit::serde::{Deserialize, Serialize}; +use rpc_toolkit::url::Host; +use rpc_toolkit::yajrc::RpcError; +use rpc_toolkit::{command, rpc_server, run_cli, Context, Metadata}; + +#[derive(Debug, Clone)] +pub struct AppState(Arc); +impl From for () { + fn from(_: AppState) -> Self { + () + } +} + +#[derive(Debug)] +pub struct ConfigSeed { + host: Host, + port: u16, +} + +impl Context for AppState { + fn host(&self) -> Host<&str> { + match &self.0.host { + Host::Domain(s) => Host::Domain(s.as_str()), + Host::Ipv4(i) => Host::Ipv4(*i), + Host::Ipv6(i) => Host::Ipv6(*i), + } + } + fn port(&self) -> u16 { + self.0.port + } +} + +fn test_string() -> String { + "test".to_owned() +} + +#[command( + about = "Does the thing", + subcommands("dothething2::", self(dothething_impl(async))) +)] +async fn dothething< + U: Serialize + for<'a> Deserialize<'a> + FromStr + Clone + 'static, + E: Display, +>( + #[context] _ctx: AppState, + #[arg(short = 'a')] arg1: Option, + #[arg(short = 'b', default = "test_string")] val: String, + #[arg(short = 'c', help = "I am the flag `c`!", default)] arg3: bool, + #[arg(stdin)] structured: U, +) -> Result<(Option, String, bool, U), RpcError> { + Ok((arg1, val, arg3, structured)) +} + +async fn dothething_impl( + ctx: AppState, + parent_data: (Option, String, bool, U), +) -> Result { + Ok(format!( + "{:?}, {:?}, {}, {}, {}", + ctx, + parent_data.0, + parent_data.1, + parent_data.2, + serde_json::to_string_pretty(&parent_data.3)? + )) +} + +#[command(about = "Does the thing")] +fn dothething2 Deserialize<'a> + FromStr, E: Display>( + #[parent_data] parent_data: (Option, String, bool, U), + #[arg(stdin)] structured2: U, +) -> Result { + Ok(format!( + "{:?}, {}, {}, {}, {}", + parent_data.0, + parent_data.1, + parent_data.2, + serde_json::to_string_pretty(&parent_data.3)?, + serde_json::to_string_pretty(&structured2)?, + )) +} + +async fn cors( + req: &mut Request, + _: M, +) -> Result>, HttpError> { + if req.method() == hyper::Method::OPTIONS { + Ok(Err(Response::builder() + .header("Access-Control-Allow-Origin", "*") + .body(Body::empty())?)) + } else { + Ok(Ok(Box::new(|_, _| { + async move { + let res: DynMiddlewareStage3 = Box::new(|_, _| { + async move { + let res: DynMiddlewareStage4 = Box::new(|res| { + async move { + res.headers_mut() + .insert("Access-Control-Allow-Origin", "*".parse()?); + Ok::<_, HttpError>(()) + } + .boxed() + }); + Ok::<_, HttpError>(Ok(res)) + } + .boxed() + }); + Ok::<_, HttpError>(Ok(res)) + } + .boxed() + }))) + } +} + +#[tokio::test] +async fn test_rpc() { + use tokio::io::AsyncWriteExt; + + let seed = Arc::new(ConfigSeed { + host: Host::parse("localhost").unwrap(), + port: 8000, + }); + let server = rpc_server!({ + command: dothething::, + context: AppState(seed), + middleware: [ + cors, + ], + }); + let handle = tokio::spawn(server); + let mut cmd = tokio::process::Command::new("cargo") + .arg("test") + .arg("--package") + .arg("rpc-toolkit") + .arg("--test") + .arg("test") + .arg("--") + .arg("cli_test") + .arg("--exact") + .arg("--nocapture") + .arg("--") + // .arg("-b") + // .arg("test") + .arg("dothething2") + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::piped()) + .spawn() + .unwrap(); + cmd.stdin + .take() + .unwrap() + .write_all(b"TEST\nHAHA") + .await + .unwrap(); + let out = cmd.wait_with_output().await.unwrap(); + assert!(out.status.success()); + assert!(dbg!(std::str::from_utf8(&out.stdout).unwrap()) + .contains("\nNone, test, false, \"TEST\", \"HAHA\"\n")); + handle.abort(); +} + +#[test] +fn cli_test() { + let app = dothething::build_app(); + let mut skip = true; + let args = std::iter::once(std::ffi::OsString::from("cli_test")) + .chain(std::env::args_os().into_iter().skip_while(|a| { + if a == "--" { + skip = false; + return true; + } + skip + })) + .collect::>(); + if skip { + return; + } + let matches = app.get_matches_from(args); + let seed = Arc::new(ConfigSeed { + host: Host::parse("localhost").unwrap(), + port: 8000, + }); + dothething::cli_handler::(AppState(seed), (), None, &matches, "".into(), ()) + .unwrap(); +} + +#[test] +#[ignore] +fn cli_example() { + run_cli! ({ + command: dothething::, + app: app => app + .arg(Arg::with_name("host").long("host").short('h').takes_value(true)) + .arg(Arg::with_name("port").long("port").short('p').takes_value(true)), + context: matches => AppState(Arc::new(ConfigSeed { + host: Host::parse(matches.value_of("host").unwrap_or("localhost")).unwrap(), + port: matches.value_of("port").unwrap_or("8000").parse().unwrap(), + })) + }) +} + +//////////////////////////////////////////////// diff --git a/rpc-toolkit/tests/test.rs b/rpc-toolkit/tests/test.rs index c3d59b1..34922a1 100644 --- a/rpc-toolkit/tests/test.rs +++ b/rpc-toolkit/tests/test.rs @@ -1,214 +1 @@ -use std::fmt::Display; -use std::str::FromStr; -use std::sync::Arc; - -use futures::FutureExt; -use hyper::Request; -use rpc_toolkit::clap::Arg; -use rpc_toolkit::hyper::http::Error as HttpError; -use rpc_toolkit::hyper::{Body, Response}; -use rpc_toolkit::rpc_server_helpers::{ - DynMiddlewareStage2, DynMiddlewareStage3, DynMiddlewareStage4, -}; -use rpc_toolkit::serde::{Deserialize, Serialize}; -use rpc_toolkit::url::Host; -use rpc_toolkit::yajrc::RpcError; -use rpc_toolkit::{command, rpc_server, run_cli, Context, Metadata}; - -#[derive(Debug, Clone)] -pub struct AppState(Arc); -impl From for () { - fn from(_: AppState) -> Self { - () - } -} - -#[derive(Debug)] -pub struct ConfigSeed { - host: Host, - port: u16, -} - -impl Context for AppState { - fn host(&self) -> Host<&str> { - match &self.0.host { - Host::Domain(s) => Host::Domain(s.as_str()), - Host::Ipv4(i) => Host::Ipv4(*i), - Host::Ipv6(i) => Host::Ipv6(*i), - } - } - fn port(&self) -> u16 { - self.0.port - } -} - -fn test_string() -> String { - "test".to_owned() -} - -#[command( - about = "Does the thing", - subcommands("dothething2::", self(dothething_impl(async))) -)] -async fn dothething< - U: Serialize + for<'a> Deserialize<'a> + FromStr + Clone + 'static, - E: Display, ->( - #[context] _ctx: AppState, - #[arg(short = 'a')] arg1: Option, - #[arg(short = 'b', default = "test_string")] val: String, - #[arg(short = 'c', help = "I am the flag `c`!", default)] arg3: bool, - #[arg(stdin)] structured: U, -) -> Result<(Option, String, bool, U), RpcError> { - Ok((arg1, val, arg3, structured)) -} - -async fn dothething_impl( - ctx: AppState, - parent_data: (Option, String, bool, U), -) -> Result { - Ok(format!( - "{:?}, {:?}, {}, {}, {}", - ctx, - parent_data.0, - parent_data.1, - parent_data.2, - serde_json::to_string_pretty(&parent_data.3)? - )) -} - -#[command(about = "Does the thing")] -fn dothething2 Deserialize<'a> + FromStr, E: Display>( - #[parent_data] parent_data: (Option, String, bool, U), - #[arg(stdin)] structured2: U, -) -> Result { - Ok(format!( - "{:?}, {}, {}, {}, {}", - parent_data.0, - parent_data.1, - parent_data.2, - serde_json::to_string_pretty(&parent_data.3)?, - serde_json::to_string_pretty(&structured2)?, - )) -} - -async fn cors( - req: &mut Request, - _: M, -) -> Result>, HttpError> { - if req.method() == hyper::Method::OPTIONS { - Ok(Err(Response::builder() - .header("Access-Control-Allow-Origin", "*") - .body(Body::empty())?)) - } else { - Ok(Ok(Box::new(|_, _| { - async move { - let res: DynMiddlewareStage3 = Box::new(|_, _| { - async move { - let res: DynMiddlewareStage4 = Box::new(|res| { - async move { - res.headers_mut() - .insert("Access-Control-Allow-Origin", "*".parse()?); - Ok::<_, HttpError>(()) - } - .boxed() - }); - Ok::<_, HttpError>(Ok(res)) - } - .boxed() - }); - Ok::<_, HttpError>(Ok(res)) - } - .boxed() - }))) - } -} - -#[tokio::test] -async fn test_rpc() { - use tokio::io::AsyncWriteExt; - - let seed = Arc::new(ConfigSeed { - host: Host::parse("localhost").unwrap(), - port: 8000, - }); - let server = rpc_server!({ - command: dothething::, - context: AppState(seed), - middleware: [ - cors, - ], - }); - let handle = tokio::spawn(server); - let mut cmd = tokio::process::Command::new("cargo") - .arg("test") - .arg("--package") - .arg("rpc-toolkit") - .arg("--test") - .arg("test") - .arg("--") - .arg("cli_test") - .arg("--exact") - .arg("--nocapture") - .arg("--") - // .arg("-b") - // .arg("test") - .arg("dothething2") - .stdin(std::process::Stdio::piped()) - .stdout(std::process::Stdio::piped()) - .spawn() - .unwrap(); - cmd.stdin - .take() - .unwrap() - .write_all(b"TEST\nHAHA") - .await - .unwrap(); - let out = cmd.wait_with_output().await.unwrap(); - assert!(out.status.success()); - assert!(dbg!(std::str::from_utf8(&out.stdout).unwrap()) - .contains("\nNone, test, false, \"TEST\", \"HAHA\"\n")); - handle.abort(); -} - -#[test] -fn cli_test() { - let app = dothething::build_app(); - let mut skip = true; - let args = std::iter::once(std::ffi::OsString::from("cli_test")) - .chain(std::env::args_os().into_iter().skip_while(|a| { - if a == "--" { - skip = false; - return true; - } - skip - })) - .collect::>(); - if skip { - return; - } - let matches = app.get_matches_from(args); - let seed = Arc::new(ConfigSeed { - host: Host::parse("localhost").unwrap(), - port: 8000, - }); - dothething::cli_handler::(AppState(seed), (), None, &matches, "".into(), ()) - .unwrap(); -} - -#[test] -#[ignore] -fn cli_example() { - run_cli! ({ - command: dothething::, - app: app => app - .arg(Arg::with_name("host").long("host").short('h').takes_value(true)) - .arg(Arg::with_name("port").long("port").short('p').takes_value(true)), - context: matches => AppState(Arc::new(ConfigSeed { - host: Host::parse(matches.value_of("host").unwrap_or("localhost")).unwrap(), - port: matches.value_of("port").unwrap_or("8000").parse().unwrap(), - })) - }) -} - -//////////////////////////////////////////////// +pub struct App; From b246ea4179b959ad8b0fdc75403ce41ab1f22aa6 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 12 Dec 2023 15:12:27 -0700 Subject: [PATCH 03/79] finish cli --- rpc-toolkit/src/cli.rs | 183 +++++++++++++++++++++++++++++++++++-- rpc-toolkit/src/command.rs | 120 ++++++++---------------- rpc-toolkit/src/context.rs | 58 +----------- rpc-toolkit/src/lib.rs | 9 +- 4 files changed, 222 insertions(+), 148 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index c2d2aa4..eebeae3 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -1,4 +1,8 @@ -use clap::ArgMatches; +use std::ffi::OsString; + +use clap::{ArgMatches, CommandFactory, FromArgMatches}; +use futures::future::BoxFuture; +use futures::{Future, FutureExt}; use imbl_value::Value; use reqwest::{Client, Method}; use serde::de::DeserializeOwned; @@ -8,11 +12,178 @@ use yajrc::{GenericRpcMethod, Id, RpcError, RpcRequest}; use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo}; use crate::util::{combine, invalid_params, parse_error}; -use crate::ParentChain; +use crate::{CliBindings, ParentChain}; -pub struct CliApp { - pub(crate) command: DynCommand, - pub(crate) make_ctx: Box Result>, +impl DynCommand { + fn cli_app(&self) -> Option { + if let Some(cli) = &self.cli { + Some( + cli.cmd + .clone() + .name(self.name) + .subcommands(self.subcommands.iter().filter_map(|c| c.cli_app())), + ) + } else { + None + } + } + fn cmd_from_cli_matches( + &self, + matches: &ArgMatches, + parent: ParentInfo, + ) -> Result<(Vec<&'static str>, Value, &DynCommand), RpcError> { + let params = combine( + parent.params, + (self + .cli + .as_ref() + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .parser)(matches)?, + )?; + if let Some((cmd, matches)) = matches.subcommand() { + let mut method = parent.method; + method.push(self.name); + self.subcommands + .iter() + .find(|c| c.name == cmd) + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .cmd_from_cli_matches(matches, ParentInfo { method, params }) + } else { + Ok((parent.method, params, self)) + } + } +} + +struct CliApp { + cli: CliBindings, + commands: Vec>, +} +impl CliApp { + pub fn new( + commands: Vec>, + ) -> Self { + Self { + cli: CliBindings::from_parent::(), + commands, + } + } + fn cmd_from_cli_matches( + &self, + matches: &ArgMatches, + ) -> Result<(Vec<&'static str>, Value, &DynCommand), RpcError> { + if let Some((cmd, matches)) = matches.subcommand() { + Ok(self + .commands + .iter() + .find(|c| c.name == cmd) + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .cmd_from_cli_matches( + matches, + ParentInfo { + method: Vec::new(), + params: Value::Object(Default::default()), + }, + )?) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } +} + +pub struct CliAppAsync { + app: CliApp, + make_ctx: Box BoxFuture<'static, Result> + Send>, +} +impl CliAppAsync { + pub fn new< + Cmd: FromArgMatches + CommandFactory + Serialize + DeserializeOwned + Send, + F: FnOnce(Cmd) -> Fut + Send + 'static, + Fut: Future> + Send, + >( + make_ctx: F, + commands: Vec>, + ) -> Self { + Self { + app: CliApp::new::(commands), + make_ctx: Box::new(|args| { + async { make_ctx(imbl_value::from_value(args).map_err(parse_error)?).await }.boxed() + }), + } + } + pub async fn run(self, args: Vec) -> Result<(), RpcError> { + let cmd = self + .app + .cli + .cmd + .clone() + .subcommands(self.app.commands.iter().filter_map(|c| c.cli_app())); + let matches = cmd.get_matches_from(args); + let make_ctx_args = (self.app.cli.parser)(&matches)?; + let ctx = (self.make_ctx)(make_ctx_args).await?; + let (parent_method, params, cmd) = self.app.cmd_from_cli_matches(&matches)?; + let display = &cmd + .cli + .as_ref() + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .display; + let res = (cmd + .implementation + .as_ref() + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .async_impl)(ctx, parent_method, params) + .await?; + if let Some(display) = display { + display(res).map_err(parse_error) + } else { + Ok(()) + } + } +} + +pub struct CliAppSync { + app: CliApp, + make_ctx: Box Result + Send>, +} +impl CliAppSync { + pub fn new< + Cmd: FromArgMatches + CommandFactory + Serialize + DeserializeOwned + Send, + F: FnOnce(Cmd) -> Result + Send + 'static, + >( + make_ctx: F, + commands: Vec>, + ) -> Self { + Self { + app: CliApp::new::(commands), + make_ctx: Box::new(|args| make_ctx(imbl_value::from_value(args).map_err(parse_error)?)), + } + } + pub async fn run(self, args: Vec) -> Result<(), RpcError> { + let cmd = self + .app + .cli + .cmd + .clone() + .subcommands(self.app.commands.iter().filter_map(|c| c.cli_app())); + let matches = cmd.get_matches_from(args); + let make_ctx_args = (self.app.cli.parser)(&matches)?; + let ctx = (self.make_ctx)(make_ctx_args)?; + let (parent_method, params, cmd) = self.app.cmd_from_cli_matches(&matches)?; + let display = &cmd + .cli + .as_ref() + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .display; + let res = (cmd + .implementation + .as_ref() + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .sync_impl)(ctx, parent_method, params)?; + if let Some(display) = display { + display(res).map_err(parse_error) + } else { + Ok(()) + } + } } #[async_trait::async_trait] @@ -98,7 +269,7 @@ where &method.join("."), combine( imbl_value::to_value(&self).map_err(invalid_params)?, - imbl_value::to_value(&parent.args).map_err(invalid_params)?, + imbl_value::to_value(&parent.params).map_err(invalid_params)?, )?, ) .await?, diff --git a/rpc-toolkit/src/command.rs b/rpc-toolkit/src/command.rs index e6147cd..fefd057 100644 --- a/rpc-toolkit/src/command.rs +++ b/rpc-toolkit/src/command.rs @@ -1,5 +1,4 @@ use std::marker::PhantomData; -use std::sync::Arc; use clap::{ArgMatches, CommandFactory, FromArgMatches}; use futures::future::BoxFuture; @@ -7,10 +6,9 @@ use futures::FutureExt; use imbl_value::Value; use serde::de::DeserializeOwned; use serde::ser::Serialize; -use tokio::runtime::Runtime; use yajrc::RpcError; -use crate::util::{combine, extract, Flat}; +use crate::util::{extract, Flat}; /// Stores a command's implementation for a given context /// Can be created from anything that implements ParentCommand, AsyncCommand, or SyncCommand @@ -20,68 +18,21 @@ pub struct DynCommand { pub(crate) cli: Option, pub(crate) subcommands: Vec, } -impl DynCommand { - pub(crate) fn cli_app(&self) -> Option { - if let Some(cli) = &self.cli { - Some( - cli.cmd - .clone() - .name(self.name) - .subcommands(self.subcommands.iter().filter_map(|c| c.cli_app())), - ) - } else { - None - } - } - pub(crate) fn impl_from_cli_matches( - &self, - matches: &ArgMatches, - parent: Value, - ) -> Result, RpcError> { - let args = combine( - parent, - (self - .cli - .as_ref() - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .parser)(matches)?, - )?; - if let Some((cmd, matches)) = matches.subcommand() { - self.subcommands - .iter() - .find(|c| c.name == cmd) - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .impl_from_cli_matches(matches, args) - } else if let Some(implementation) = self.implementation.clone() { - Ok(implementation) - } else { - Err(yajrc::METHOD_NOT_FOUND_ERROR) - } - } -} -struct Implementation { - pub(crate) async_impl: Arc< +pub(crate) struct Implementation { + pub(crate) async_impl: Box< dyn Fn(Context, Vec<&'static str>, Value) -> BoxFuture<'static, Result>, >, - pub(crate) sync_impl: Arc, Value) -> Result>, -} -impl Clone for Implementation { - fn clone(&self) -> Self { - Self { - async_impl: self.async_impl.clone(), - sync_impl: self.sync_impl.clone(), - } - } + pub(crate) sync_impl: Box, Value) -> Result>, } -struct CliBindings { - cmd: clap::Command, - parser: Box Fn(&'a ArgMatches) -> Result + Send + Sync>, - display: Option Result<(), imbl_value::Error> + Send + Sync>>, +pub(crate) struct CliBindings { + pub(crate) cmd: clap::Command, + pub(crate) parser: Box Fn(&'a ArgMatches) -> Result + Send + Sync>, + pub(crate) display: Option Result<(), imbl_value::Error> + Send + Sync>>, } impl CliBindings { - fn from_parent() -> Self { + pub(crate) fn from_parent() -> Self { Self { cmd: Cmd::command(), parser: Box::new(|matches| { @@ -118,13 +69,18 @@ pub trait Command: DeserializeOwned + Sized + Send { /// Arguments are flattened out in the params object, so ensure that there are no collisions between the names of the arguments for your method and its parents pub struct ParentInfo { pub method: Vec<&'static str>, - pub args: T, + pub params: T, } /// This is automatically generated from a command based on its Parents. /// It can be used to generate a proof that one of the parents contains the necessary arguments that a subcommand requires. pub struct ParentChain(PhantomData); pub struct Contains(PhantomData); +impl Contains { + pub fn none() -> Self { + Self(PhantomData) + } +} impl From<(Contains, Contains)> for Contains> { fn from(_: (Contains, Contains)) -> Self { Self(PhantomData) @@ -142,9 +98,6 @@ impl ParentChain where Cmd: Command, { - pub fn none(&self) -> Contains { - Contains(PhantomData) - } pub fn child(&self) -> Contains { Contains(PhantomData) } @@ -198,20 +151,20 @@ pub trait AsyncCommand: LeafCommand { Vec::new() } } -impl Implementation { +impl Implementation { fn for_async>(contains: Contains) -> Self { drop(contains); Self { - async_impl: Arc::new(|ctx, method, params| { + async_impl: Box::new(|ctx, parent_method, params| { async move { - let parent = extract::(¶ms)?; + let parent_params = extract::(¶ms)?; imbl_value::to_value( &extract::(¶ms)? .implementation( ctx, ParentInfo { - method, - args: parent, + method: parent_method, + params: parent_params, }, ) .await @@ -224,11 +177,10 @@ impl Implementation { } .boxed() }), - sync_impl: Arc::new(|ctx, method, params| { - let parent = extract::(¶ms)?; + sync_impl: Box::new(|ctx, parent_method, params| { + let parent_params = extract::(¶ms)?; imbl_value::to_value( - &Runtime::new() - .unwrap() + &ctx.runtime() .block_on( extract::(¶ms) .map_err(|e| RpcError { @@ -238,8 +190,8 @@ impl Implementation { .implementation( ctx, ParentInfo { - method, - args: parent, + method: parent_method, + params: parent_params, }, ), ) @@ -253,7 +205,7 @@ impl Implementation { } } } -impl DynCommand { +impl DynCommand { pub fn from_async + FromArgMatches + CommandFactory + Serialize>( contains: Contains, ) -> Self { @@ -292,9 +244,9 @@ impl Implementation { drop(contains); Self { async_impl: if Cmd::BLOCKING { - Arc::new(|ctx, method, params| { + Box::new(|ctx, parent_method, params| { tokio::task::spawn_blocking(move || { - let parent = extract::(¶ms)?; + let parent_params = extract::(¶ms)?; imbl_value::to_value( &extract::(¶ms) .map_err(|e| RpcError { @@ -304,8 +256,8 @@ impl Implementation { .implementation( ctx, ParentInfo { - method, - args: parent, + method: parent_method, + params: parent_params, }, ) .map_err(|e| e.into())?, @@ -324,9 +276,9 @@ impl Implementation { .boxed() }) } else { - Arc::new(|ctx, method, params| { + Box::new(|ctx, parent_method, params| { async move { - let parent = extract::(¶ms)?; + let parent_params = extract::(¶ms)?; imbl_value::to_value( &extract::(¶ms) .map_err(|e| RpcError { @@ -336,8 +288,8 @@ impl Implementation { .implementation( ctx, ParentInfo { - method, - args: parent, + method: parent_method, + params: parent_params, }, ) .map_err(|e| e.into())?, @@ -350,7 +302,7 @@ impl Implementation { .boxed() }) }, - sync_impl: Arc::new(|ctx, method, params| { + sync_impl: Box::new(|ctx, method, params| { let parent = extract::(¶ms)?; imbl_value::to_value( &extract::(¶ms) @@ -362,7 +314,7 @@ impl Implementation { ctx, ParentInfo { method, - args: parent, + params: parent, }, ) .map_err(|e| e.into())?, diff --git a/rpc-toolkit/src/context.rs b/rpc-toolkit/src/context.rs index 91e9114..57035cb 100644 --- a/rpc-toolkit/src/context.rs +++ b/rpc-toolkit/src/context.rs @@ -1,57 +1,7 @@ -use std::sync::Arc; +use tokio::runtime::Handle; -use lazy_static::lazy_static; -use reqwest::Client; -use tokio::runtime::Runtime; -use url::{Host, Url}; - -lazy_static! { - static ref DEFAULT_CLIENT: Client = Client::new(); -} - -pub trait Context: Send { - fn runtime(&self) -> Arc { - Arc::new(Runtime::new().unwrap()) - } - fn protocol(&self) -> &str { - "http" - } - fn host(&self) -> Host<&str> { - Host::Ipv4([127, 0, 0, 1].into()) - } - fn port(&self) -> u16 { - 8080 - } - fn path(&self) -> &str { - "/" - } - fn url(&self) -> Url { - let mut url: Url = "http://localhost".parse().unwrap(); - url.set_scheme(self.protocol()).expect("protocol"); - url.set_host(Some(&self.host().to_string())).expect("host"); - url.set_port(Some(self.port())).expect("port"); - url.set_path(self.path()); - url - } - fn client(&self) -> &Client { - &*DEFAULT_CLIENT - } -} - -impl Context for () {} - -impl<'a, T: Context + 'a> From for Box { - fn from(ctx: T) -> Self { - Box::new(ctx) - } -} - -impl Context for (T, U) -where - T: Context, - U: Send, -{ - fn runtime(&self) -> Arc { - self.0.runtime() +pub trait Context: Send + 'static { + fn runtime(&self) -> Handle { + Handle::current() } } diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index 1613002..e4e47f9 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -1,5 +1,6 @@ pub use cli::*; pub use command::*; +pub use context::Context; /// `#[command(...)]` /// - `#[command(cli_only)]` -> executed by CLI instead of RPC server (leaf commands only) /// - `#[command(rpc_only)]` -> no CLI bindings (leaf commands only) @@ -24,10 +25,10 @@ pub use command::*; pub use rpc_toolkit_macro::command; pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc}; -pub(crate) mod cli; -pub(crate) mod command; +mod cli; +mod command; // pub mod command_helpers; -// mod context; +mod context; // mod metadata; // pub mod rpc_server_helpers; -pub(crate) mod util; +mod util; From 64a6c0034460dfaee6aaa76007a857557c65bb58 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 12 Dec 2023 20:43:04 -0700 Subject: [PATCH 04/79] wip --- Cargo.lock | 26 +++- rpc-toolkit/Cargo.toml | 3 +- rpc-toolkit/src/cli.rs | 25 ++-- rpc-toolkit/src/command.rs | 40 ++++-- rpc-toolkit/src/context.rs | 1 + rpc-toolkit/src/lib.rs | 2 + rpc-toolkit/src/server/http.rs | 0 rpc-toolkit/src/server/mod.rs | 224 +++++++++++++++++++++++++++++++ rpc-toolkit/src/server/socket.rs | 25 ++++ rpc-toolkit/src/util.rs | 7 + 10 files changed, 329 insertions(+), 24 deletions(-) create mode 100644 rpc-toolkit/src/server/http.rs create mode 100644 rpc-toolkit/src/server/mod.rs create mode 100644 rpc-toolkit/src/server/socket.rs diff --git a/Cargo.lock b/Cargo.lock index 6b3d782..208f7ae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,6 +84,28 @@ version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.40", +] + [[package]] name = "async-trait" version = "0.1.74" @@ -596,7 +618,8 @@ dependencies = [ [[package]] name = "imbl-value" version = "0.1.0" -source = "git+https://github.com/Start9Labs/imbl-value.git#929395141c3a882ac366c12ac9402d0ebaa2201b" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6d3d8cdfd1ac46aab6195692baf9c65dd34dcc7e7ddfb426a2c014736ba90c" dependencies = [ "imbl", "serde", @@ -929,6 +952,7 @@ dependencies = [ name = "rpc-toolkit" version = "0.2.3" dependencies = [ + "async-stream", "async-trait", "clap", "futures", diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml index c537815..aefba8e 100644 --- a/rpc-toolkit/Cargo.toml +++ b/rpc-toolkit/Cargo.toml @@ -16,11 +16,12 @@ cbor = ["serde_cbor"] default = ["cbor"] [dependencies] +async-stream = "0.3" async-trait = "0.1" clap = "4" futures = "0.3" hyper = { version = "1", features = ["server", "http1", "http2", "client"] } -imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } +imbl-value = "0.1" lazy_static = "1.4" openssl = { version = "0.10", features = ["vendored"] } reqwest = { version = "0.11" } diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index eebeae3..8afdc6d 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -14,7 +14,7 @@ use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo}; use crate::util::{combine, invalid_params, parse_error}; use crate::{CliBindings, ParentChain}; -impl DynCommand { +impl DynCommand { fn cli_app(&self) -> Option { if let Some(cli) = &self.cli { Some( @@ -54,11 +54,11 @@ impl DynCommand { } } -struct CliApp { +struct CliApp { cli: CliBindings, commands: Vec>, } -impl CliApp { +impl CliApp { pub fn new( commands: Vec>, ) -> Self { @@ -90,11 +90,11 @@ impl CliApp { } } -pub struct CliAppAsync { +pub struct CliAppAsync { app: CliApp, make_ctx: Box BoxFuture<'static, Result> + Send>, } -impl CliAppAsync { +impl CliAppAsync { pub fn new< Cmd: FromArgMatches + CommandFactory + Serialize + DeserializeOwned + Send, F: FnOnce(Cmd) -> Fut + Send + 'static, @@ -140,11 +140,11 @@ impl CliAppAsync { } } -pub struct CliAppSync { +pub struct CliAppSync { app: CliApp, make_ctx: Box Result + Send>, } -impl CliAppSync { +impl CliAppSync { pub fn new< Cmd: FromArgMatches + CommandFactory + Serialize + DeserializeOwned + Send, F: FnOnce(Cmd) -> Result + Send + 'static, @@ -187,11 +187,11 @@ impl CliAppSync { } #[async_trait::async_trait] -pub trait CliContext { +pub trait CliContext: crate::Context { async fn call_remote(&self, method: &str, params: Value) -> Result; } -pub trait CliContextHttp { +pub trait CliContextHttp: crate::Context { fn client(&self) -> &Client; fn url(&self) -> Url; } @@ -243,6 +243,7 @@ impl CliContext for T { } pub trait RemoteCommand: LeafCommand { + fn metadata() -> Context::Metadata; fn subcommands(chain: ParentChain) -> Vec> { drop(chain); Vec::new() @@ -257,6 +258,9 @@ where T::Err: From, Context: CliContext + Send + 'static, { + fn metadata() -> Context::Metadata { + T::metadata() + } async fn implementation( self, ctx: Context, @@ -276,4 +280,7 @@ where ) .map_err(parse_error)?) } + fn subcommands(chain: ParentChain) -> Vec> { + T::subcommands(chain) + } } diff --git a/rpc-toolkit/src/command.rs b/rpc-toolkit/src/command.rs index fefd057..05e2549 100644 --- a/rpc-toolkit/src/command.rs +++ b/rpc-toolkit/src/command.rs @@ -1,4 +1,5 @@ use std::marker::PhantomData; +use std::sync::Arc; use clap::{ArgMatches, CommandFactory, FromArgMatches}; use futures::future::BoxFuture; @@ -12,18 +13,22 @@ use crate::util::{extract, Flat}; /// Stores a command's implementation for a given context /// Can be created from anything that implements ParentCommand, AsyncCommand, or SyncCommand -pub struct DynCommand { +pub struct DynCommand { pub(crate) name: &'static str, + pub(crate) metadata: Context::Metadata, pub(crate) implementation: Option>, pub(crate) cli: Option, pub(crate) subcommands: Vec, } pub(crate) struct Implementation { - pub(crate) async_impl: Box< - dyn Fn(Context, Vec<&'static str>, Value) -> BoxFuture<'static, Result>, + pub(crate) async_impl: Arc< + dyn Fn(Context, Vec<&'static str>, Value) -> BoxFuture<'static, Result> + + Send + + Sync, >, - pub(crate) sync_impl: Box, Value) -> Result>, + pub(crate) sync_impl: + Box, Value) -> Result + Send + Sync>, } pub(crate) struct CliBindings { @@ -107,15 +112,17 @@ where } /// Implement this for a command that has no implementation, but simply exists to organize subcommands -pub trait ParentCommand: Command { +pub trait ParentCommand: Command { + fn metadata() -> Context::Metadata; fn subcommands(chain: ParentChain) -> Vec>; } -impl DynCommand { +impl DynCommand { pub fn from_parent< Cmd: ParentCommand + FromArgMatches + CommandFactory + Serialize, >() -> Self { Self { name: Cmd::NAME, + metadata: Cmd::metadata(), implementation: None, cli: Some(CliBindings::from_parent::()), subcommands: Cmd::subcommands(ParentChain::(PhantomData)), @@ -124,6 +131,7 @@ impl DynCommand { pub fn from_parent_no_cli>() -> Self { Self { name: Cmd::NAME, + metadata: Cmd::metadata(), implementation: None, cli: None, subcommands: Cmd::subcommands(ParentChain::(PhantomData)), @@ -140,7 +148,8 @@ pub trait LeafCommand: Command { /// Implement this if your Command's implementation is async #[async_trait::async_trait] -pub trait AsyncCommand: LeafCommand { +pub trait AsyncCommand: LeafCommand { + fn metadata() -> Context::Metadata; async fn implementation( self, ctx: Context, @@ -155,7 +164,7 @@ impl Implementation { fn for_async>(contains: Contains) -> Self { drop(contains); Self { - async_impl: Box::new(|ctx, parent_method, params| { + async_impl: Arc::new(|ctx, parent_method, params| { async move { let parent_params = extract::(¶ms)?; imbl_value::to_value( @@ -211,6 +220,7 @@ impl DynCommand { ) -> Self { Self { name: Cmd::NAME, + metadata: Cmd::metadata(), implementation: Some(Implementation::for_async::(contains)), cli: Some(CliBindings::from_leaf::()), subcommands: Cmd::subcommands(ParentChain::(PhantomData)), @@ -219,6 +229,7 @@ impl DynCommand { pub fn from_async_no_cli>(contains: Contains) -> Self { Self { name: Cmd::NAME, + metadata: Cmd::metadata(), implementation: Some(Implementation::for_async::(contains)), cli: None, subcommands: Cmd::subcommands(ParentChain::(PhantomData)), @@ -227,8 +238,9 @@ impl DynCommand { } /// Implement this if your Command's implementation is not async -pub trait SyncCommand: LeafCommand { +pub trait SyncCommand: LeafCommand { const BLOCKING: bool; + fn metadata() -> Context::Metadata; fn implementation( self, ctx: Context, @@ -239,12 +251,12 @@ pub trait SyncCommand: LeafCommand { Vec::new() } } -impl Implementation { +impl Implementation { fn for_sync>(contains: Contains) -> Self { drop(contains); Self { async_impl: if Cmd::BLOCKING { - Box::new(|ctx, parent_method, params| { + Arc::new(|ctx, parent_method, params| { tokio::task::spawn_blocking(move || { let parent_params = extract::(¶ms)?; imbl_value::to_value( @@ -276,7 +288,7 @@ impl Implementation { .boxed() }) } else { - Box::new(|ctx, parent_method, params| { + Arc::new(|ctx, parent_method, params| { async move { let parent_params = extract::(¶ms)?; imbl_value::to_value( @@ -327,12 +339,13 @@ impl Implementation { } } } -impl DynCommand { +impl DynCommand { pub fn from_sync + FromArgMatches + CommandFactory + Serialize>( contains: Contains, ) -> Self { Self { name: Cmd::NAME, + metadata: Cmd::metadata(), implementation: Some(Implementation::for_sync::(contains)), cli: Some(CliBindings::from_leaf::()), subcommands: Cmd::subcommands(ParentChain::(PhantomData)), @@ -341,6 +354,7 @@ impl DynCommand { pub fn from_sync_no_cli>(contains: Contains) -> Self { Self { name: Cmd::NAME, + metadata: Cmd::metadata(), implementation: Some(Implementation::for_sync::(contains)), cli: None, subcommands: Cmd::subcommands(ParentChain::(PhantomData)), diff --git a/rpc-toolkit/src/context.rs b/rpc-toolkit/src/context.rs index 57035cb..b718129 100644 --- a/rpc-toolkit/src/context.rs +++ b/rpc-toolkit/src/context.rs @@ -1,6 +1,7 @@ use tokio::runtime::Handle; pub trait Context: Send + 'static { + type Metadata: Default; fn runtime(&self) -> Handle { Handle::current() } diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index e4e47f9..7c5807e 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -23,6 +23,7 @@ pub use context::Context; /// /// See also: [arg](rpc_toolkit_macro::arg), [context](rpc_toolkit_macro::context) pub use rpc_toolkit_macro::command; +pub use server::*; pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc}; mod cli; @@ -31,4 +32,5 @@ mod command; mod context; // mod metadata; // pub mod rpc_server_helpers; +mod server; mod util; diff --git a/rpc-toolkit/src/server/http.rs b/rpc-toolkit/src/server/http.rs new file mode 100644 index 0000000..e69de29 diff --git a/rpc-toolkit/src/server/mod.rs b/rpc-toolkit/src/server/mod.rs new file mode 100644 index 0000000..be25722 --- /dev/null +++ b/rpc-toolkit/src/server/mod.rs @@ -0,0 +1,224 @@ +use std::sync::Arc; + +use futures::future::{join_all, BoxFuture}; +use futures::stream::{BoxStream, Fuse}; +use futures::{Future, FutureExt, Stream, StreamExt, TryStreamExt}; +use imbl_value::Value; +use tokio::runtime::Handle; +use tokio::task::JoinHandle; +use yajrc::{AnyParams, RpcError, RpcMethod, RpcRequest, RpcResponse, SingleOrBatchRpcRequest}; + +use crate::util::{invalid_request, parse_error}; +use crate::DynCommand; + +mod http; +mod socket; + +pub use http::*; +pub use socket::*; + +impl DynCommand { + fn cmd_from_method( + &self, + method: &[&str], + parent_method: Vec<&'static str>, + ) -> Result<(Vec<&'static str>, &DynCommand), RpcError> { + let mut ret_method = parent_method; + ret_method.push(self.name); + if let Some((cmd, rest)) = method.split_first() { + self.subcommands + .iter() + .find(|c| c.name == *cmd) + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .cmd_from_method(rest, ret_method) + } else { + Ok((ret_method, self)) + } + } +} + +pub struct Server { + commands: Vec>, + make_ctx: Arc BoxFuture<'static, Result> + Send + Sync>, +} +impl Server { + pub fn new< + F: Fn() -> Fut + Send + Sync + 'static, + Fut: Future> + Send + 'static, + >( + commands: Vec>, + make_ctx: F, + ) -> Self { + Server { + commands, + make_ctx: Arc::new(move || make_ctx().boxed()), + } + } + + pub fn handle_command( + &self, + method: &str, + params: Value, + ) -> impl Future> + Send + 'static { + let from_self = (|| { + let method: Vec<_> = method.split(".").collect(); + let (cmd, rest) = method.split_first().ok_or(yajrc::METHOD_NOT_FOUND_ERROR)?; + let (method, cmd) = self + .commands + .iter() + .find(|c| c.name == *cmd) + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .cmd_from_method(rest, Vec::new())?; + Ok::<_, RpcError>(( + cmd.implementation + .as_ref() + .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? + .async_impl + .clone(), + self.make_ctx.clone(), + method, + params, + )) + })(); + + async move { + let (implementation, make_ctx, method, params) = from_self?; + implementation(make_ctx().await?, method, params).await + } + } + + fn handle_single_request( + &self, + RpcRequest { id, method, params }: RpcRequest, + ) -> impl Future + Send + 'static { + let handle = (|| { + Ok::<_, RpcError>(self.handle_command( + method.as_str(), + match params { + AnyParams::Named(a) => serde_json::Value::Object(a).into(), + _ => { + return Err(RpcError { + data: Some("positional parameters unsupported".into()), + ..yajrc::INVALID_PARAMS_ERROR + }) + } + }, + )) + })(); + async move { + RpcResponse { + id, + result: match handle { + Ok(handle) => handle.await.map(serde_json::Value::from), + Err(e) => Err(e), + }, + } + } + } + + pub fn handle(&self, request: Value) -> BoxFuture<'static, Result> { + let request = + imbl_value::from_value::(request).map_err(invalid_request); + match request { + Ok(SingleOrBatchRpcRequest::Single(req)) => { + let fut = self.handle_single_request(req); + async { imbl_value::to_value(&fut.await).map_err(parse_error) }.boxed() + } + Ok(SingleOrBatchRpcRequest::Batch(reqs)) => { + let futs: Vec<_> = reqs + .into_iter() + .map(|req| self.handle_single_request(req)) + .collect(); + async { imbl_value::to_value(&join_all(futs).await).map_err(parse_error) }.boxed() + } + Err(e) => async { Err(e) }.boxed(), + } + } + + pub fn stream<'a>( + &'a self, + requests: impl Stream> + Send + 'a, + ) -> impl Stream> + 'a { + let mut running = RunningCommands::default(); + let mut requests = requests.boxed().fuse(); + async fn next<'a, Context: crate::Context>( + server: &'a Server, + running: &mut RunningCommands, + requests: &mut Fuse>>, + ) -> Result, RpcError> { + loop { + tokio::select! { + req = requests.try_next() => { + let req = req?; + if let Some(req) = req { + running.running.push(tokio::spawn(server.handle(req))); + } else { + running.closed = true; + } + } + res = running.try_next() => { + return res; + } + } + } + } + async_stream::try_stream! { + while let Some(res) = next(self, &mut running, &mut requests).await? { + yield res; + } + } + } +} + +#[derive(Default)] +struct RunningCommands { + closed: bool, + running: Vec>>, +} + +impl Stream for RunningCommands { + type Item = Result; + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let item = self + .running + .iter_mut() + .enumerate() + .find_map(|(i, f)| match f.poll_unpin(cx) { + std::task::Poll::Pending => None, + std::task::Poll::Ready(e) => Some(( + i, + e.map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + }) + .and_then(|a| a), + )), + }); + match item { + Some((idx, res)) => { + drop(self.running.swap_remove(idx)); + std::task::Poll::Ready(Some(res)) + } + None => { + if !self.closed || !self.running.is_empty() { + std::task::Poll::Pending + } else { + std::task::Poll::Ready(None) + } + } + } + } +} +impl Drop for RunningCommands { + fn drop(&mut self) { + for hdl in &self.running { + hdl.abort(); + } + if let Ok(rt) = Handle::try_current() { + rt.block_on(join_all(std::mem::take(&mut self.running).into_iter())); + } + } +} diff --git a/rpc-toolkit/src/server/socket.rs b/rpc-toolkit/src/server/socket.rs new file mode 100644 index 0000000..42e8aa3 --- /dev/null +++ b/rpc-toolkit/src/server/socket.rs @@ -0,0 +1,25 @@ +use futures::{AsyncWrite, Future, Stream}; +use tokio::io::AsyncRead; +use tokio::sync::oneshot; +use yajrc::RpcError; + +use crate::Server; + +pub struct ShutdownHandle(oneshot::Sender<()>); + +pub struct SocketServer { + server: Server, +} +impl SocketServer { + pub fn run_json( + &self, + listener: impl Stream, + ) -> (ShutdownHandle, impl Future>) { + let (shutdown_send, shutdown_recv) = oneshot::channel(); + (ShutdownHandle(shutdown_send), async move { + //asdf + //adf + Ok(()) + }) + } +} diff --git a/rpc-toolkit/src/util.rs b/rpc-toolkit/src/util.rs index eccfad9..b56cb47 100644 --- a/rpc-toolkit/src/util.rs +++ b/rpc-toolkit/src/util.rs @@ -35,6 +35,13 @@ pub fn invalid_params(e: imbl_value::Error) -> RpcError { } } +pub fn invalid_request(e: imbl_value::Error) -> RpcError { + RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_REQUEST_ERROR + } +} + pub fn parse_error(e: imbl_value::Error) -> RpcError { RpcError { data: Some(e.to_string().into()), From be51ffdd87966444b22febf2c0c94ef6ec64957e Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 13 Dec 2023 01:33:57 -0700 Subject: [PATCH 05/79] example wip --- Cargo.lock | 74 ++++-- rpc-toolkit/Cargo.toml | 5 +- rpc-toolkit/src/cli.rs | 136 ++++++++--- rpc-toolkit/src/command.rs | 68 ++++-- rpc-toolkit/src/context.rs | 2 +- rpc-toolkit/src/server/http.rs | 60 +++++ rpc-toolkit/src/server/mod.rs | 139 +++-------- rpc-toolkit/src/server/socket.rs | 108 +++++++-- rpc-toolkit/src/util.rs | 82 ++++++- rpc-toolkit/tests/compat.rs | 391 +++++++++++++++---------------- rpc-toolkit/tests/test.rs | 110 ++++++++- 11 files changed, 769 insertions(+), 406 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 208f7ae..cafdfe2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,7 +103,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -114,7 +114,7 @@ checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -196,6 +196,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -210,6 +211,18 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.41", +] + [[package]] name = "clap_lex" version = "0.6.0" @@ -355,7 +368,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -464,6 +477,12 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "hermit-abi" version = "0.3.3" @@ -513,6 +532,19 @@ dependencies = [ "http 1.0.0", ] +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "pin-project-lite", +] + [[package]] name = "httparse" version = "1.8.0" @@ -790,7 +822,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -956,6 +988,8 @@ dependencies = [ "async-trait", "clap", "futures", + "http 1.0.0", + "http-body-util", "hyper 1.0.1", "imbl-value", "lazy_static", @@ -967,6 +1001,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "tokio-stream", "url", "yajrc", ] @@ -1079,7 +1114,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -1168,9 +1203,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.40" +version = "2.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e" +checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" dependencies = [ "proc-macro2", "quote", @@ -1228,7 +1263,7 @@ checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -1273,7 +1308,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.41", ] [[package]] @@ -1286,6 +1321,17 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.10" @@ -1417,7 +1463,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.41", "wasm-bindgen-shared", ] @@ -1451,7 +1497,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.41", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1638,9 +1684,9 @@ dependencies = [ [[package]] name = "yajrc" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc08b562507a1674a1ef886a1aedeeb19d41462386ae09f634995d41bbef87d3" +checksum = "47cb33cb21fb6923a0dd074fd20dfd98fc3758103b7e2607db1354b4a86ef37c" dependencies = [ "anyhow", "serde", @@ -1677,5 +1723,5 @@ checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.40", + "syn 2.0.41", ] diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml index aefba8e..cc97b2a 100644 --- a/rpc-toolkit/Cargo.toml +++ b/rpc-toolkit/Cargo.toml @@ -18,8 +18,10 @@ default = ["cbor"] [dependencies] async-stream = "0.3" async-trait = "0.1" -clap = "4" +clap = { version = "4", features = ["derive"] } futures = "0.3" +http = "1" +http-body-util = "0.1" hyper = { version = "1", features = ["server", "http1", "http2", "client"] } imbl-value = "0.1" lazy_static = "1.4" @@ -31,5 +33,6 @@ serde_cbor = { version = "0.11", optional = true } serde_json = "1.0" thiserror = "1.0" tokio = { version = "1", features = ["full"] } +tokio-stream = { version = "0.1", features = ["io-util", "net"] } url = "2" yajrc = "0.1" diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 8afdc6d..a696105 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -7,12 +7,17 @@ use imbl_value::Value; use reqwest::{Client, Method}; use serde::de::DeserializeOwned; use serde::Serialize; +use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}; use url::Url; -use yajrc::{GenericRpcMethod, Id, RpcError, RpcRequest}; +use yajrc::{Id, RpcError}; use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo}; -use crate::util::{combine, invalid_params, parse_error}; -use crate::{CliBindings, ParentChain}; +use crate::util::{combine, internal_error, invalid_params, parse_error}; +use crate::{CliBindings, SyncCommand}; + +type GenericRpcMethod<'a> = yajrc::GenericRpcMethod<&'a str, Value, Value>; +type RpcRequest<'a> = yajrc::RpcRequest>; +type RpcResponse<'a> = yajrc::RpcResponse>; impl DynCommand { fn cli_app(&self) -> Option { @@ -55,7 +60,7 @@ impl DynCommand { } struct CliApp { - cli: CliBindings, + cli: CliBindings, commands: Vec>, } impl CliApp { @@ -110,6 +115,8 @@ impl CliAppAsync { }), } } +} +impl CliAppAsync { pub async fn run(self, args: Vec) -> Result<(), RpcError> { let cmd = self .app @@ -130,10 +137,10 @@ impl CliAppAsync { .implementation .as_ref() .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .async_impl)(ctx, parent_method, params) + .async_impl)(ctx.clone(), parent_method.clone(), params.clone()) .await?; if let Some(display) = display { - display(res).map_err(parse_error) + display(ctx, parent_method, params, res).map_err(parse_error) } else { Ok(()) } @@ -157,6 +164,8 @@ impl CliAppSync { make_ctx: Box::new(|args| make_ctx(imbl_value::from_value(args).map_err(parse_error)?)), } } +} +impl CliAppSync { pub async fn run(self, args: Vec) -> Result<(), RpcError> { let cmd = self .app @@ -177,9 +186,9 @@ impl CliAppSync { .implementation .as_ref() .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .sync_impl)(ctx, parent_method, params)?; + .sync_impl)(ctx.clone(), parent_method.clone(), params.clone())?; if let Some(display) = display { - display(res).map_err(parse_error) + display(ctx, parent_method, params, res).map_err(parse_error) } else { Ok(()) } @@ -191,14 +200,12 @@ pub trait CliContext: crate::Context { async fn call_remote(&self, method: &str, params: Value) -> Result; } +#[async_trait::async_trait] pub trait CliContextHttp: crate::Context { fn client(&self) -> &Client; fn url(&self) -> Url; -} -#[async_trait::async_trait] -impl CliContext for T { async fn call_remote(&self, method: &str, params: Value) -> Result { - let rpc_req: RpcRequest> = RpcRequest { + let rpc_req = RpcRequest { id: Some(Id::Number(0.into())), method: GenericRpcMethod::new(method), params, @@ -222,33 +229,61 @@ impl CliContext for T { .body(body) .send() .await?; - Ok( - match res - .headers() - .get("content-type") - .and_then(|v| v.to_str().ok()) - { - Some("application/json") => serde_json::from_slice(&*res.bytes().await?)?, - #[cfg(feature = "cbor")] - Some("application/cbor") => serde_cbor::from_slice(&*res.bytes().await?)?, - _ => { - return Err(RpcError { - data: Some("missing content type".into()), - ..yajrc::INTERNAL_ERROR - }) - } - }, - ) + + match res + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + { + Some("application/json") => { + serde_json::from_slice::(&*res.bytes().await.map_err(internal_error)?) + .map_err(parse_error)? + .result + } + #[cfg(feature = "cbor")] + Some("application/cbor") => { + serde_cbor::from_slice::(&*res.bytes().await.map_err(internal_error)?) + .map_err(parse_error)? + .result + } + _ => Err(internal_error("missing content type")), + } } } -pub trait RemoteCommand: LeafCommand { - fn metadata() -> Context::Metadata; - fn subcommands(chain: ParentChain) -> Vec> { - drop(chain); - Vec::new() +#[async_trait::async_trait] +pub trait CliContextSocket: crate::Context { + type Stream: AsyncRead + AsyncWrite + Send; + async fn connect(&self) -> std::io::Result; + async fn call_remote(&self, method: &str, params: Value) -> Result { + let rpc_req = RpcRequest { + id: Some(Id::Number(0.into())), + method: GenericRpcMethod::new(method), + params, + }; + let conn = self.connect().await.map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + })?; + tokio::pin!(conn); + let mut buf = serde_json::to_vec(&rpc_req).map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + })?; + buf.push(b'\n'); + conn.write_all(&buf).await.map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + })?; + let mut line = String::new(); + BufReader::new(conn).read_line(&mut line).await?; + serde_json::from_str::(&line) + .map_err(parse_error)? + .result } } + +pub trait RemoteCommand: LeafCommand {} #[async_trait::async_trait] impl AsyncCommand for T where @@ -258,9 +293,6 @@ where T::Err: From, Context: CliContext + Send + 'static, { - fn metadata() -> Context::Metadata { - T::metadata() - } async fn implementation( self, ctx: Context, @@ -280,7 +312,33 @@ where ) .map_err(parse_error)?) } - fn subcommands(chain: ParentChain) -> Vec> { - T::subcommands(chain) +} + +impl SyncCommand for T +where + T: RemoteCommand + Send + Serialize, + T::Parent: Serialize, + T::Ok: DeserializeOwned, + T::Err: From, + Context: CliContext + Send + 'static, +{ + const BLOCKING: bool = true; + fn implementation( + self, + ctx: Context, + parent: ParentInfo, + ) -> Result { + let mut method = parent.method; + method.push(Self::NAME); + Ok( + imbl_value::from_value(ctx.runtime().block_on(ctx.call_remote( + &method.join("."), + combine( + imbl_value::to_value(&self).map_err(invalid_params)?, + imbl_value::to_value(&parent.params).map_err(invalid_params)?, + )?, + ))?) + .map_err(parse_error)?, + ) } } diff --git a/rpc-toolkit/src/command.rs b/rpc-toolkit/src/command.rs index 05e2549..3dabecf 100644 --- a/rpc-toolkit/src/command.rs +++ b/rpc-toolkit/src/command.rs @@ -17,7 +17,7 @@ pub struct DynCommand { pub(crate) name: &'static str, pub(crate) metadata: Context::Metadata, pub(crate) implementation: Option>, - pub(crate) cli: Option, + pub(crate) cli: Option>, pub(crate) subcommands: Vec, } @@ -31,12 +31,18 @@ pub(crate) struct Implementation { Box, Value) -> Result + Send + Sync>, } -pub(crate) struct CliBindings { +pub(crate) struct CliBindings { pub(crate) cmd: clap::Command, pub(crate) parser: Box Fn(&'a ArgMatches) -> Result + Send + Sync>, - pub(crate) display: Option Result<(), imbl_value::Error> + Send + Sync>>, + pub(crate) display: Option< + Box< + dyn Fn(Context, Vec<&'static str>, Value, Value) -> Result<(), imbl_value::Error> + + Send + + Sync, + >, + >, } -impl CliBindings { +impl CliBindings { pub(crate) fn from_parent() -> Self { Self { cmd: Cmd::command(), @@ -53,10 +59,19 @@ impl CliBindings { display: None, } } - fn from_leaf() -> Self { + fn from_leaf>() -> Self + { Self { - display: Some(Box::new(|res| { - Ok(Cmd::display(imbl_value::from_value(res)?)) + display: Some(Box::new(|ctx, parent_method, params, res| { + let parent_params = imbl_value::from_value(params.clone())?; + Ok(imbl_value::from_value::(params)?.display( + ctx, + ParentInfo { + method: parent_method, + params: parent_params, + }, + imbl_value::from_value(res)?, + )) })), ..Self::from_parent::() } @@ -113,13 +128,18 @@ where /// Implement this for a command that has no implementation, but simply exists to organize subcommands pub trait ParentCommand: Command { - fn metadata() -> Context::Metadata; + fn metadata() -> Context::Metadata { + Context::Metadata::default() + } fn subcommands(chain: ParentChain) -> Vec>; } impl DynCommand { pub fn from_parent< Cmd: ParentCommand + FromArgMatches + CommandFactory + Serialize, - >() -> Self { + >( + contains: Contains, + ) -> Self { + drop(contains); Self { name: Cmd::NAME, metadata: Cmd::metadata(), @@ -128,7 +148,10 @@ impl DynCommand { subcommands: Cmd::subcommands(ParentChain::(PhantomData)), } } - pub fn from_parent_no_cli>() -> Self { + pub fn from_parent_no_cli>( + contains: Contains, + ) -> Self { + drop(contains); Self { name: Cmd::NAME, metadata: Cmd::metadata(), @@ -140,25 +163,27 @@ impl DynCommand { } /// Implement this for any command with an implementation -pub trait LeafCommand: Command { +pub trait LeafCommand: Command { type Ok: DeserializeOwned + Serialize + Send; type Err: From + Into + Send; - fn display(res: Self::Ok); + fn metadata() -> Context::Metadata { + Context::Metadata::default() + } + fn display(self, ctx: Context, parent: ParentInfo, res: Self::Ok); + fn subcommands(chain: ParentChain) -> Vec> { + drop(chain); + Vec::new() + } } /// Implement this if your Command's implementation is async #[async_trait::async_trait] -pub trait AsyncCommand: LeafCommand { - fn metadata() -> Context::Metadata; +pub trait AsyncCommand: LeafCommand { async fn implementation( self, ctx: Context, parent: ParentInfo, ) -> Result; - fn subcommands(chain: ParentChain) -> Vec> { - drop(chain); - Vec::new() - } } impl Implementation { fn for_async>(contains: Contains) -> Self { @@ -238,18 +263,13 @@ impl DynCommand { } /// Implement this if your Command's implementation is not async -pub trait SyncCommand: LeafCommand { +pub trait SyncCommand: LeafCommand { const BLOCKING: bool; - fn metadata() -> Context::Metadata; fn implementation( self, ctx: Context, parent: ParentInfo, ) -> Result; - fn subcommands(chain: ParentChain) -> Vec> { - drop(chain); - Vec::new() - } } impl Implementation { fn for_sync>(contains: Contains) -> Self { diff --git a/rpc-toolkit/src/context.rs b/rpc-toolkit/src/context.rs index b718129..2c47228 100644 --- a/rpc-toolkit/src/context.rs +++ b/rpc-toolkit/src/context.rs @@ -1,7 +1,7 @@ use tokio::runtime::Handle; pub trait Context: Send + 'static { - type Metadata: Default; + type Metadata: Default + Send + Sync; fn runtime(&self) -> Handle { Handle::current() } diff --git a/rpc-toolkit/src/server/http.rs b/rpc-toolkit/src/server/http.rs index e69de29..29bdee6 100644 --- a/rpc-toolkit/src/server/http.rs +++ b/rpc-toolkit/src/server/http.rs @@ -0,0 +1,60 @@ +use std::task::Context; + +use futures::future::BoxFuture; +use http::request::Parts; +use hyper::body::{Bytes, Incoming}; +use hyper::{Request, Response}; +use yajrc::{RpcRequest, RpcResponse}; + +type BoxBody = http_body_util::combinators::BoxBody; + +#[async_trait::async_trait] +pub trait Middleware { + type ProcessHttpRequestResult; + async fn process_http_request( + &self, + req: &mut Request, + ) -> Result>>; + type ProcessRpcRequestResult; + async fn process_rpc_request( + &self, + prev: Self::ProcessHttpRequestResult, + metadata: &Context::Metadata, + req: &mut RpcRequest, + ) -> Result; + type ProcessRpcResponseResult; + async fn process_rpc_response( + &self, + prev: Self::ProcessRpcRequestResult, + res: &mut RpcResponse, + ) -> Self::ProcessRpcResponseResult; + async fn process_http_response( + &self, + prev: Self::ProcessRpcResponseResult, + res: &mut Response, + ); +} + +// pub struct DynMiddleware { +// process_http_request: Box< +// dyn for<'a> Fn( +// &'a mut Request, +// ) -> BoxFuture< +// 'a, +// Result, hyper::Result>>, +// > + Send +// + Sync, +// >, +// } +// type DynProcessRpcRequest<'m, Context: crate::Context> = Box< +// dyn for<'a> FnOnce( +// &'a Context::Metadata, +// &'a mut RpcRequest, +// ) +// -> BoxFuture<'a, Result, DynSkipHandler<'m>>> +// + Send +// + Sync +// + 'm, +// >; +// type DynProcessRpcResponse<'m> = +// Box FnOnce(&'a mut RpcResponse) -> BoxFuture<'a, DynProcessHttpResponse<'m>>>; diff --git a/rpc-toolkit/src/server/mod.rs b/rpc-toolkit/src/server/mod.rs index be25722..2ee1ca1 100644 --- a/rpc-toolkit/src/server/mod.rs +++ b/rpc-toolkit/src/server/mod.rs @@ -1,16 +1,19 @@ +use std::borrow::Cow; use std::sync::Arc; use futures::future::{join_all, BoxFuture}; -use futures::stream::{BoxStream, Fuse}; -use futures::{Future, FutureExt, Stream, StreamExt, TryStreamExt}; +use futures::{Future, FutureExt, Stream, StreamExt}; use imbl_value::Value; -use tokio::runtime::Handle; -use tokio::task::JoinHandle; -use yajrc::{AnyParams, RpcError, RpcMethod, RpcRequest, RpcResponse, SingleOrBatchRpcRequest}; +use yajrc::{AnyParams, AnyRpcMethod, RpcError, RpcMethod}; -use crate::util::{invalid_request, parse_error}; +use crate::util::{invalid_request, JobRunner}; use crate::DynCommand; +type GenericRpcMethod = yajrc::GenericRpcMethod; +type RpcRequest = yajrc::RpcRequest; +type RpcResponse = yajrc::RpcResponse; +type SingleOrBatchRpcRequest = yajrc::SingleOrBatchRpcRequest; + mod http; mod socket; @@ -91,134 +94,58 @@ impl Server { &self, RpcRequest { id, method, params }: RpcRequest, ) -> impl Future + Send + 'static { - let handle = (|| { - Ok::<_, RpcError>(self.handle_command( - method.as_str(), - match params { - AnyParams::Named(a) => serde_json::Value::Object(a).into(), - _ => { - return Err(RpcError { - data: Some("positional parameters unsupported".into()), - ..yajrc::INVALID_PARAMS_ERROR - }) - } - }, - )) - })(); + let handle = (|| Ok::<_, RpcError>(self.handle_command(method.as_str(), params)))(); async move { RpcResponse { id, result: match handle { - Ok(handle) => handle.await.map(serde_json::Value::from), + Ok(handle) => handle.await, Err(e) => Err(e), }, } } } - pub fn handle(&self, request: Value) -> BoxFuture<'static, Result> { - let request = - imbl_value::from_value::(request).map_err(invalid_request); - match request { + pub fn handle( + &self, + request: Result, + ) -> BoxFuture<'static, Result> { + match request.and_then(|request| { + imbl_value::from_value::(request).map_err(invalid_request) + }) { Ok(SingleOrBatchRpcRequest::Single(req)) => { let fut = self.handle_single_request(req); - async { imbl_value::to_value(&fut.await).map_err(parse_error) }.boxed() + async { imbl_value::to_value(&fut.await) }.boxed() } Ok(SingleOrBatchRpcRequest::Batch(reqs)) => { let futs: Vec<_> = reqs .into_iter() .map(|req| self.handle_single_request(req)) .collect(); - async { imbl_value::to_value(&join_all(futs).await).map_err(parse_error) }.boxed() + async { imbl_value::to_value(&join_all(futs).await) }.boxed() } - Err(e) => async { Err(e) }.boxed(), + Err(e) => async { + imbl_value::to_value(&RpcResponse { + id: None, + result: Err(e), + }) + } + .boxed(), } } pub fn stream<'a>( &'a self, requests: impl Stream> + Send + 'a, - ) -> impl Stream> + 'a { - let mut running = RunningCommands::default(); - let mut requests = requests.boxed().fuse(); - async fn next<'a, Context: crate::Context>( - server: &'a Server, - running: &mut RunningCommands, - requests: &mut Fuse>>, - ) -> Result, RpcError> { - loop { - tokio::select! { - req = requests.try_next() => { - let req = req?; - if let Some(req) = req { - running.running.push(tokio::spawn(server.handle(req))); - } else { - running.closed = true; - } - } - res = running.try_next() => { - return res; - } - } - } - } + ) -> impl Stream> + 'a { async_stream::try_stream! { - while let Some(res) = next(self, &mut running, &mut requests).await? { + let mut runner = JobRunner::new(); + let requests = requests.fuse().map(|req| self.handle(req)); + tokio::pin!(requests); + + while let Some(res) = runner.next_result(&mut requests).await.transpose()? { yield res; } } } } - -#[derive(Default)] -struct RunningCommands { - closed: bool, - running: Vec>>, -} - -impl Stream for RunningCommands { - type Item = Result; - fn poll_next( - mut self: std::pin::Pin<&mut Self>, - cx: &mut std::task::Context<'_>, - ) -> std::task::Poll> { - let item = self - .running - .iter_mut() - .enumerate() - .find_map(|(i, f)| match f.poll_unpin(cx) { - std::task::Poll::Pending => None, - std::task::Poll::Ready(e) => Some(( - i, - e.map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INTERNAL_ERROR - }) - .and_then(|a| a), - )), - }); - match item { - Some((idx, res)) => { - drop(self.running.swap_remove(idx)); - std::task::Poll::Ready(Some(res)) - } - None => { - if !self.closed || !self.running.is_empty() { - std::task::Poll::Pending - } else { - std::task::Poll::Ready(None) - } - } - } - } -} -impl Drop for RunningCommands { - fn drop(&mut self) { - for hdl in &self.running { - hdl.abort(); - } - if let Ok(rt) = Handle::try_current() { - rt.block_on(join_all(std::mem::take(&mut self.running).into_iter())); - } - } -} diff --git a/rpc-toolkit/src/server/socket.rs b/rpc-toolkit/src/server/socket.rs index 42e8aa3..e42b42c 100644 --- a/rpc-toolkit/src/server/socket.rs +++ b/rpc-toolkit/src/server/socket.rs @@ -1,25 +1,95 @@ -use futures::{AsyncWrite, Future, Stream}; -use tokio::io::AsyncRead; -use tokio::sync::oneshot; +use std::path::Path; +use std::sync::Arc; + +use futures::{Future, Stream, StreamExt, TryStreamExt}; +use imbl_value::Value; +use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}; +use tokio::net::{TcpListener, ToSocketAddrs, UnixListener}; +use tokio::sync::OnceCell; use yajrc::RpcError; +use crate::util::{parse_error, JobRunner}; use crate::Server; -pub struct ShutdownHandle(oneshot::Sender<()>); - -pub struct SocketServer { - server: Server, -} -impl SocketServer { - pub fn run_json( - &self, - listener: impl Stream, - ) -> (ShutdownHandle, impl Future>) { - let (shutdown_send, shutdown_recv) = oneshot::channel(); - (ShutdownHandle(shutdown_send), async move { - //asdf - //adf - Ok(()) - }) +#[derive(Clone)] +pub struct ShutdownHandle(Arc>); +impl ShutdownHandle { + pub fn shutdown(self) { + let _ = self.0.set(()); + } +} + +impl Server { + pub fn run_socket<'a, T: AsyncRead + AsyncWrite + Send>( + &'a self, + listener: impl Stream> + 'a, + error_handler: impl Fn(std::io::Error) + Sync + 'a, + ) -> (ShutdownHandle, impl Future + 'a) { + let shutdown = Arc::new(OnceCell::new()); + (ShutdownHandle(shutdown.clone()), async move { + let mut runner = JobRunner::>::new(); + let jobs = listener.map(|pipe| async { + let pipe = pipe?; + let (r, mut w) = tokio::io::split(pipe); + let stream = self.stream( + tokio_stream::wrappers::LinesStream::new(BufReader::new(r).lines()) + .map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + }) + .try_filter_map(|a| async move { + Ok(if a.is_empty() { + None + } else { + Some(serde_json::from_str::(&a).map_err(parse_error)?) + }) + }), + ); + tokio::pin!(stream); + while let Some(res) = stream.next().await { + if let Err(e) = async { + let mut buf = serde_json::to_vec( + &res.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?, + ) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?; + buf.push(b'\n'); + w.write_all(&buf).await + } + .await + { + error_handler(e) + } + } + Ok(()) + }); + tokio::pin!(jobs); + while let Some(res) = runner.next_result(&mut jobs).await { + if let Err(e) = res { + error_handler(e) + } + } + }) + } + pub fn run_unix<'a>( + &'a self, + path: impl AsRef + 'a, + error_handler: impl Fn(std::io::Error) + Sync + 'a, + ) -> std::io::Result<(ShutdownHandle, impl Future + 'a)> { + let listener = UnixListener::bind(path)?; + Ok(self.run_socket( + tokio_stream::wrappers::UnixListenerStream::new(listener), + error_handler, + )) + } + pub async fn run_tcp<'a>( + &'a self, + addr: impl ToSocketAddrs + 'a, + error_handler: impl Fn(std::io::Error) + Sync + 'a, + ) -> std::io::Result<(ShutdownHandle, impl Future + 'a)> { + let listener = TcpListener::bind(addr).await?; + Ok(self.run_socket( + tokio_stream::wrappers::TcpListenerStream::new(listener), + error_handler, + )) } } diff --git a/rpc-toolkit/src/util.rs b/rpc-toolkit/src/util.rs index b56cb47..00c3540 100644 --- a/rpc-toolkit/src/util.rs +++ b/rpc-toolkit/src/util.rs @@ -1,3 +1,7 @@ +use std::fmt::Display; + +use futures::future::BoxFuture; +use futures::{Future, FutureExt, Stream, StreamExt}; use imbl_value::Value; use serde::de::DeserializeOwned; use serde::Deserialize; @@ -42,13 +46,20 @@ pub fn invalid_request(e: imbl_value::Error) -> RpcError { } } -pub fn parse_error(e: imbl_value::Error) -> RpcError { +pub fn parse_error(e: impl Display) -> RpcError { RpcError { data: Some(e.to_string().into()), ..yajrc::PARSE_ERROR } } +pub fn internal_error(e: impl Display) -> RpcError { + RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + } +} + pub struct Flat(pub A, pub B); impl<'de, A, B> Deserialize<'de> for Flat where @@ -65,3 +76,72 @@ where Ok(Flat(a, b)) } } + +pub fn poll_select_all<'a, T>( + futs: &mut Vec>, + cx: &mut std::task::Context<'_>, +) -> std::task::Poll { + let item = futs + .iter_mut() + .enumerate() + .find_map(|(i, f)| match f.poll_unpin(cx) { + std::task::Poll::Pending => None, + std::task::Poll::Ready(e) => Some((i, e)), + }); + match item { + Some((idx, res)) => { + drop(futs.swap_remove(idx)); + std::task::Poll::Ready(res) + } + None => std::task::Poll::Pending, + } +} + +pub struct JobRunner<'a, T> { + closed: bool, + running: Vec>, +} +impl<'a, T> JobRunner<'a, T> { + pub fn new() -> Self { + JobRunner { + closed: false, + running: Vec::new(), + } + } + pub async fn next_result< + Src: Stream + Unpin, + Fut: Future + Send + 'a, + >( + &mut self, + job_source: &mut Src, + ) -> Option { + loop { + tokio::select! { + job = job_source.next() => { + if let Some(job) = job { + self.running.push(job.boxed()); + } else { + self.closed = true; + } + } + res = self.next() => { + return res; + } + } + } + } +} +impl<'a, T> Stream for JobRunner<'a, T> { + type Item = T; + fn poll_next( + mut self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + match poll_select_all(&mut self.running, cx) { + std::task::Poll::Pending if self.closed && self.running.is_empty() => { + std::task::Poll::Ready(None) + } + a => a.map(Some), + } + } +} diff --git a/rpc-toolkit/tests/compat.rs b/rpc-toolkit/tests/compat.rs index c3d59b1..a96d00c 100644 --- a/rpc-toolkit/tests/compat.rs +++ b/rpc-toolkit/tests/compat.rs @@ -1,214 +1,205 @@ -use std::fmt::Display; -use std::str::FromStr; -use std::sync::Arc; +// use std::fmt::Display; +// use std::str::FromStr; +// use std::sync::Arc; -use futures::FutureExt; -use hyper::Request; -use rpc_toolkit::clap::Arg; -use rpc_toolkit::hyper::http::Error as HttpError; -use rpc_toolkit::hyper::{Body, Response}; -use rpc_toolkit::rpc_server_helpers::{ - DynMiddlewareStage2, DynMiddlewareStage3, DynMiddlewareStage4, -}; -use rpc_toolkit::serde::{Deserialize, Serialize}; -use rpc_toolkit::url::Host; -use rpc_toolkit::yajrc::RpcError; -use rpc_toolkit::{command, rpc_server, run_cli, Context, Metadata}; +// use futures::FutureExt; +// use hyper::Request; +// use rpc_toolkit::clap::Arg; +// use rpc_toolkit::hyper::http::Error as HttpError; +// use rpc_toolkit::hyper::{Body, Response}; +// use rpc_toolkit::rpc_server_helpers::{ +// DynMiddlewareStage2, DynMiddlewareStage3, DynMiddlewareStage4, +// }; +// use rpc_toolkit::serde::{Deserialize, Serialize}; +// use rpc_toolkit::url::Host; +// use rpc_toolkit::yajrc::RpcError; +// use rpc_toolkit::{command, rpc_server, run_cli, Context, Metadata}; -#[derive(Debug, Clone)] -pub struct AppState(Arc); -impl From for () { - fn from(_: AppState) -> Self { - () - } -} +// #[derive(Debug, Clone)] +// pub struct AppState(Arc); +// impl From for () { +// fn from(_: AppState) -> Self { +// () +// } +// } -#[derive(Debug)] -pub struct ConfigSeed { - host: Host, - port: u16, -} +// #[derive(Debug)] +// pub struct ConfigSeed { +// host: Host, +// port: u16, +// } -impl Context for AppState { - fn host(&self) -> Host<&str> { - match &self.0.host { - Host::Domain(s) => Host::Domain(s.as_str()), - Host::Ipv4(i) => Host::Ipv4(*i), - Host::Ipv6(i) => Host::Ipv6(*i), - } - } - fn port(&self) -> u16 { - self.0.port - } -} +// impl Context for AppState { +// type Metadata = (); +// } -fn test_string() -> String { - "test".to_owned() -} +// fn test_string() -> String { +// "test".to_owned() +// } -#[command( - about = "Does the thing", - subcommands("dothething2::", self(dothething_impl(async))) -)] -async fn dothething< - U: Serialize + for<'a> Deserialize<'a> + FromStr + Clone + 'static, - E: Display, ->( - #[context] _ctx: AppState, - #[arg(short = 'a')] arg1: Option, - #[arg(short = 'b', default = "test_string")] val: String, - #[arg(short = 'c', help = "I am the flag `c`!", default)] arg3: bool, - #[arg(stdin)] structured: U, -) -> Result<(Option, String, bool, U), RpcError> { - Ok((arg1, val, arg3, structured)) -} +// #[command( +// about = "Does the thing", +// subcommands("dothething2::", self(dothething_impl(async))) +// )] +// async fn dothething< +// U: Serialize + for<'a> Deserialize<'a> + FromStr + Clone + 'static, +// E: Display, +// >( +// #[context] _ctx: AppState, +// #[arg(short = 'a')] arg1: Option, +// #[arg(short = 'b', default = "test_string")] val: String, +// #[arg(short = 'c', help = "I am the flag `c`!", default)] arg3: bool, +// #[arg(stdin)] structured: U, +// ) -> Result<(Option, String, bool, U), RpcError> { +// Ok((arg1, val, arg3, structured)) +// } -async fn dothething_impl( - ctx: AppState, - parent_data: (Option, String, bool, U), -) -> Result { - Ok(format!( - "{:?}, {:?}, {}, {}, {}", - ctx, - parent_data.0, - parent_data.1, - parent_data.2, - serde_json::to_string_pretty(&parent_data.3)? - )) -} +// async fn dothething_impl( +// ctx: AppState, +// parent_data: (Option, String, bool, U), +// ) -> Result { +// Ok(format!( +// "{:?}, {:?}, {}, {}, {}", +// ctx, +// parent_data.0, +// parent_data.1, +// parent_data.2, +// serde_json::to_string_pretty(&parent_data.3)? +// )) +// } -#[command(about = "Does the thing")] -fn dothething2 Deserialize<'a> + FromStr, E: Display>( - #[parent_data] parent_data: (Option, String, bool, U), - #[arg(stdin)] structured2: U, -) -> Result { - Ok(format!( - "{:?}, {}, {}, {}, {}", - parent_data.0, - parent_data.1, - parent_data.2, - serde_json::to_string_pretty(&parent_data.3)?, - serde_json::to_string_pretty(&structured2)?, - )) -} +// #[command(about = "Does the thing")] +// fn dothething2 Deserialize<'a> + FromStr, E: Display>( +// #[parent_data] parent_data: (Option, String, bool, U), +// #[arg(stdin)] structured2: U, +// ) -> Result { +// Ok(format!( +// "{:?}, {}, {}, {}, {}", +// parent_data.0, +// parent_data.1, +// parent_data.2, +// serde_json::to_string_pretty(&parent_data.3)?, +// serde_json::to_string_pretty(&structured2)?, +// )) +// } -async fn cors( - req: &mut Request, - _: M, -) -> Result>, HttpError> { - if req.method() == hyper::Method::OPTIONS { - Ok(Err(Response::builder() - .header("Access-Control-Allow-Origin", "*") - .body(Body::empty())?)) - } else { - Ok(Ok(Box::new(|_, _| { - async move { - let res: DynMiddlewareStage3 = Box::new(|_, _| { - async move { - let res: DynMiddlewareStage4 = Box::new(|res| { - async move { - res.headers_mut() - .insert("Access-Control-Allow-Origin", "*".parse()?); - Ok::<_, HttpError>(()) - } - .boxed() - }); - Ok::<_, HttpError>(Ok(res)) - } - .boxed() - }); - Ok::<_, HttpError>(Ok(res)) - } - .boxed() - }))) - } -} +// async fn cors( +// req: &mut Request, +// _: M, +// ) -> Result>, HttpError> { +// if req.method() == hyper::Method::OPTIONS { +// Ok(Err(Response::builder() +// .header("Access-Control-Allow-Origin", "*") +// .body(Body::empty())?)) +// } else { +// Ok(Ok(Box::new(|_, _| { +// async move { +// let res: DynMiddlewareStage3 = Box::new(|_, _| { +// async move { +// let res: DynMiddlewareStage4 = Box::new(|res| { +// async move { +// res.headers_mut() +// .insert("Access-Control-Allow-Origin", "*".parse()?); +// Ok::<_, HttpError>(()) +// } +// .boxed() +// }); +// Ok::<_, HttpError>(Ok(res)) +// } +// .boxed() +// }); +// Ok::<_, HttpError>(Ok(res)) +// } +// .boxed() +// }))) +// } +// } -#[tokio::test] -async fn test_rpc() { - use tokio::io::AsyncWriteExt; +// #[tokio::test] +// async fn test_rpc() { +// use tokio::io::AsyncWriteExt; - let seed = Arc::new(ConfigSeed { - host: Host::parse("localhost").unwrap(), - port: 8000, - }); - let server = rpc_server!({ - command: dothething::, - context: AppState(seed), - middleware: [ - cors, - ], - }); - let handle = tokio::spawn(server); - let mut cmd = tokio::process::Command::new("cargo") - .arg("test") - .arg("--package") - .arg("rpc-toolkit") - .arg("--test") - .arg("test") - .arg("--") - .arg("cli_test") - .arg("--exact") - .arg("--nocapture") - .arg("--") - // .arg("-b") - // .arg("test") - .arg("dothething2") - .stdin(std::process::Stdio::piped()) - .stdout(std::process::Stdio::piped()) - .spawn() - .unwrap(); - cmd.stdin - .take() - .unwrap() - .write_all(b"TEST\nHAHA") - .await - .unwrap(); - let out = cmd.wait_with_output().await.unwrap(); - assert!(out.status.success()); - assert!(dbg!(std::str::from_utf8(&out.stdout).unwrap()) - .contains("\nNone, test, false, \"TEST\", \"HAHA\"\n")); - handle.abort(); -} +// let seed = Arc::new(ConfigSeed { +// host: Host::parse("localhost").unwrap(), +// port: 8000, +// }); +// let server = rpc_server!({ +// command: dothething::, +// context: AppState(seed), +// middleware: [ +// cors, +// ], +// }); +// let handle = tokio::spawn(server); +// let mut cmd = tokio::process::Command::new("cargo") +// .arg("test") +// .arg("--package") +// .arg("rpc-toolkit") +// .arg("--test") +// .arg("test") +// .arg("--") +// .arg("cli_test") +// .arg("--exact") +// .arg("--nocapture") +// .arg("--") +// // .arg("-b") +// // .arg("test") +// .arg("dothething2") +// .stdin(std::process::Stdio::piped()) +// .stdout(std::process::Stdio::piped()) +// .spawn() +// .unwrap(); +// cmd.stdin +// .take() +// .unwrap() +// .write_all(b"TEST\nHAHA") +// .await +// .unwrap(); +// let out = cmd.wait_with_output().await.unwrap(); +// assert!(out.status.success()); +// assert!(dbg!(std::str::from_utf8(&out.stdout).unwrap()) +// .contains("\nNone, test, false, \"TEST\", \"HAHA\"\n")); +// handle.abort(); +// } -#[test] -fn cli_test() { - let app = dothething::build_app(); - let mut skip = true; - let args = std::iter::once(std::ffi::OsString::from("cli_test")) - .chain(std::env::args_os().into_iter().skip_while(|a| { - if a == "--" { - skip = false; - return true; - } - skip - })) - .collect::>(); - if skip { - return; - } - let matches = app.get_matches_from(args); - let seed = Arc::new(ConfigSeed { - host: Host::parse("localhost").unwrap(), - port: 8000, - }); - dothething::cli_handler::(AppState(seed), (), None, &matches, "".into(), ()) - .unwrap(); -} +// #[test] +// fn cli_test() { +// let app = dothething::build_app(); +// let mut skip = true; +// let args = std::iter::once(std::ffi::OsString::from("cli_test")) +// .chain(std::env::args_os().into_iter().skip_while(|a| { +// if a == "--" { +// skip = false; +// return true; +// } +// skip +// })) +// .collect::>(); +// if skip { +// return; +// } +// let matches = app.get_matches_from(args); +// let seed = Arc::new(ConfigSeed { +// host: Host::parse("localhost").unwrap(), +// port: 8000, +// }); +// dothething::cli_handler::(AppState(seed), (), None, &matches, "".into(), ()) +// .unwrap(); +// } -#[test] -#[ignore] -fn cli_example() { - run_cli! ({ - command: dothething::, - app: app => app - .arg(Arg::with_name("host").long("host").short('h').takes_value(true)) - .arg(Arg::with_name("port").long("port").short('p').takes_value(true)), - context: matches => AppState(Arc::new(ConfigSeed { - host: Host::parse(matches.value_of("host").unwrap_or("localhost")).unwrap(), - port: matches.value_of("port").unwrap_or("8000").parse().unwrap(), - })) - }) -} +// #[test] +// #[ignore] +// fn cli_example() { +// run_cli! ({ +// command: dothething::, +// app: app => app +// .arg(Arg::with_name("host").long("host").short('h').takes_value(true)) +// .arg(Arg::with_name("port").long("port").short('p').takes_value(true)), +// context: matches => AppState(Arc::new(ConfigSeed { +// host: Host::parse(matches.value_of("host").unwrap_or("localhost")).unwrap(), +// port: matches.value_of("port").unwrap_or("8000").parse().unwrap(), +// })) +// }) +// } -//////////////////////////////////////////////// +// //////////////////////////////////////////////// diff --git a/rpc-toolkit/tests/test.rs b/rpc-toolkit/tests/test.rs index 34922a1..8c8b206 100644 --- a/rpc-toolkit/tests/test.rs +++ b/rpc-toolkit/tests/test.rs @@ -1 +1,109 @@ -pub struct App; +use std::path::PathBuf; + +use clap::Parser; +use futures::Future; +use rpc_toolkit::{ + AsyncCommand, CliContextSocket, Command, Contains, Context, DynCommand, LeafCommand, NoParent, + ParentCommand, ParentInfo, Server, ShutdownHandle, +}; +use serde::{Deserialize, Serialize}; +use tokio::net::UnixStream; +use yajrc::RpcError; + +struct ServerContext; +impl Context for ServerContext { + type Metadata = (); +} + +struct CliContext(PathBuf); +impl Context for CliContext { + type Metadata = (); +} +#[async_trait::async_trait] +impl CliContextSocket for CliContext { + type Stream = UnixStream; + async fn connect(&self) -> std::io::Result { + UnixStream::connect(&self.0).await + } +} +#[async_trait::async_trait] +impl rpc_toolkit::CliContext for CliContext { + async fn call_remote( + &self, + method: &str, + params: imbl_value::Value, + ) -> Result { + ::call_remote(self, method, params).await + } +} + +async fn run_server() { + Server::new( + vec![ + DynCommand::from_parent::(Contains::none()), + DynCommand::from_async::(Contains::none()), + // DynCommand::from_async::(Contains::none()), + // DynCommand::from_sync::(Contains::none()), + // DynCommand::from_sync::(Contains::none()), + ], + || async { Ok(ServerContext) }, + ) + .run_unix("./test.sock", |e| eprintln!("{e}")) + .unwrap() + .1 + .await +} + +#[derive(Debug, Deserialize, Serialize, Parser)] +struct Group { + #[arg(short, long)] + verbose: bool, +} +impl Command for Group { + const NAME: &'static str = "group"; + type Parent = NoParent; +} +impl ParentCommand for Group +where + Ctx: Context, + // SubThing: AsyncCommand, + Thing1: AsyncCommand, +{ + fn subcommands(chain: rpc_toolkit::ParentChain) -> Vec> { + vec![ + // DynCommand::from_async::(chain.child()), + DynCommand::from_async::(Contains::none()), + ] + } +} + +#[derive(Debug, Deserialize, Serialize, Parser)] +struct Thing1 { + thing: String, +} +impl Command for Thing1 { + const NAME: &'static str = "thing1"; + type Parent = NoParent; +} +impl LeafCommand for Thing1 { + type Ok = String; + type Err = RpcError; + fn display(self, _: ServerContext, _: rpc_toolkit::ParentInfo, res: Self::Ok) { + println!("{}", res); + } +} +#[async_trait::async_trait] +impl AsyncCommand for Thing1 { + async fn implementation( + self, + _: ServerContext, + _: ParentInfo, + ) -> Result { + Ok(format!("Thing1 is {}", self.thing)) + } +} + +#[tokio::test] +async fn test() { + let server = tokio::spawn(run_server()); +} From 68c8881c0b016adbead732075d401fce71a9b81f Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 13 Dec 2023 23:33:51 -0700 Subject: [PATCH 06/79] wip --- Cargo.lock | 7 + rpc-toolkit/Cargo.toml | 1 + rpc-toolkit/src/cli.rs | 112 ++-- rpc-toolkit/src/command_helpers.rs | 130 ----- rpc-toolkit/src/handler.rs | 769 ++++++++++++++++++++++++++ rpc-toolkit/src/lib.rs | 4 +- rpc-toolkit/src/rpc_server_helpers.rs | 186 ------- rpc-toolkit/src/util.rs | 56 +- 8 files changed, 885 insertions(+), 380 deletions(-) delete mode 100644 rpc-toolkit/src/command_helpers.rs create mode 100644 rpc-toolkit/src/handler.rs delete mode 100644 rpc-toolkit/src/rpc_server_helpers.rs diff --git a/Cargo.lock b/Cargo.lock index cafdfe2..a2d76e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -690,6 +690,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "lazy_format" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e479e99b287d578ed5f6cd4c92cdf48db219088adb9c5b14f7c155b71dfba792" + [[package]] name = "lazy_static" version = "1.4.0" @@ -992,6 +998,7 @@ dependencies = [ "http-body-util", "hyper 1.0.1", "imbl-value", + "lazy_format", "lazy_static", "openssl", "reqwest", diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml index cc97b2a..6b225b2 100644 --- a/rpc-toolkit/Cargo.toml +++ b/rpc-toolkit/Cargo.toml @@ -24,6 +24,7 @@ http = "1" http-body-util = "0.1" hyper = { version = "1", features = ["server", "http1", "http2", "client"] } imbl-value = "0.1" +lazy_format = "2" lazy_static = "1.4" openssl = { version = "0.10", features = ["vendored"] } reqwest = { version = "0.11" } diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index a696105..4502d5b 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -11,58 +11,56 @@ use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader use url::Url; use yajrc::{Id, RpcError}; -use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo}; +use crate::command::ParentCommand; +// use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo}; use crate::util::{combine, internal_error, invalid_params, parse_error}; -use crate::{CliBindings, SyncCommand}; +// use crate::{CliBindings, SyncCommand}; type GenericRpcMethod<'a> = yajrc::GenericRpcMethod<&'a str, Value, Value>; type RpcRequest<'a> = yajrc::RpcRequest>; type RpcResponse<'a> = yajrc::RpcResponse>; -impl DynCommand { - fn cli_app(&self) -> Option { - if let Some(cli) = &self.cli { - Some( - cli.cmd - .clone() - .name(self.name) - .subcommands(self.subcommands.iter().filter_map(|c| c.cli_app())), - ) - } else { - None - } - } - fn cmd_from_cli_matches( - &self, - matches: &ArgMatches, - parent: ParentInfo, - ) -> Result<(Vec<&'static str>, Value, &DynCommand), RpcError> { - let params = combine( - parent.params, - (self - .cli - .as_ref() - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .parser)(matches)?, - )?; - if let Some((cmd, matches)) = matches.subcommand() { - let mut method = parent.method; - method.push(self.name); - self.subcommands - .iter() - .find(|c| c.name == cmd) - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .cmd_from_cli_matches(matches, ParentInfo { method, params }) - } else { - Ok((parent.method, params, self)) - } - } -} +// impl DynCommand { +// fn cli_app(&self) -> Option { +// if let Some(cli) = &self.cli { +// Some( +// cli.cmd +// .clone() +// .name(self.name) +// .subcommands(self.subcommands.iter().filter_map(|c| c.cli_app())), +// ) +// } else { +// None +// } +// } +// fn cmd_from_cli_matches( +// &self, +// matches: &ArgMatches, +// parent: ParentInfo, +// ) -> Result<(Vec<&'static str>, Value, &DynCommand), RpcError> { +// let params = combine( +// parent.params, +// (self +// .cli +// .as_ref() +// .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? +// .parser)(matches)?, +// )?; +// if let Some((cmd, matches)) = matches.subcommand() { +// let mut method = parent.method; +// method.push(self.name); +// self.subcommands +// .iter() +// .find(|c| c.name == cmd) +// .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? +// .cmd_from_cli_matches(matches, ParentInfo { method, params }) +// } else { +// Ok((parent.method, params, self)) +// } +// } +// } -struct CliApp { - cli: CliBindings, - commands: Vec>, -} +struct CliApp(ParentCommand); impl CliApp { pub fn new( commands: Vec>, @@ -306,7 +304,8 @@ where combine( imbl_value::to_value(&self).map_err(invalid_params)?, imbl_value::to_value(&parent.params).map_err(invalid_params)?, - )?, + ) + .map_err(invalid_params)?, ) .await?, ) @@ -330,15 +329,18 @@ where ) -> Result { let mut method = parent.method; method.push(Self::NAME); - Ok( - imbl_value::from_value(ctx.runtime().block_on(ctx.call_remote( - &method.join("."), - combine( - imbl_value::to_value(&self).map_err(invalid_params)?, - imbl_value::to_value(&parent.params).map_err(invalid_params)?, - )?, - ))?) - .map_err(parse_error)?, + Ok(imbl_value::from_value( + ctx.runtime().block_on( + ctx.call_remote( + &method.join("."), + combine( + imbl_value::to_value(&self).map_err(invalid_params)?, + imbl_value::to_value(&parent.params).map_err(invalid_params)?, + ) + .map_err(invalid_params)?, + ), + )?, ) + .map_err(parse_error)?) } } diff --git a/rpc-toolkit/src/command_helpers.rs b/rpc-toolkit/src/command_helpers.rs deleted file mode 100644 index c077ad5..0000000 --- a/rpc-toolkit/src/command_helpers.rs +++ /dev/null @@ -1,130 +0,0 @@ -use std::fmt::Display; -use std::io::Stdin; -use std::marker::PhantomData; -use std::str::FromStr; - -use clap::ArgMatches; -use hyper::Method; -use serde::{Deserialize, Serialize}; -use thiserror::Error; -use yajrc::{GenericRpcMethod, Id, RpcError, RpcRequest, RpcResponse}; - -use crate::Context; - -pub mod prelude { - pub use std::borrow::Cow; - pub use std::marker::PhantomData; - - pub use clap::{App, AppSettings, Arg, ArgMatches}; - pub use hyper::http::request::Parts as RequestParts; - pub use hyper::http::response::Parts as ResponseParts; - pub use serde::{Deserialize, Serialize}; - pub use serde_json::{from_value, to_value, Value}; - pub use tokio::runtime::Runtime; - pub use tokio::task::spawn_blocking; - pub use yajrc::{self, RpcError}; - - pub use super::{ - call_remote, default_arg_parser, default_display, default_stdin_parser, make_phantom, - match_types, - }; - pub use crate::Context; -} - -#[derive(Debug, Error)] -pub enum RequestError { - #[error("JSON Error: {0}")] - JSON(#[from] serde_json::Error), - #[cfg(feature = "cbor")] - #[error("CBOR Error: {0}")] - CBOR(#[from] serde_cbor::Error), - #[error("HTTP Error: {0}")] - HTTP(#[from] reqwest::Error), - #[error("Missing Content-Type")] - MissingContentType, -} - -pub fn make_phantom(_actual: T) -> PhantomData { - PhantomData -} - -pub fn match_types(_: &T, _: &T) {} - -pub async fn call_remote Deserialize<'de>>( - ctx: Ctx, - method: &str, - params: Params, - _return_ty: PhantomData, -) -> Result>, RequestError> { - let rpc_req: RpcRequest> = RpcRequest { - id: Some(Id::Number(0.into())), - method: GenericRpcMethod::new(method), - params, - }; - let mut req = ctx.client().request(Method::POST, ctx.url()); - let body; - #[cfg(feature = "cbor")] - { - req = req.header("content-type", "application/cbor"); - req = req.header("accept", "application/cbor, application/json"); - body = serde_cbor::to_vec(&rpc_req)?; - } - #[cfg(not(feature = "cbor"))] - { - req = req.header("content-type", "application/json"); - req = req.header("accept", "application/json"); - body = serde_json::to_vec(&req)?; - } - let res = req - .header("content-length", body.len()) - .body(body) - .send() - .await?; - Ok( - match res - .headers() - .get("content-type") - .and_then(|v| v.to_str().ok()) - { - Some("application/json") => serde_json::from_slice(&*res.bytes().await?)?, - #[cfg(feature = "cbor")] - Some("application/cbor") => serde_cbor::from_slice(&*res.bytes().await?)?, - _ => return Err(RequestError::MissingContentType), - }, - ) -} - -pub fn default_arg_parser, E: Display>( - arg: &str, - _: &ArgMatches, -) -> Result { - arg.parse().map_err(|e| RpcError { - data: Some(format!("{}", e).into()), - ..yajrc::INVALID_PARAMS_ERROR - }) -} - -pub fn default_stdin_parser, E: Display>( - stdin: &mut Stdin, - _: &ArgMatches, -) -> Result { - let mut s = String::new(); - stdin.read_line(&mut s).map_err(|e| RpcError { - data: Some(format!("{}", e).into()), - ..yajrc::INVALID_PARAMS_ERROR - })?; - if let Some(s) = s.strip_suffix("\n") { - s - } else { - &s - } - .parse() - .map_err(|e| RpcError { - data: Some(format!("{}", e).into()), - ..yajrc::INVALID_PARAMS_ERROR - }) -} - -pub fn default_display(t: T, _: &ArgMatches) { - println!("{}", t) -} diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs new file mode 100644 index 0000000..91756f3 --- /dev/null +++ b/rpc-toolkit/src/handler.rs @@ -0,0 +1,769 @@ +use std::collections::{BTreeMap, VecDeque}; +use std::marker::PhantomData; + +use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser}; +use imbl_value::Value; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use yajrc::RpcError; + +use crate::util::{combine, internal_error, invalid_params, Flat}; + +struct HandleAnyArgs { + context: Context, + parent_method: Vec<&'static str>, + method: VecDeque<&'static str>, + params: Value, +} +impl HandleAnyArgs { + fn downcast(self) -> Result, imbl_value::Error> + where + H: Handler, + H::Params: DeserializeOwned, + H::InheritedParams: DeserializeOwned, + { + let Self { + context, + parent_method, + method, + params, + } = self; + Ok(HandleArgs { + context, + parent_method, + method, + params: imbl_value::from_value(params.clone())?, + inherited_params: imbl_value::from_value(params.clone())?, + }) + } +} + +#[async_trait::async_trait] +trait HandleAny { + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; + // async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result; +} + +trait CliBindingsAny { + fn cli_command(&self) -> Command; + fn cli_parse( + &self, + matches: &ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; + fn cli_display( + &self, + handle_args: HandleAnyArgs, + result: Value, + ) -> Result<(), RpcError>; +} + +pub trait CliBindings: Handler { + fn cli_command(&self) -> Command; + fn cli_parse( + &self, + matches: &ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; + fn cli_display( + &self, + handle_args: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err>; +} + +pub trait PrintCliResult: Handler { + fn print( + &self, + handle_args: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err>; +} + +// impl PrintCliResult for H +// where +// Context: crate::Context, +// H: Handler, +// H::Ok: Display, +// { +// fn print( +// &self, +// handle_args: HandleArgs, +// result: Self::Ok, +// ) -> Result<(), Self::Err> { +// Ok(println!("{result}")) +// } +// } + +struct WithCliBindings { + _ctx: PhantomData, + handler: H, +} + +impl Handler for WithCliBindings +where + Context: crate::Context, + H: Handler, +{ + type Params = H::Params; + type InheritedParams = H::InheritedParams; + type Ok = H::Ok; + type Err = H::Err; + fn handle_sync( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + }: HandleArgs, + ) -> Result { + self.handler.handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + }) + } +} + +impl CliBindings for WithCliBindings +where + Context: crate::Context, + H: Handler, + H::Params: FromArgMatches + CommandFactory + Serialize, + H: PrintCliResult, +{ + fn cli_command(&self) -> Command { + H::Params::command() + } + fn cli_parse( + &self, + matches: &ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + H::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.handler.print( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + }, + result, + ) + } +} + +trait HandleAnyWithCli: HandleAny + CliBindingsAny {} +impl + CliBindingsAny> + HandleAnyWithCli for T +{ +} + +enum DynHandler { + WithoutCli(Box>), + WithCli(Box>), +} +impl HandleAny for DynHandler { + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + match self { + DynHandler::WithoutCli(h) => h.handle_sync(handle_args), + DynHandler::WithCli(h) => h.handle_sync(handle_args), + } + } +} + +pub struct HandleArgs + ?Sized> { + context: Context, + parent_method: Vec<&'static str>, + method: VecDeque<&'static str>, + params: H::Params, + inherited_params: H::InheritedParams, +} +impl HandleArgs +where + Context: crate::Context, + H: Handler, + H::Params: Serialize, + H::InheritedParams: Serialize, +{ + fn upcast( + Self { + context, + parent_method, + method, + params, + inherited_params, + }: Self, + ) -> Result, imbl_value::Error> { + Ok(HandleAnyArgs { + context, + parent_method, + method, + params: combine( + imbl_value::to_value(¶ms)?, + imbl_value::to_value(&inherited_params)?, + )?, + }) + } +} + +pub trait Handler { + type Params; + type InheritedParams; + type Ok; + type Err; + fn handle_sync(&self, handle_args: HandleArgs) -> Result; +} + +struct AnyHandler { + _ctx: PhantomData, + handler: H, +} + +impl> HandleAny for AnyHandler +where + H::Params: DeserializeOwned, + H::InheritedParams: DeserializeOwned, + H::Ok: Serialize, + RpcError: From, +{ + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + imbl_value::to_value( + &self + .handler + .handle_sync(handle_args.downcast().map_err(invalid_params)?)?, + ) + .map_err(internal_error) + } +} + +impl> CliBindingsAny + for AnyHandler +where + H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, + H::InheritedParams: DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, +{ + fn cli_command(&self) -> Command { + self.handler.cli_command() + } + fn cli_parse( + &self, + matches: &ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + self.handler.cli_parse(matches) + } + fn cli_display( + &self, + handle_args: HandleAnyArgs, + result: Value, + ) -> Result<(), RpcError> { + self.handler + .cli_display( + handle_args.downcast().map_err(invalid_params)?, + imbl_value::from_value(result).map_err(internal_error)?, + ) + .map_err(RpcError::from) + } +} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] +pub struct NoParams {} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] +enum Never {} + +struct EmptyHandler(PhantomData<(Params, InheritedParams)>); +impl Handler + for EmptyHandler +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = Never; + type Err = RpcError; + fn handle_sync(&self, _: HandleArgs) -> Result { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } +} + +pub struct ParentHandler> { + handler: H, + subcommands: BTreeMap<&'static str, DynHandler>, +} +impl + ParentHandler> +where + EmptyHandler: CliBindings, +{ + pub fn new() -> Self { + Self { + handler: WithCliBindings { + _ctx: PhantomData, + handler: EmptyHandler(PhantomData).into(), + }, + subcommands: BTreeMap::new(), + } + } +} + +impl + ParentHandler> +{ + pub fn new_no_cli() -> Self { + Self { + handler: EmptyHandler(PhantomData).into(), + subcommands: BTreeMap::new(), + } + } +} + +impl> From for ParentHandler { + fn from(value: H) -> Self { + Self { + handler: value.into(), + subcommands: BTreeMap::new(), + } + } +} + +struct InheritanceHandler< + Context: crate::Context, + H: Handler, + SubH: Handler, + F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams, +> { + _phantom: PhantomData<(Context, H)>, + handler: SubH, + inherit: F, +} +impl< + Context: crate::Context, + H: Handler, + SubH: Handler, + F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams, + > Handler for InheritanceHandler +{ + type Params = SubH::Params; + type InheritedParams = Flat; + type Ok = SubH::Ok; + type Err = SubH::Err; + fn handle_sync( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + }: HandleArgs, + ) -> Result { + self.handler.handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + }) + } +} + +impl PrintCliResult for InheritanceHandler +where + Context: crate::Context, + H: Handler, + SubH: Handler + PrintCliResult, + F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams, +{ + fn print( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.handler.print( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + }, + result, + ) + } +} + +impl> ParentHandler { + pub fn subcommand(mut self, method: &'static str, handler: SubH) -> Self + where + SubH: Handler + PrintCliResult + 'static, + SubH::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, + SubH::Ok: Serialize + DeserializeOwned, + RpcError: From, + { + self.subcommands.insert( + method, + DynHandler::WithCli(Box::new(AnyHandler { + _ctx: PhantomData, + handler: WithCliBindings { + _ctx: PhantomData, + handler, + }, + })), + ); + self + } + pub fn subcommand_with_inherited( + mut self, + method: &'static str, + handler: SubH, + inherit: F, + ) -> Self + where + SubH: Handler + PrintCliResult + 'static, + SubH::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, + SubH::Ok: Serialize + DeserializeOwned, + H: 'static, + H::Params: DeserializeOwned, + H::InheritedParams: DeserializeOwned, + RpcError: From, + F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams + 'static, + { + self.subcommands.insert( + method, + DynHandler::WithCli(Box::new(AnyHandler { + _ctx: PhantomData, + handler: WithCliBindings { + _ctx: PhantomData, + handler: InheritanceHandler:: { + _phantom: PhantomData, + handler, + inherit, + }, + }, + })), + ); + self + } + pub fn subcommand_no_cli(mut self, method: &'static str, handler: SubH) -> Self + where + SubH: Handler + 'static, + SubH::Params: DeserializeOwned, + SubH::Ok: Serialize, + RpcError: From, + { + self.subcommands.insert( + method, + DynHandler::WithoutCli(Box::new(AnyHandler { + _ctx: PhantomData, + handler, + })), + ); + self + } + pub fn subcommand_with_inherited_no_cli( + mut self, + method: &'static str, + handler: SubH, + inherit: F, + ) -> Self + where + SubH: Handler + 'static, + SubH::Params: DeserializeOwned, + SubH::Ok: Serialize, + H: 'static, + H::Params: DeserializeOwned, + H::InheritedParams: DeserializeOwned, + RpcError: From, + F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams + 'static, + { + self.subcommands.insert( + method, + DynHandler::WithoutCli(Box::new(AnyHandler { + _ctx: PhantomData, + handler: InheritanceHandler:: { + _phantom: PhantomData, + handler, + inherit, + }, + })), + ); + self + } +} + +impl Handler for ParentHandler +where + Context: crate::Context, + H: Handler, + H::Params: Serialize, + H::InheritedParams: Serialize, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, +{ + type Params = H::Params; + type InheritedParams = H::InheritedParams; + type Ok = Value; + type Err = RpcError; + fn handle_sync( + &self, + HandleArgs { + context, + mut parent_method, + mut method, + params, + inherited_params, + }: HandleArgs, + ) -> Result { + if let Some(cmd) = method.pop_front() { + parent_method.push(cmd); + if let Some(sub_handler) = self.subcommands.get(cmd) { + sub_handler.handle_sync(HandleAnyArgs { + context, + parent_method, + method, + params: imbl_value::to_value(&Flat(params, inherited_params)) + .map_err(invalid_params)?, + }) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } else { + self.handler + .handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + }) + .map_err(RpcError::from) + .and_then(|r| imbl_value::to_value(&r).map_err(internal_error)) + } + } +} + +impl CliBindings for ParentHandler +where + Context: crate::Context, + H: CliBindings, + H::Params: FromArgMatches + CommandFactory + Serialize, + H::InheritedParams: Serialize, + H::Ok: PrintCliResult + Serialize + DeserializeOwned, + RpcError: From, +{ + fn cli_command(&self) -> Command { + H::Params::command().subcommands(self.subcommands.iter().filter_map(|(method, handler)| { + match handler { + DynHandler::WithCli(h) => Some(h.cli_command().name(method)), + DynHandler::WithoutCli(_) => None, + } + })) + } + fn cli_parse( + &self, + matches: &ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + let (_, root_params) = self.handler.cli_parse(matches)?; + if let Some((sub, matches)) = matches.subcommand() { + if let Some((sub, DynHandler::WithCli(h))) = self.subcommands.get_key_value(sub) { + let (mut method, params) = h.cli_parse(matches)?; + method.push_front(*sub); + return Ok(( + method, + combine(root_params, params).map_err(|e| { + clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e) + })?, + )); + } + } + Ok((VecDeque::new(), root_params)) + } + fn cli_display( + &self, + HandleArgs { + context, + mut parent_method, + mut method, + params, + inherited_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + if let Some(cmd) = method.pop_front() { + parent_method.push(cmd); + if let Some(DynHandler::WithCli(sub_handler)) = self.subcommands.get(cmd) { + sub_handler.cli_display( + HandleAnyArgs { + context, + parent_method, + method, + params: imbl_value::to_value(&Flat(params, inherited_params)) + .map_err(invalid_params)?, + }, + result, + ) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } else { + self.handler + .cli_display( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + }, + imbl_value::from_value(result).map_err(internal_error)?, + ) + .map_err(RpcError::from) + } + } +} + +pub struct FromFn { + _phantom: PhantomData<(T, E, Args)>, + function: F, +} + +pub fn from_fn(function: F) -> FromFn { + FromFn { + function, + _phantom: PhantomData, + } +} + +impl Handler for FromFn +where + Context: crate::Context, + F: Fn() -> Result, +{ + type Params = NoParams; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; + fn handle_sync(&self, _: HandleArgs) -> Result { + (self.function)() + } +} + +impl Handler for FromFn +where + Context: crate::Context, + F: Fn(Context) -> Result, +{ + type Params = NoParams; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; + fn handle_sync(&self, handle_args: HandleArgs) -> Result { + (self.function)(handle_args.context) + } +} +impl Handler for FromFn +where + Context: crate::Context, + F: Fn(Context, Params) -> Result, + Params: DeserializeOwned, +{ + type Params = Params; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; + fn handle_sync(&self, handle_args: HandleArgs) -> Result { + let HandleArgs { + context, params, .. + } = handle_args; + (self.function)(context, params) + } +} +impl Handler + for FromFn +where + Context: crate::Context, + F: Fn(Context, Params, InheritedParams) -> Result, + Params: DeserializeOwned, + InheritedParams: DeserializeOwned, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = T; + type Err = E; + fn handle_sync(&self, handle_args: HandleArgs) -> Result { + let HandleArgs { + context, + params, + inherited_params, + .. + } = handle_args; + (self.function)(context, params, inherited_params) + } +} + +#[derive(Parser)] +#[command(about = "this is db stuff")] +struct DbParams {} + +// Server::new( +// ParentCommand::new() +// .subcommand("foo", from_fn(foo)) +// .subcommand("db", +// ParentCommand::new::() +// .subcommand("dump", from_fn(dump)) +// ) +// ) + +// Server::new() +// .handle( +// "db", +// with_description("Description maybe?") +// .handle("dump", from_fn(dump_route)) +// ) +// .handle( +// "server", +// no_description() +// .handle("version", from_fn(version)) +// ) + +// #[derive(clap::Parser)] +// struct DumpParams { +// test: Option +// } + +// fn dump_route(context: Context, param: Param) -> Result { +// Ok(json!({ +// "db": {} +// })) +// } + +// fn version() -> &'static str { +// "1.0.0" +// } diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index 7c5807e..e69e045 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -1,6 +1,7 @@ pub use cli::*; -pub use command::*; +// pub use command::*; pub use context::Context; +pub use handler::*; /// `#[command(...)]` /// - `#[command(cli_only)]` -> executed by CLI instead of RPC server (leaf commands only) /// - `#[command(rpc_only)]` -> no CLI bindings (leaf commands only) @@ -28,6 +29,7 @@ pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc}; mod cli; mod command; +mod handler; // pub mod command_helpers; mod context; // mod metadata; diff --git a/rpc-toolkit/src/rpc_server_helpers.rs b/rpc-toolkit/src/rpc_server_helpers.rs deleted file mode 100644 index 02ecef4..0000000 --- a/rpc-toolkit/src/rpc_server_helpers.rs +++ /dev/null @@ -1,186 +0,0 @@ -use std::future::Future; -use std::sync::Arc; - -use futures::future::BoxFuture; -use futures::FutureExt; -use hyper::body::Buf; -use hyper::header::HeaderValue; -use hyper::http::request::Parts as RequestParts; -use hyper::http::response::Parts as ResponseParts; -use hyper::http::Error as HttpError; -use hyper::server::conn::AddrIncoming; -use hyper::server::{Builder, Server}; -use hyper::{Body, HeaderMap, Request, Response, StatusCode}; -use lazy_static::lazy_static; -use serde_json::{Map, Value}; -use url::Host; -use yajrc::{AnyRpcMethod, GenericRpcMethod, Id, RpcError, RpcRequest, RpcResponse}; - -use crate::{Context, Metadata}; - -lazy_static! { - #[cfg(feature = "cbor")] - static ref CBOR_INTERNAL_ERROR: Vec = - serde_cbor::to_vec(&RpcResponse::>::from(yajrc::INTERNAL_ERROR)).unwrap(); - static ref JSON_INTERNAL_ERROR: Vec = - serde_json::to_vec(&RpcResponse::>::from(yajrc::INTERNAL_ERROR)).unwrap(); -} - -pub fn make_builder(ctx: &Ctx) -> Builder { - let addr = match ctx.host() { - Host::Ipv4(ip) => (ip, ctx.port()).into(), - Host::Ipv6(ip) => (ip, ctx.port()).into(), - Host::Domain(localhost) if localhost == "localhost" => ([127, 0, 0, 1], ctx.port()).into(), - _ => ([0, 0, 0, 0], ctx.port()).into(), - }; - Server::bind(&addr) -} - -pub async fn make_request( - req_parts: &RequestParts, - req_body: Body, -) -> Result>>, RpcError> { - let body = hyper::body::aggregate(req_body).await?.reader(); - let rpc_req: RpcRequest>>; - #[cfg(feature = "cbor")] - if req_parts - .headers - .get("content-type") - .and_then(|h| h.to_str().ok()) - == Some("application/cbor") - { - rpc_req = serde_cbor::from_reader(body)?; - } else { - rpc_req = serde_json::from_reader(body)?; - } - #[cfg(not(feature = "cbor"))] - { - rpc_req = serde_json::from_reader(body)?; - } - - Ok(rpc_req) -} - -pub fn to_response StatusCode>( - req_headers: &HeaderMap, - mut res_parts: ResponseParts, - res: Result<(Option, Result), RpcError>, - status_code_fn: F, -) -> Result, HttpError> { - let rpc_res: RpcResponse = match res { - Ok((id, result)) => RpcResponse { id, result }, - Err(e) => e.into(), - }; - let body; - #[cfg(feature = "cbor")] - if req_headers - .get("accept") - .and_then(|h| h.to_str().ok()) - .iter() - .flat_map(|s| s.split(",")) - .map(|s| s.trim()) - .any(|s| s == "application/cbor") - // prefer cbor if accepted - { - res_parts - .headers - .insert("content-type", HeaderValue::from_static("application/cbor")); - body = serde_cbor::to_vec(&rpc_res).unwrap_or_else(|_| CBOR_INTERNAL_ERROR.clone()); - } else { - res_parts - .headers - .insert("content-type", HeaderValue::from_static("application/json")); - body = serde_json::to_vec(&rpc_res).unwrap_or_else(|_| JSON_INTERNAL_ERROR.clone()); - } - #[cfg(not(feature = "cbor"))] - { - res_parts - .headers - .insert("content-type", HeaderValue::from_static("application/json")); - body = serde_json::to_vec(&rpc_res).unwrap_or_else(|_| JSON_INTERNAL_ERROR.clone()); - } - res_parts.headers.insert( - "content-length", - HeaderValue::from_str(&format!("{}", body.len()))?, - ); - res_parts.status = match &rpc_res.result { - Ok(_) => StatusCode::OK, - Err(e) => status_code_fn(e.code), - }; - Ok(Response::from_parts(res_parts, body.into())) -} - -pub type RpcHandler = Arc< - dyn Fn(Request) -> BoxFuture<'static, Result, HttpError>> + Send + Sync, ->; - -// &mut Request -> Result -> Future -> Future>, Response>, HttpError>>>, Response>, HttpError> -pub type DynMiddleware = Box< - dyn for<'a> Fn( - &'a mut Request, - Metadata, - ) - -> BoxFuture<'a, Result>, HttpError>> - + Send - + Sync, ->; -pub fn noop() -> DynMiddleware { - Box::new(|_, _| async { Ok(Ok(noop2())) }.boxed()) -} -pub type DynMiddlewareStage2 = Box< - dyn for<'a> FnOnce( - &'a mut RequestParts, - &'a mut RpcRequest>>, - ) -> BoxFuture< - 'a, - Result>, HttpError>, - > + Send - + Sync, ->; -pub fn noop2() -> DynMiddlewareStage2 { - Box::new(|_, _| async { Ok(Ok(noop3())) }.boxed()) -} -pub type DynMiddlewareStage3 = Box< - dyn for<'a> FnOnce( - &'a mut ResponseParts, - &'a mut Result, - ) -> BoxFuture< - 'a, - Result>, HttpError>, - > + Send - + Sync, ->; -pub fn noop3() -> DynMiddlewareStage3 { - Box::new(|_, _| async { Ok(Ok(noop4())) }.boxed()) -} -pub type DynMiddlewareStage4 = Box< - dyn for<'a> FnOnce(&'a mut Response) -> BoxFuture<'a, Result<(), HttpError>> - + Send - + Sync, ->; -pub fn noop4() -> DynMiddlewareStage4 { - Box::new(|_| async { Ok(()) }.boxed()) -} - -pub fn constrain_middleware< - 'a, - 'b, - 'c, - 'd, - M: Metadata, - ReqFn: Fn(&'a mut Request, M) -> ReqFut + Clone, - ReqFut: Future>, HttpError>> + 'a, - RpcReqFn: FnOnce( - &'b mut RequestParts, - &'b mut RpcRequest>>, - ) -> RpcReqFut, - RpcReqFut: Future>, HttpError>> + 'b, - RpcResFn: FnOnce(&'c mut ResponseParts, &'c mut Result) -> RpcResFut, - RpcResFut: Future>, HttpError>> + 'c, - ResFn: FnOnce(&'d mut Response) -> ResFut, - ResFut: Future> + 'd, ->( - f: ReqFn, -) -> ReqFn { - f -} diff --git a/rpc-toolkit/src/util.rs b/rpc-toolkit/src/util.rs index 00c3540..133b8cf 100644 --- a/rpc-toolkit/src/util.rs +++ b/rpc-toolkit/src/util.rs @@ -4,7 +4,8 @@ use futures::future::BoxFuture; use futures::{Future, FutureExt, Stream, StreamExt}; use imbl_value::Value; use serde::de::DeserializeOwned; -use serde::Deserialize; +use serde::ser::Error; +use serde::{Deserialize, Serialize}; use yajrc::RpcError; pub fn extract(value: &Value) -> Result { @@ -14,18 +15,20 @@ pub fn extract(value: &Value) -> Result { }) } -pub fn combine(v1: Value, v2: Value) -> Result { +pub fn combine(v1: Value, v2: Value) -> Result { let (Value::Object(mut v1), Value::Object(v2)) = (v1, v2) else { - return Err(RpcError { - data: Some("params must be object".into()), - ..yajrc::INVALID_PARAMS_ERROR + return Err(imbl_value::Error { + kind: imbl_value::ErrorKind::Serialization, + source: serde_json::Error::custom("params must be object"), }); }; for (key, value) in v2 { if v1.insert(key.clone(), value).is_some() { - return Err(RpcError { - data: Some(format!("duplicate key: {key}").into()), - ..yajrc::INVALID_PARAMS_ERROR + return Err(imbl_value::Error { + kind: imbl_value::ErrorKind::Serialization, + source: serde_json::Error::custom(lazy_format::lazy_format!( + "duplicate key: {key}" + )), }); } } @@ -76,6 +79,29 @@ where Ok(Flat(a, b)) } } +impl Serialize for Flat +where + A: Serialize, + B: Serialize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + #[derive(serde::Serialize)] + struct FlatStruct<'a, A, B> { + #[serde(flatten)] + a: &'a A, + #[serde(flatten)] + b: &'a B, + } + FlatStruct { + a: &self.0, + b: &self.1, + } + .serialize(serializer) + } +} pub fn poll_select_all<'a, T>( futs: &mut Vec>, @@ -145,3 +171,17 @@ impl<'a, T> Stream for JobRunner<'a, T> { } } } + +// #[derive(Debug)] +// pub enum Infallible {} +// impl From for T { +// fn from(value: Infallible) -> Self { +// match value {} +// } +// } +// impl std::fmt::Display for Infallible { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// match *self {} +// } +// } +// impl std::error::Error for Infallible {} From e86cd8275e09c3215038bca13e4586445eabceb6 Mon Sep 17 00:00:00 2001 From: J H Date: Thu, 14 Dec 2023 12:36:35 -0700 Subject: [PATCH 07/79] wip --- .../src/command/parse.rs | 2 +- rpc-toolkit/src/cli.rs | 51 ++++- rpc-toolkit/src/context.rs | 4 +- rpc-toolkit/src/handler.rs | 22 +- rpc-toolkit/src/lib.rs | 10 +- rpc-toolkit/tests/handler.rs | 72 +++++++ rpc-toolkit/tests/test.rs | 204 +++++++++--------- 7 files changed, 239 insertions(+), 126 deletions(-) create mode 100644 rpc-toolkit/tests/handler.rs diff --git a/rpc-toolkit-macro-internals/src/command/parse.rs b/rpc-toolkit-macro-internals/src/command/parse.rs index d2bf990..fe45a03 100644 --- a/rpc-toolkit-macro-internals/src/command/parse.rs +++ b/rpc-toolkit-macro-internals/src/command/parse.rs @@ -966,7 +966,7 @@ pub fn parse_param_attrs(item: &mut ItemFn) -> Result> { attr.span(), "`context` and `response` are mutually exclusive", )); - } else if matches!(ty, ParamType::Context(_)) { + } else if matches!(ty, ParamType::ParentData(_)) { return Err(Error::new( attr.span(), "`parent_data` and `response` are mutually exclusive", diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 4502d5b..232002f 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -1,17 +1,18 @@ -use std::ffi::OsString; +use std::{ffi::OsString, marker::PhantomData}; use clap::{ArgMatches, CommandFactory, FromArgMatches}; -use futures::future::BoxFuture; +use futures::{future::BoxFuture, never::Never}; use futures::{Future, FutureExt}; use imbl_value::Value; use reqwest::{Client, Method}; use serde::de::DeserializeOwned; use serde::Serialize; +use std::marker::PhantomData; use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}; use url::Url; use yajrc::{Id, RpcError}; -use crate::command::ParentCommand; +use crate::{command::ParentCommand, CliBindings, EmptyHandler, HandleArgs, Handler, NoParams}; // use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo}; use crate::util::{combine, internal_error, invalid_params, parse_error}; // use crate::{CliBindings, SyncCommand}; @@ -60,11 +61,47 @@ type RpcResponse<'a> = yajrc::RpcResponse>; // } // } -struct CliApp(ParentCommand); +struct RootCliHandler( + PhantomData<(Context, Config)>, +); +impl Handler + for RootCliHandler +{ + type Params = NoParams; + type InheritedParams = NoParams; + type Ok = Never; + type Err = RpcError; + fn handle_sync(&self, _: HandleArgs) -> Result { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } +} +impl CliBindings + for RootCliHandler +{ + fn cli_command(&self) -> clap::Command { + Config::command() + } + + fn cli_parse( + &self, + matches: &ArgMatches, + ) -> Result<(std::collections::VecDeque<&'static str>, Value), clap::Error> { + } + + fn cli_display( + &self, + handle_args: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + todo!() + } +} + +struct CliApp( + ParentCommand>, +); impl CliApp { - pub fn new( - commands: Vec>, - ) -> Self { + pub fn new(commands: Vec>) -> Self { Self { cli: CliBindings::from_parent::(), commands, diff --git a/rpc-toolkit/src/context.rs b/rpc-toolkit/src/context.rs index 2c47228..fd7016e 100644 --- a/rpc-toolkit/src/context.rs +++ b/rpc-toolkit/src/context.rs @@ -1,7 +1,7 @@ +use std::any::Any; use tokio::runtime::Handle; -pub trait Context: Send + 'static { - type Metadata: Default + Send + Sync; +pub trait Context: Any + Send + 'static { fn runtime(&self) -> Handle { Handle::current() } diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index 91756f3..2131fe6 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -9,14 +9,14 @@ use yajrc::RpcError; use crate::util::{combine, internal_error, invalid_params, Flat}; -struct HandleAnyArgs { - context: Context, +struct HandleAnyArgs { + context: Box, parent_method: Vec<&'static str>, method: VecDeque<&'static str>, params: Value, } -impl HandleAnyArgs { - fn downcast(self) -> Result, imbl_value::Error> +impl HandleAnyArgs { + fn downcast(self) -> Result, imbl_value::Error> where H: Handler, H::Params: DeserializeOwned, @@ -36,6 +36,9 @@ impl HandleAnyArgs { inherited_params: imbl_value::from_value(params.clone())?, }) } +}rams.clone())?, + }) + } } #[async_trait::async_trait] @@ -294,7 +297,9 @@ pub struct NoParams {} #[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] enum Never {} -struct EmptyHandler(PhantomData<(Params, InheritedParams)>); +pub(crate) struct EmptyHandler( + PhantomData<(Params, InheritedParams)>, +); impl Handler for EmptyHandler { @@ -307,14 +312,13 @@ impl Handler } } -pub struct ParentHandler> { +pub struct ParentHandler = EmptyHandler> { handler: H, subcommands: BTreeMap<&'static str, DynHandler>, } -impl - ParentHandler> +impl ParentHandler where - EmptyHandler: CliBindings, + EmptyHandler: CliBindings, { pub fn new() -> Self { Self { diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index e69e045..da703a3 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -1,4 +1,4 @@ -pub use cli::*; +// pub use cli::*; // pub use command::*; pub use context::Context; pub use handler::*; @@ -24,15 +24,15 @@ pub use handler::*; /// /// See also: [arg](rpc_toolkit_macro::arg), [context](rpc_toolkit_macro::context) pub use rpc_toolkit_macro::command; -pub use server::*; +// pub use server::*; pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc}; -mod cli; -mod command; +// mod cli; +// mod command; mod handler; // pub mod command_helpers; mod context; // mod metadata; // pub mod rpc_server_helpers; -mod server; +// mod server; mod util; diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs new file mode 100644 index 0000000..d0c643f --- /dev/null +++ b/rpc-toolkit/tests/handler.rs @@ -0,0 +1,72 @@ +use std::sync::Arc; + +use clap::Parser; +use rpc_toolkit::{Context, ParentHandler}; +use tokio::{ + runtime::{Handle, Runtime}, + sync::OnceCell, +}; +use url::Url; +use yajrc::RpcError; + +#[derive(Parser)] +#[command( + name = "test-cli", + version, + author, + about = "This is a test cli application." +)] +struct CliConfig { + host: Option, + config: Option, +} +impl CliConfig { + fn load_rec(&mut self) -> Result<(), RpcError> { + if let Some(path) = self.config.as_ref() { + let extra_cfg = + serde_json::from_str(&std::fs::read_to_string(path).map_err(internal_error)?) + .map_err(internal_error)?; + extra_cfg.load_rec()?; + self.merge_with(extra_cfg); + } + Ok(()) + } + fn merge_with(&mut self, extra: Self) { + if self.host.is_none() { + self.host = extra.host; + } + } +} + +struct CliContextSeed { + host: Url, + rt: OnceCell, +} +#[derive(Clone)] +struct CliContext(Arc); +impl Context for CliContext { + fn runtime(&self) -> Handle { + if self.rt.get().is_none() { + self.rt.set(Runtime::new().unwrap()).unwrap(); + } + self.rt.get().unwrap().handle() + } +} + +fn make_cli() -> CliApp { + CliApp::new::<_, CliConfig>(|mut config| { + config.load_rec()?; + Ok(CliContext(Arc::new(CliContextSeed { + host: config + .host + .unwrap_or_else("http://localhost:8080/rpc".parse().unwrap()), + rt: OnceCell::new(), + }))) + }) + .subcommands(make_api()) + .subcommands(ParentHandler::new().subcommand("hello", from_fn(|| Ok("world")))); +} + +fn make_api() -> ParentHandler { + ParentHandler::new().subcommand("hello", from_fn(|| Ok("world"))) +} diff --git a/rpc-toolkit/tests/test.rs b/rpc-toolkit/tests/test.rs index 8c8b206..bc555b0 100644 --- a/rpc-toolkit/tests/test.rs +++ b/rpc-toolkit/tests/test.rs @@ -1,109 +1,109 @@ -use std::path::PathBuf; +// use std::path::PathBuf; -use clap::Parser; -use futures::Future; -use rpc_toolkit::{ - AsyncCommand, CliContextSocket, Command, Contains, Context, DynCommand, LeafCommand, NoParent, - ParentCommand, ParentInfo, Server, ShutdownHandle, -}; -use serde::{Deserialize, Serialize}; -use tokio::net::UnixStream; -use yajrc::RpcError; +// use clap::Parser; +// use futures::Future; +// use rpc_toolkit::{ +// AsyncCommand, CliContextSocket, Command, Contains, Context, DynCommand, LeafCommand, NoParent, +// ParentCommand, ParentInfo, Server, ShutdownHandle, +// }; +// use serde::{Deserialize, Serialize}; +// use tokio::net::UnixStream; +// use yajrc::RpcError; -struct ServerContext; -impl Context for ServerContext { - type Metadata = (); -} +// struct ServerContext; +// impl Context for ServerContext { +// type Metadata = (); +// } -struct CliContext(PathBuf); -impl Context for CliContext { - type Metadata = (); -} -#[async_trait::async_trait] -impl CliContextSocket for CliContext { - type Stream = UnixStream; - async fn connect(&self) -> std::io::Result { - UnixStream::connect(&self.0).await - } -} -#[async_trait::async_trait] -impl rpc_toolkit::CliContext for CliContext { - async fn call_remote( - &self, - method: &str, - params: imbl_value::Value, - ) -> Result { - ::call_remote(self, method, params).await - } -} +// struct CliContext(PathBuf); +// impl Context for CliContext { +// type Metadata = (); +// } +// #[async_trait::async_trait] +// impl CliContextSocket for CliContext { +// type Stream = UnixStream; +// async fn connect(&self) -> std::io::Result { +// UnixStream::connect(&self.0).await +// } +// } +// #[async_trait::async_trait] +// impl rpc_toolkit::CliContext for CliContext { +// async fn call_remote( +// &self, +// method: &str, +// params: imbl_value::Value, +// ) -> Result { +// ::call_remote(self, method, params).await +// } +// } -async fn run_server() { - Server::new( - vec![ - DynCommand::from_parent::(Contains::none()), - DynCommand::from_async::(Contains::none()), - // DynCommand::from_async::(Contains::none()), - // DynCommand::from_sync::(Contains::none()), - // DynCommand::from_sync::(Contains::none()), - ], - || async { Ok(ServerContext) }, - ) - .run_unix("./test.sock", |e| eprintln!("{e}")) - .unwrap() - .1 - .await -} +// async fn run_server() { +// Server::new( +// vec![ +// DynCommand::from_parent::(Contains::none()), +// DynCommand::from_async::(Contains::none()), +// // DynCommand::from_async::(Contains::none()), +// // DynCommand::from_sync::(Contains::none()), +// // DynCommand::from_sync::(Contains::none()), +// ], +// || async { Ok(ServerContext) }, +// ) +// .run_unix("./test.sock", |e| eprintln!("{e}")) +// .unwrap() +// .1 +// .await +// } -#[derive(Debug, Deserialize, Serialize, Parser)] -struct Group { - #[arg(short, long)] - verbose: bool, -} -impl Command for Group { - const NAME: &'static str = "group"; - type Parent = NoParent; -} -impl ParentCommand for Group -where - Ctx: Context, - // SubThing: AsyncCommand, - Thing1: AsyncCommand, -{ - fn subcommands(chain: rpc_toolkit::ParentChain) -> Vec> { - vec![ - // DynCommand::from_async::(chain.child()), - DynCommand::from_async::(Contains::none()), - ] - } -} +// #[derive(Debug, Deserialize, Serialize, Parser)] +// struct Group { +// #[arg(short, long)] +// verbose: bool, +// } +// impl Command for Group { +// const NAME: &'static str = "group"; +// type Parent = NoParent; +// } +// impl ParentCommand for Group +// where +// Ctx: Context, +// // SubThing: AsyncCommand, +// Thing1: AsyncCommand, +// { +// fn subcommands(chain: rpc_toolkit::ParentChain) -> Vec> { +// vec![ +// // DynCommand::from_async::(chain.child()), +// DynCommand::from_async::(Contains::none()), +// ] +// } +// } -#[derive(Debug, Deserialize, Serialize, Parser)] -struct Thing1 { - thing: String, -} -impl Command for Thing1 { - const NAME: &'static str = "thing1"; - type Parent = NoParent; -} -impl LeafCommand for Thing1 { - type Ok = String; - type Err = RpcError; - fn display(self, _: ServerContext, _: rpc_toolkit::ParentInfo, res: Self::Ok) { - println!("{}", res); - } -} -#[async_trait::async_trait] -impl AsyncCommand for Thing1 { - async fn implementation( - self, - _: ServerContext, - _: ParentInfo, - ) -> Result { - Ok(format!("Thing1 is {}", self.thing)) - } -} +// #[derive(Debug, Deserialize, Serialize, Parser)] +// struct Thing1 { +// thing: String, +// } +// impl Command for Thing1 { +// const NAME: &'static str = "thing1"; +// type Parent = NoParent; +// } +// impl LeafCommand for Thing1 { +// type Ok = String; +// type Err = RpcError; +// fn display(self, _: ServerContext, _: rpc_toolkit::ParentInfo, res: Self::Ok) { +// println!("{}", res); +// } +// } +// #[async_trait::async_trait] +// impl AsyncCommand for Thing1 { +// async fn implementation( +// self, +// _: ServerContext, +// _: ParentInfo, +// ) -> Result { +// Ok(format!("Thing1 is {}", self.thing)) +// } +// } -#[tokio::test] -async fn test() { - let server = tokio::spawn(run_server()); -} +// #[tokio::test] +// async fn test() { +// let server = tokio::spawn(run_server()); +// } From b4661cab407be10e3bcc7f0d6ca02a7098df4cc3 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 14 Dec 2023 19:04:05 -0700 Subject: [PATCH 08/79] wip --- rpc-toolkit/src/context.rs | 104 ++++++- rpc-toolkit/src/handler.rs | 525 ++++++++++++++++------------------- rpc-toolkit/src/lib.rs | 2 +- rpc-toolkit/tests/handler.rs | 61 ++-- 4 files changed, 385 insertions(+), 307 deletions(-) diff --git a/rpc-toolkit/src/context.rs b/rpc-toolkit/src/context.rs index fd7016e..79720f8 100644 --- a/rpc-toolkit/src/context.rs +++ b/rpc-toolkit/src/context.rs @@ -1,8 +1,110 @@ -use std::any::Any; +use std::any::{Any, TypeId}; +use std::collections::BTreeSet; + use tokio::runtime::Handle; +use crate::Handler; + pub trait Context: Any + Send + 'static { fn runtime(&self) -> Handle { Handle::current() } } + +#[allow(private_bounds)] +pub trait IntoContext: sealed::Sealed + Any + Send + Sized + 'static { + fn type_ids_for + ?Sized>(handler: &H) -> Option>; + fn inner_type_id(&self) -> TypeId; + fn upcast(self) -> AnyContext; + fn downcast(value: AnyContext) -> Result; +} + +impl IntoContext for C { + fn type_ids_for + ?Sized>(handler: &H) -> Option> { + let mut set = BTreeSet::new(); + set.insert(TypeId::of::()); + Some(set) + } + fn inner_type_id(&self) -> TypeId { + TypeId::of::() + } + fn upcast(self) -> AnyContext { + AnyContext::new(self) + } + fn downcast(value: AnyContext) -> Result { + if value.0.type_id() == TypeId::of::() { + unsafe { Ok(value.downcast_unchecked::()) } + } else { + Err(value) + } + } +} + +pub enum EitherContext { + C1(C1), + C2(C2), +} +impl IntoContext for EitherContext { + fn type_ids_for + ?Sized>(handler: &H) -> Option> { + let mut set = BTreeSet::new(); + set.insert(TypeId::of::()); + set.insert(TypeId::of::()); + Some(set) + } + fn inner_type_id(&self) -> TypeId { + match self { + EitherContext::C1(c) => c.type_id(), + EitherContext::C2(c) => c.type_id(), + } + } + fn downcast(value: AnyContext) -> Result { + if value.inner_type_id() == TypeId::of::() { + Ok(EitherContext::C1(C1::downcast(value)?)) + } else if value.inner_type_id() == TypeId::of::() { + Ok(EitherContext::C2(C2::downcast(value)?)) + } else { + Err(value) + } + } + fn upcast(self) -> AnyContext { + match self { + Self::C1(c) => AnyContext::new(c), + Self::C2(c) => AnyContext::new(c), + } + } +} + +pub struct AnyContext(Box); +impl AnyContext { + pub fn new(value: C) -> Self { + Self(Box::new(value)) + } + unsafe fn downcast_unchecked(self) -> C { + unsafe { + let raw: *mut dyn Context = Box::into_raw(self.0); + *Box::from_raw(raw as *mut C) + } + } +} + +impl IntoContext for AnyContext { + fn type_ids_for + ?Sized>(_: &H) -> Option> { + None + } + fn inner_type_id(&self) -> TypeId { + self.0.type_id() + } + fn downcast(value: AnyContext) -> Result { + Ok(value) + } + fn upcast(self) -> AnyContext { + self + } +} + +mod sealed { + pub(crate) trait Sealed {} + impl Sealed for C {} + impl Sealed for super::EitherContext {} + impl Sealed for super::AnyContext {} +} diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index 2131fe6..0e0c325 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -1,5 +1,7 @@ -use std::collections::{BTreeMap, VecDeque}; +use std::any::TypeId; +use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::marker::PhantomData; +use std::sync::Arc; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser}; use imbl_value::Value; @@ -7,16 +9,18 @@ use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use yajrc::RpcError; +use crate::context::{AnyContext, IntoContext}; +use crate::handler; use crate::util::{combine, internal_error, invalid_params, Flat}; struct HandleAnyArgs { - context: Box, + context: AnyContext, parent_method: Vec<&'static str>, method: VecDeque<&'static str>, params: Value, } impl HandleAnyArgs { - fn downcast(self) -> Result, imbl_value::Error> + fn downcast(self) -> Result, imbl_value::Error> where H: Handler, H::Params: DeserializeOwned, @@ -29,38 +33,34 @@ impl HandleAnyArgs { params, } = self; Ok(HandleArgs { - context, + context: Context::downcast(context).map_err(|_| imbl_value::Error { + kind: imbl_value::ErrorKind::Deserialization, + source: serde::ser::Error::custom("context does not match expected"), + })?, parent_method, method, params: imbl_value::from_value(params.clone())?, inherited_params: imbl_value::from_value(params.clone())?, }) } -}rams.clone())?, - }) - } } #[async_trait::async_trait] -trait HandleAny { - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; +trait HandleAny { + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; // async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result; } -trait CliBindingsAny { +trait CliBindingsAny { fn cli_command(&self) -> Command; fn cli_parse( &self, matches: &ArgMatches, ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; - fn cli_display( - &self, - handle_args: HandleAnyArgs, - result: Value, - ) -> Result<(), RpcError>; + fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError>; } -pub trait CliBindings: Handler { +pub trait CliBindings: Handler { fn cli_command(&self) -> Command; fn cli_parse( &self, @@ -73,7 +73,7 @@ pub trait CliBindings: Handler { ) -> Result<(), Self::Err>; } -pub trait PrintCliResult: Handler { +pub trait PrintCliResult: Handler { fn print( &self, handle_args: HandleArgs, @@ -83,7 +83,7 @@ pub trait PrintCliResult: Handler { // impl PrintCliResult for H // where -// Context: crate::Context, +// Context: IntoContext, // H: Handler, // H::Ok: Display, // { @@ -103,7 +103,7 @@ struct WithCliBindings { impl Handler for WithCliBindings where - Context: crate::Context, + Context: IntoContext, H: Handler, { type Params = H::Params; @@ -132,7 +132,7 @@ where impl CliBindings for WithCliBindings where - Context: crate::Context, + Context: IntoContext, H: Handler, H::Params: FromArgMatches + CommandFactory + Serialize, H: PrintCliResult, @@ -176,18 +176,16 @@ where } } -trait HandleAnyWithCli: HandleAny + CliBindingsAny {} -impl + CliBindingsAny> - HandleAnyWithCli for T -{ -} +trait HandleAnyWithCli: HandleAny + CliBindingsAny {} +impl HandleAnyWithCli for T {} -enum DynHandler { - WithoutCli(Box>), - WithCli(Box>), +#[derive(Clone)] +enum DynHandler { + WithoutCli(Arc), + WithCli(Arc), } -impl HandleAny for DynHandler { - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { +impl HandleAny for DynHandler { + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { match self { DynHandler::WithoutCli(h) => h.handle_sync(handle_args), DynHandler::WithCli(h) => h.handle_sync(handle_args), @@ -195,47 +193,23 @@ impl HandleAny for DynHandler { } } -pub struct HandleArgs + ?Sized> { +pub struct HandleArgs + ?Sized> { context: Context, parent_method: Vec<&'static str>, method: VecDeque<&'static str>, params: H::Params, inherited_params: H::InheritedParams, } -impl HandleArgs -where - Context: crate::Context, - H: Handler, - H::Params: Serialize, - H::InheritedParams: Serialize, -{ - fn upcast( - Self { - context, - parent_method, - method, - params, - inherited_params, - }: Self, - ) -> Result, imbl_value::Error> { - Ok(HandleAnyArgs { - context, - parent_method, - method, - params: combine( - imbl_value::to_value(¶ms)?, - imbl_value::to_value(&inherited_params)?, - )?, - }) - } -} -pub trait Handler { +pub trait Handler { type Params; type InheritedParams; type Ok; type Err; fn handle_sync(&self, handle_args: HandleArgs) -> Result; + fn contexts(&self) -> Option> { + Context::type_ids_for(self) + } } struct AnyHandler { @@ -243,14 +217,14 @@ struct AnyHandler { handler: H, } -impl> HandleAny for AnyHandler +impl> HandleAny for AnyHandler where H::Params: DeserializeOwned, H::InheritedParams: DeserializeOwned, H::Ok: Serialize, RpcError: From, { - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { imbl_value::to_value( &self .handler @@ -260,8 +234,7 @@ where } } -impl> CliBindingsAny - for AnyHandler +impl> CliBindingsAny for AnyHandler where H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, H::InheritedParams: DeserializeOwned, @@ -277,11 +250,7 @@ where ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { self.handler.cli_parse(matches) } - fn cli_display( - &self, - handle_args: HandleAnyArgs, - result: Value, - ) -> Result<(), RpcError> { + fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError> { self.handler .cli_display( handle_args.downcast().map_err(invalid_params)?, @@ -295,83 +264,83 @@ where pub struct NoParams {} #[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] -enum Never {} +pub enum Never {} -pub(crate) struct EmptyHandler( - PhantomData<(Params, InheritedParams)>, -); -impl Handler - for EmptyHandler -{ - type Params = Params; - type InheritedParams = InheritedParams; - type Ok = Never; - type Err = RpcError; - fn handle_sync(&self, _: HandleArgs) -> Result { - Err(yajrc::METHOD_NOT_FOUND_ERROR) +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +struct Name(Option<&'static str>); +impl<'a> std::borrow::Borrow> for Name { + fn borrow(&self) -> &Option<&'a str> { + &self.0 } } -pub struct ParentHandler = EmptyHandler> { - handler: H, - subcommands: BTreeMap<&'static str, DynHandler>, +struct SubcommandMap(BTreeMap, DynHandler>>); +impl SubcommandMap { + fn insert( + &mut self, + ctx_tys: Option>, + name: Option<&'static str>, + handler: DynHandler, + ) { + let mut for_name = self.0.remove(&name).unwrap_or_default(); + if let Some(ctx_tys) = ctx_tys { + for ctx_ty in ctx_tys { + for_name.insert(Some(ctx_ty), handler.clone()); + } + } else { + for_name.insert(None, handler); + } + self.0.insert(Name(name), for_name); + } + + fn get<'a>(&'a self, ctx_ty: TypeId, name: Option<&str>) -> Option<(Name, &'a DynHandler)> { + if let Some((name, for_name)) = self.0.get_key_value(&name) { + if let Some(for_ctx) = for_name.get(&Some(ctx_ty)) { + Some((*name, for_ctx)) + } else { + for_name.get(&None).map(|h| (*name, h)) + } + } else { + None + } + } } -impl ParentHandler -where - EmptyHandler: CliBindings, -{ + +pub struct ParentHandler { + _phantom: PhantomData<(Params, InheritedParams)>, + subcommands: SubcommandMap, +} +impl ParentHandler { pub fn new() -> Self { Self { - handler: WithCliBindings { - _ctx: PhantomData, - handler: EmptyHandler(PhantomData).into(), - }, - subcommands: BTreeMap::new(), - } - } -} - -impl - ParentHandler> -{ - pub fn new_no_cli() -> Self { - Self { - handler: EmptyHandler(PhantomData).into(), - subcommands: BTreeMap::new(), - } - } -} - -impl> From for ParentHandler { - fn from(value: H) -> Self { - Self { - handler: value.into(), - subcommands: BTreeMap::new(), + _phantom: PhantomData, + subcommands: SubcommandMap(BTreeMap::new()), } } } struct InheritanceHandler< - Context: crate::Context, + Context: IntoContext, + Params, + InheritedParams, H: Handler, - SubH: Handler, - F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams, + F: Fn(Params, InheritedParams) -> H::InheritedParams, > { - _phantom: PhantomData<(Context, H)>, - handler: SubH, + _phantom: PhantomData<(Context, Params, InheritedParams)>, + handler: H, inherit: F, } -impl< - Context: crate::Context, - H: Handler, - SubH: Handler, - F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams, - > Handler for InheritanceHandler +impl Handler + for InheritanceHandler +where + Context: IntoContext, + H: Handler, + F: Fn(Params, InheritedParams) -> H::InheritedParams, { - type Params = SubH::Params; - type InheritedParams = Flat; - type Ok = SubH::Ok; - type Err = SubH::Err; + type Params = H::Params; + type InheritedParams = Flat; + type Ok = H::Ok; + type Err = H::Err; fn handle_sync( &self, HandleArgs { @@ -392,12 +361,12 @@ impl< } } -impl PrintCliResult for InheritanceHandler +impl PrintCliResult + for InheritanceHandler where - Context: crate::Context, - H: Handler, - SubH: Handler + PrintCliResult, - F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams, + Context: IntoContext, + H: Handler + PrintCliResult, + F: Fn(Params, InheritedParams) -> H::InheritedParams, { fn print( &self, @@ -423,17 +392,19 @@ where } } -impl> ParentHandler { - pub fn subcommand(mut self, method: &'static str, handler: SubH) -> Self +impl ParentHandler { + pub fn subcommand(mut self, name: Option<&'static str>, handler: H) -> Self where - SubH: Handler + PrintCliResult + 'static, - SubH::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, - SubH::Ok: Serialize + DeserializeOwned, - RpcError: From, + Context: IntoContext, + H: Handler + PrintCliResult + 'static, + H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, { self.subcommands.insert( - method, - DynHandler::WithCli(Box::new(AnyHandler { + handler.contexts(), + name, + DynHandler::WithCli(Arc::new(AnyHandler { _ctx: PhantomData, handler: WithCliBindings { _ctx: PhantomData, @@ -443,29 +414,52 @@ impl> ParentHandler { ); self } - pub fn subcommand_with_inherited( + pub fn subcommand_no_cli(mut self, name: Option<&'static str>, handler: H) -> Self + where + Context: IntoContext, + H: Handler + 'static, + H::Params: DeserializeOwned, + H::Ok: Serialize, + RpcError: From, + { + self.subcommands.insert( + handler.contexts(), + name, + DynHandler::WithoutCli(Arc::new(AnyHandler { + _ctx: PhantomData, + handler, + })), + ); + self + } +} +impl ParentHandler +where + Params: DeserializeOwned + 'static, + InheritedParams: DeserializeOwned + 'static, +{ + pub fn subcommand_with_inherited( mut self, - method: &'static str, - handler: SubH, + name: Option<&'static str>, + handler: H, inherit: F, ) -> Self where - SubH: Handler + PrintCliResult + 'static, - SubH::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, - SubH::Ok: Serialize + DeserializeOwned, - H: 'static, - H::Params: DeserializeOwned, - H::InheritedParams: DeserializeOwned, - RpcError: From, - F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams + 'static, + Context: IntoContext, + H: Handler + PrintCliResult + 'static, + H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, + F: Fn(Params, InheritedParams) -> H::InheritedParams + 'static, { self.subcommands.insert( - method, - DynHandler::WithCli(Box::new(AnyHandler { + handler.contexts(), + name, + DynHandler::WithCli(Arc::new(AnyHandler { _ctx: PhantomData, handler: WithCliBindings { _ctx: PhantomData, - handler: InheritanceHandler:: { + handler: InheritanceHandler:: { _phantom: PhantomData, handler, inherit, @@ -475,43 +469,26 @@ impl> ParentHandler { ); self } - pub fn subcommand_no_cli(mut self, method: &'static str, handler: SubH) -> Self - where - SubH: Handler + 'static, - SubH::Params: DeserializeOwned, - SubH::Ok: Serialize, - RpcError: From, - { - self.subcommands.insert( - method, - DynHandler::WithoutCli(Box::new(AnyHandler { - _ctx: PhantomData, - handler, - })), - ); - self - } - pub fn subcommand_with_inherited_no_cli( + pub fn subcommand_with_inherited_no_cli( mut self, - method: &'static str, - handler: SubH, + name: Option<&'static str>, + handler: H, inherit: F, ) -> Self where - SubH: Handler + 'static, - SubH::Params: DeserializeOwned, - SubH::Ok: Serialize, - H: 'static, + Context: IntoContext, + H: Handler + 'static, H::Params: DeserializeOwned, - H::InheritedParams: DeserializeOwned, - RpcError: From, - F: Fn(H::Params, H::InheritedParams) -> SubH::InheritedParams + 'static, + H::Ok: Serialize, + RpcError: From, + F: Fn(Params, InheritedParams) -> H::InheritedParams + 'static, { self.subcommands.insert( - method, - DynHandler::WithoutCli(Box::new(AnyHandler { + handler.contexts(), + name, + DynHandler::WithoutCli(Arc::new(AnyHandler { _ctx: PhantomData, - handler: InheritanceHandler:: { + handler: InheritanceHandler:: { _phantom: PhantomData, handler, inherit, @@ -522,17 +499,11 @@ impl> ParentHandler { } } -impl Handler for ParentHandler -where - Context: crate::Context, - H: Handler, - H::Params: Serialize, - H::InheritedParams: Serialize, - H::Ok: Serialize + DeserializeOwned, - RpcError: From, +impl Handler + for ParentHandler { - type Params = H::Params; - type InheritedParams = H::InheritedParams; + type Params = Params; + type InheritedParams = InheritedParams; type Ok = Value; type Err = RpcError; fn handle_sync( @@ -543,71 +514,74 @@ where mut method, params, inherited_params, - }: HandleArgs, + }: HandleArgs, ) -> Result { - if let Some(cmd) = method.pop_front() { + let cmd = method.pop_front(); + if let Some(cmd) = cmd { parent_method.push(cmd); - if let Some(sub_handler) = self.subcommands.get(cmd) { - sub_handler.handle_sync(HandleAnyArgs { - context, - parent_method, - method, - params: imbl_value::to_value(&Flat(params, inherited_params)) - .map_err(invalid_params)?, - }) - } else { - Err(yajrc::METHOD_NOT_FOUND_ERROR) - } - } else { - self.handler - .handle_sync(HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - }) - .map_err(RpcError::from) - .and_then(|r| imbl_value::to_value(&r).map_err(internal_error)) } + if let Some((_, sub_handler)) = self.subcommands.get(context.inner_type_id(), cmd) { + sub_handler.handle_sync(HandleAnyArgs { + context: context.upcast(), + parent_method, + method, + params: imbl_value::to_value(&Flat(params, inherited_params)) + .map_err(invalid_params)?, + }) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } + fn contexts(&self) -> Option> { + let mut set = BTreeSet::new(); + for ctx_ty in self.subcommands.0.values().flat_map(|c| c.keys()) { + set.insert((*ctx_ty)?); + } + Some(set) } } -impl CliBindings for ParentHandler +impl CliBindings for ParentHandler where - Context: crate::Context, - H: CliBindings, - H::Params: FromArgMatches + CommandFactory + Serialize, - H::InheritedParams: Serialize, - H::Ok: PrintCliResult + Serialize + DeserializeOwned, - RpcError: From, + Params: FromArgMatches + CommandFactory + Serialize, + InheritedParams: Serialize, { fn cli_command(&self) -> Command { - H::Params::command().subcommands(self.subcommands.iter().filter_map(|(method, handler)| { - match handler { - DynHandler::WithCli(h) => Some(h.cli_command().name(method)), - DynHandler::WithoutCli(_) => None, - } - })) + // Params::command().subcommands(self.subcommands.0.iter().filter_map(|(name, handlers)| { + // handlers.iter().find_map(|(ctx_ty, handler)| { + // if let DynHandler::WithCli(h) = handler { + // h.cli_command() + // } + // }) + // })) + todo!() } fn cli_parse( &self, matches: &ArgMatches, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - let (_, root_params) = self.handler.cli_parse(matches)?; - if let Some((sub, matches)) = matches.subcommand() { - if let Some((sub, DynHandler::WithCli(h))) = self.subcommands.get_key_value(sub) { - let (mut method, params) = h.cli_parse(matches)?; - method.push_front(*sub); - return Ok(( - method, - combine(root_params, params).map_err(|e| { - clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e) - })?, - )); - } - } - Ok((VecDeque::new(), root_params)) + // let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?) + // .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?; + // let (m, matches) = match matches.subcommand() { + // Some((m, matches)) => (Some(m), matches), + // None => (None, matches), + // }; + // if let Some((SubcommandKey((_, m)), DynHandler::WithCli(h))) = self + // .subcommands + // .get_key_value(&(TypeId::of::(), m)) + // { + // let (mut method, params) = h.cli_parse(matches)?; + // if let Some(m) = m { + // method.push_front(*m); + // } + // return Ok(( + // method, + // combine(root_params, params) + // .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e))?, + // )); + // } + // Ok((VecDeque::new(), root_params)) + todo!() } fn cli_display( &self, @@ -617,39 +591,30 @@ where mut method, params, inherited_params, - }: HandleArgs, + }: HandleArgs, result: Self::Ok, ) -> Result<(), Self::Err> { - if let Some(cmd) = method.pop_front() { - parent_method.push(cmd); - if let Some(DynHandler::WithCli(sub_handler)) = self.subcommands.get(cmd) { - sub_handler.cli_display( - HandleAnyArgs { - context, - parent_method, - method, - params: imbl_value::to_value(&Flat(params, inherited_params)) - .map_err(invalid_params)?, - }, - result, - ) - } else { - Err(yajrc::METHOD_NOT_FOUND_ERROR) - } - } else { - self.handler - .cli_display( - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - }, - imbl_value::from_value(result).map_err(internal_error)?, - ) - .map_err(RpcError::from) - } + // let cmd = method.pop_front(); + // if let Some(cmd) = cmd { + // parent_method.push(cmd); + // } + // if let Some(DynHandler::WithCli(sub_handler)) = + // self.subcommands.get(&(context.inner_type_id(), cmd)) + // { + // sub_handler.cli_display( + // HandleAnyArgs { + // context: AnyContext::new(context), + // parent_method, + // method, + // params: imbl_value::to_value(&Flat(params, inherited_params)) + // .map_err(invalid_params)?, + // }, + // result, + // ) + // } else { + // Err(yajrc::METHOD_NOT_FOUND_ERROR) + // } + todo!() } } @@ -667,7 +632,7 @@ pub fn from_fn(function: F) -> FromFn { impl Handler for FromFn where - Context: crate::Context, + Context: IntoContext, F: Fn() -> Result, { type Params = NoParams; @@ -681,7 +646,7 @@ where impl Handler for FromFn where - Context: crate::Context, + Context: IntoContext, F: Fn(Context) -> Result, { type Params = NoParams; @@ -694,7 +659,7 @@ where } impl Handler for FromFn where - Context: crate::Context, + Context: IntoContext, F: Fn(Context, Params) -> Result, Params: DeserializeOwned, { @@ -712,7 +677,7 @@ where impl Handler for FromFn where - Context: crate::Context, + Context: IntoContext, F: Fn(Context, Params, InheritedParams) -> Result, Params: DeserializeOwned, InheritedParams: DeserializeOwned, diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index da703a3..fec86b5 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -1,6 +1,6 @@ // pub use cli::*; // pub use command::*; -pub use context::Context; +pub use context::*; pub use handler::*; /// `#[command(...)]` /// - `#[command(cli_only)]` -> executed by CLI instead of RPC server (leaf commands only) diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index d0c643f..5b0eaeb 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -1,15 +1,16 @@ +use std::fmt::Display; +use std::path::PathBuf; use std::sync::Arc; use clap::Parser; -use rpc_toolkit::{Context, ParentHandler}; -use tokio::{ - runtime::{Handle, Runtime}, - sync::OnceCell, -}; +use rpc_toolkit::{from_fn, Context, ParentHandler}; +use serde::Deserialize; +use tokio::runtime::{Handle, Runtime}; +use tokio::sync::OnceCell; use url::Url; use yajrc::RpcError; -#[derive(Parser)] +#[derive(Parser, Deserialize)] #[command( name = "test-cli", version, @@ -17,13 +18,13 @@ use yajrc::RpcError; about = "This is a test cli application." )] struct CliConfig { - host: Option, + host: Option, config: Option, } impl CliConfig { fn load_rec(&mut self) -> Result<(), RpcError> { if let Some(path) = self.config.as_ref() { - let extra_cfg = + let mut extra_cfg: Self = serde_json::from_str(&std::fs::read_to_string(path).map_err(internal_error)?) .map_err(internal_error)?; extra_cfg.load_rec()?; @@ -46,27 +47,37 @@ struct CliContextSeed { struct CliContext(Arc); impl Context for CliContext { fn runtime(&self) -> Handle { - if self.rt.get().is_none() { - self.rt.set(Runtime::new().unwrap()).unwrap(); + if self.0.rt.get().is_none() { + self.0.rt.set(Runtime::new().unwrap()).unwrap(); } - self.rt.get().unwrap().handle() + self.0.rt.get().unwrap().handle().clone() } } -fn make_cli() -> CliApp { - CliApp::new::<_, CliConfig>(|mut config| { - config.load_rec()?; - Ok(CliContext(Arc::new(CliContextSeed { - host: config - .host - .unwrap_or_else("http://localhost:8080/rpc".parse().unwrap()), - rt: OnceCell::new(), - }))) - }) - .subcommands(make_api()) - .subcommands(ParentHandler::new().subcommand("hello", from_fn(|| Ok("world")))); -} +// fn make_cli() -> CliApp { +// CliApp::new::<_, CliConfig>(|mut config| { +// config.load_rec()?; +// Ok(CliContext(Arc::new(CliContextSeed { +// host: config +// .host +// .unwrap_or_else("http://localhost:8080/rpc".parse().unwrap()), +// rt: OnceCell::new(), +// }))) +// }) +// .subcommands(make_api()) +// .subcommands(ParentHandler::new().subcommand("hello", from_fn(|| Ok("world")))); +// } fn make_api() -> ParentHandler { - ParentHandler::new().subcommand("hello", from_fn(|| Ok("world"))) + ParentHandler::new().subcommand_no_cli( + Some("hello"), + from_fn(|_: CliContext| Ok::<_, RpcError>("world")), + ) +} + +pub fn internal_error(e: impl Display) -> RpcError { + RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + } } From 8ea6909ac582bdc1baa96ca5cc2e1c3d940b9eb6 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 15 Dec 2023 12:58:52 -0700 Subject: [PATCH 09/79] finish todos --- rpc-toolkit/src/handler.rs | 127 ++++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 59 deletions(-) diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index 0e0c325..3a2fb53 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -10,7 +10,6 @@ use serde::{Deserialize, Serialize}; use yajrc::RpcError; use crate::context::{AnyContext, IntoContext}; -use crate::handler; use crate::util::{combine, internal_error, invalid_params, Flat}; struct HandleAnyArgs { @@ -52,19 +51,21 @@ trait HandleAny { } trait CliBindingsAny { - fn cli_command(&self) -> Command; + fn cli_command(&self, ctx_ty: TypeId) -> Command; fn cli_parse( &self, matches: &ArgMatches, + ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError>; } pub trait CliBindings: Handler { - fn cli_command(&self) -> Command; + fn cli_command(&self, ctx_ty: TypeId) -> Command; fn cli_parse( &self, matches: &ArgMatches, + ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; fn cli_display( &self, @@ -137,12 +138,13 @@ where H::Params: FromArgMatches + CommandFactory + Serialize, H: PrintCliResult, { - fn cli_command(&self) -> Command { + fn cli_command(&self, _: TypeId) -> Command { H::Params::command() } fn cli_parse( &self, matches: &ArgMatches, + _: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { H::Params::from_arg_matches(matches).and_then(|a| { Ok(( @@ -241,14 +243,15 @@ where H::Ok: Serialize + DeserializeOwned, RpcError: From, { - fn cli_command(&self) -> Command { - self.handler.cli_command() + fn cli_command(&self, ctx_ty: TypeId) -> Command { + self.handler.cli_command(ctx_ty) } fn cli_parse( &self, matches: &ArgMatches, + ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.handler.cli_parse(matches) + self.handler.cli_parse(matches, ctx_ty) } fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError> { self.handler @@ -546,42 +549,49 @@ where Params: FromArgMatches + CommandFactory + Serialize, InheritedParams: Serialize, { - fn cli_command(&self) -> Command { - // Params::command().subcommands(self.subcommands.0.iter().filter_map(|(name, handlers)| { - // handlers.iter().find_map(|(ctx_ty, handler)| { - // if let DynHandler::WithCli(h) = handler { - // h.cli_command() - // } - // }) - // })) - todo!() + fn cli_command(&self, ctx_ty: TypeId) -> Command { + let mut base = Params::command(); + for (name, handlers) in &self.subcommands.0 { + if let (Name(Some(name)), Some(DynHandler::WithCli(handler))) = ( + name, + if let Some(handler) = handlers.get(&Some(ctx_ty)) { + Some(handler) + } else if let Some(handler) = handlers.get(&None) { + Some(handler) + } else { + None + }, + ) { + base = base.subcommand(handler.cli_command(ctx_ty).name(name)); + } + } + base } fn cli_parse( &self, matches: &ArgMatches, + ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - // let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?) - // .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?; - // let (m, matches) = match matches.subcommand() { - // Some((m, matches)) => (Some(m), matches), - // None => (None, matches), - // }; - // if let Some((SubcommandKey((_, m)), DynHandler::WithCli(h))) = self - // .subcommands - // .get_key_value(&(TypeId::of::(), m)) - // { - // let (mut method, params) = h.cli_parse(matches)?; - // if let Some(m) = m { - // method.push_front(*m); - // } - // return Ok(( - // method, - // combine(root_params, params) - // .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e))?, - // )); - // } - // Ok((VecDeque::new(), root_params)) - todo!() + let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?; + let (name, matches) = match matches.subcommand() { + Some((name, matches)) => (Some(name), matches), + None => (None, matches), + }; + if let Some((Name(Some(name)), DynHandler::WithCli(handler))) = + self.subcommands.get(ctx_ty, name) + { + let (mut method, params) = handler.cli_parse(matches, ctx_ty)?; + method.push_front(name); + + Ok(( + method, + combine(root_params, params) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e))?, + )) + } else { + Ok((VecDeque::new(), root_params)) + } } fn cli_display( &self, @@ -594,27 +604,26 @@ where }: HandleArgs, result: Self::Ok, ) -> Result<(), Self::Err> { - // let cmd = method.pop_front(); - // if let Some(cmd) = cmd { - // parent_method.push(cmd); - // } - // if let Some(DynHandler::WithCli(sub_handler)) = - // self.subcommands.get(&(context.inner_type_id(), cmd)) - // { - // sub_handler.cli_display( - // HandleAnyArgs { - // context: AnyContext::new(context), - // parent_method, - // method, - // params: imbl_value::to_value(&Flat(params, inherited_params)) - // .map_err(invalid_params)?, - // }, - // result, - // ) - // } else { - // Err(yajrc::METHOD_NOT_FOUND_ERROR) - // } - todo!() + let cmd = method.pop_front(); + if let Some(cmd) = cmd { + parent_method.push(cmd); + } + if let Some((_, DynHandler::WithCli(sub_handler))) = + self.subcommands.get(context.inner_type_id(), cmd) + { + sub_handler.cli_display( + HandleAnyArgs { + context, + parent_method, + method, + params: imbl_value::to_value(&Flat(params, inherited_params)) + .map_err(invalid_params)?, + }, + result, + ) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } } } From 875d4beacb5bd631f97c8ce424d4a4ab19423a99 Mon Sep 17 00:00:00 2001 From: J H Date: Mon, 18 Dec 2023 10:27:49 -0700 Subject: [PATCH 10/79] feat: Add more helper --- rpc-toolkit/src/handler.rs | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index 3a2fb53..82f4eab 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -396,7 +396,11 @@ where } impl ParentHandler { - pub fn subcommand(mut self, name: Option<&'static str>, handler: H) -> Self + pub fn subcommand( + mut self, + name: impl Into>, + handler: H, + ) -> Self where Context: IntoContext, H: Handler + PrintCliResult + 'static, @@ -406,7 +410,7 @@ impl ParentHandler { { self.subcommands.insert( handler.contexts(), - name, + name.into(), DynHandler::WithCli(Arc::new(AnyHandler { _ctx: PhantomData, handler: WithCliBindings { @@ -417,7 +421,11 @@ impl ParentHandler { ); self } - pub fn subcommand_no_cli(mut self, name: Option<&'static str>, handler: H) -> Self + pub fn subcommand_no_cli( + mut self, + name: impl Into>, + handler: H, + ) -> Self where Context: IntoContext, H: Handler + 'static, @@ -427,7 +435,7 @@ impl ParentHandler { { self.subcommands.insert( handler.contexts(), - name, + name.into(), DynHandler::WithoutCli(Arc::new(AnyHandler { _ctx: PhantomData, handler, @@ -443,7 +451,7 @@ where { pub fn subcommand_with_inherited( mut self, - name: Option<&'static str>, + name: impl Into>, handler: H, inherit: F, ) -> Self @@ -457,7 +465,7 @@ where { self.subcommands.insert( handler.contexts(), - name, + name.into(), DynHandler::WithCli(Arc::new(AnyHandler { _ctx: PhantomData, handler: WithCliBindings { @@ -474,7 +482,7 @@ where } pub fn subcommand_with_inherited_no_cli( mut self, - name: Option<&'static str>, + name: impl Into>, handler: H, inherit: F, ) -> Self @@ -488,7 +496,7 @@ where { self.subcommands.insert( handler.contexts(), - name, + name.into(), DynHandler::WithoutCli(Arc::new(AnyHandler { _ctx: PhantomData, handler: InheritanceHandler:: { From 178790c67464da59106c45d0d5a22e63c35501da Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Mon, 18 Dec 2023 11:18:57 -0700 Subject: [PATCH 11/79] root handler --- rpc-toolkit/src/handler.rs | 65 +++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index 82f4eab..ab1c498 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -396,11 +396,7 @@ where } impl ParentHandler { - pub fn subcommand( - mut self, - name: impl Into>, - handler: H, - ) -> Self + pub fn subcommand(mut self, name: &'static str, handler: H) -> Self where Context: IntoContext, H: Handler + PrintCliResult + 'static, @@ -421,11 +417,7 @@ impl ParentHandler { ); self } - pub fn subcommand_no_cli( - mut self, - name: impl Into>, - handler: H, - ) -> Self + pub fn subcommand_no_cli(mut self, name: &'static str, handler: H) -> Self where Context: IntoContext, H: Handler + 'static, @@ -451,7 +443,7 @@ where { pub fn subcommand_with_inherited( mut self, - name: impl Into>, + name: &'static str, handler: H, inherit: F, ) -> Self @@ -482,7 +474,7 @@ where } pub fn subcommand_with_inherited_no_cli( mut self, - name: impl Into>, + name: &'static str, handler: H, inherit: F, ) -> Self @@ -508,6 +500,55 @@ where ); self } + pub fn root_handler(mut self, handler: H, inherit: F) -> Self + where + Context: IntoContext, + H: Handler + PrintCliResult + 'static, + H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, + F: Fn(Params, InheritedParams) -> H::InheritedParams + 'static, + { + self.subcommands.insert( + handler.contexts(), + None, + DynHandler::WithCli(Arc::new(AnyHandler { + _ctx: PhantomData, + handler: WithCliBindings { + _ctx: PhantomData, + handler: InheritanceHandler:: { + _phantom: PhantomData, + handler, + inherit, + }, + }, + })), + ); + self + } + pub fn root_handler_no_cli(mut self, handler: H, inherit: F) -> Self + where + Context: IntoContext, + H: Handler + 'static, + H::Params: DeserializeOwned, + H::Ok: Serialize, + RpcError: From, + F: Fn(Params, InheritedParams) -> H::InheritedParams + 'static, + { + self.subcommands.insert( + handler.contexts(), + None, + DynHandler::WithoutCli(Arc::new(AnyHandler { + _ctx: PhantomData, + handler: InheritanceHandler:: { + _phantom: PhantomData, + handler, + inherit, + }, + })), + ); + self + } } impl Handler From c8bd32ba9720288f75c41117aa92c73a2f3d2ac3 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 21 Dec 2023 16:07:39 -0700 Subject: [PATCH 12/79] connect apis --- rpc-toolkit/src/cli.rs | 378 +++++++--------------- rpc-toolkit/src/context.rs | 21 +- rpc-toolkit/src/handler.rs | 576 ++++++++++++++++++++++++++------- rpc-toolkit/src/lib.rs | 14 +- rpc-toolkit/src/server/http.rs | 2 +- rpc-toolkit/src/server/mod.rs | 72 ++--- rpc-toolkit/tests/handler.rs | 6 +- 7 files changed, 628 insertions(+), 441 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 232002f..6f11f9f 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -1,232 +1,82 @@ -use std::{ffi::OsString, marker::PhantomData}; +use std::any::TypeId; +use std::ffi::OsString; +use std::marker::PhantomData; -use clap::{ArgMatches, CommandFactory, FromArgMatches}; -use futures::{future::BoxFuture, never::Never}; -use futures::{Future, FutureExt}; +use clap::{CommandFactory, FromArgMatches}; use imbl_value::Value; use reqwest::{Client, Method}; use serde::de::DeserializeOwned; use serde::Serialize; -use std::marker::PhantomData; use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}; use url::Url; use yajrc::{Id, RpcError}; -use crate::{command::ParentCommand, CliBindings, EmptyHandler, HandleArgs, Handler, NoParams}; -// use crate::command::{AsyncCommand, DynCommand, LeafCommand, ParentInfo}; -use crate::util::{combine, internal_error, invalid_params, parse_error}; -// use crate::{CliBindings, SyncCommand}; +use crate::util::{internal_error, parse_error, Flat}; +use crate::{ + AnyHandler, CliBindingsAny, DynHandler, HandleAny, HandleAnyArgs, HandleArgs, Handler, + IntoContext, Name, ParentHandler, +}; type GenericRpcMethod<'a> = yajrc::GenericRpcMethod<&'a str, Value, Value>; type RpcRequest<'a> = yajrc::RpcRequest>; type RpcResponse<'a> = yajrc::RpcResponse>; -// impl DynCommand { -// fn cli_app(&self) -> Option { -// if let Some(cli) = &self.cli { -// Some( -// cli.cmd -// .clone() -// .name(self.name) -// .subcommands(self.subcommands.iter().filter_map(|c| c.cli_app())), -// ) -// } else { -// None -// } -// } -// fn cmd_from_cli_matches( -// &self, -// matches: &ArgMatches, -// parent: ParentInfo, -// ) -> Result<(Vec<&'static str>, Value, &DynCommand), RpcError> { -// let params = combine( -// parent.params, -// (self -// .cli -// .as_ref() -// .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? -// .parser)(matches)?, -// )?; -// if let Some((cmd, matches)) = matches.subcommand() { -// let mut method = parent.method; -// method.push(self.name); -// self.subcommands -// .iter() -// .find(|c| c.name == cmd) -// .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? -// .cmd_from_cli_matches(matches, ParentInfo { method, params }) -// } else { -// Ok((parent.method, params, self)) -// } -// } -// } - -struct RootCliHandler( - PhantomData<(Context, Config)>, -); -impl Handler - for RootCliHandler +pub struct CliApp { + _phantom: PhantomData<(Context, Config)>, + make_ctx: Box Result + Send + Sync>, + root_handler: ParentHandler, +} +impl + CliApp { - type Params = NoParams; - type InheritedParams = NoParams; - type Ok = Never; - type Err = RpcError; - fn handle_sync(&self, _: HandleArgs) -> Result { - Err(yajrc::METHOD_NOT_FOUND_ERROR) - } -} -impl CliBindings - for RootCliHandler -{ - fn cli_command(&self) -> clap::Command { - Config::command() - } - - fn cli_parse( - &self, - matches: &ArgMatches, - ) -> Result<(std::collections::VecDeque<&'static str>, Value), clap::Error> { - } - - fn cli_display( - &self, - handle_args: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err> { - todo!() - } -} - -struct CliApp( - ParentCommand>, -); -impl CliApp { - pub fn new(commands: Vec>) -> Self { - Self { - cli: CliBindings::from_parent::(), - commands, - } - } - fn cmd_from_cli_matches( - &self, - matches: &ArgMatches, - ) -> Result<(Vec<&'static str>, Value, &DynCommand), RpcError> { - if let Some((cmd, matches)) = matches.subcommand() { - Ok(self - .commands - .iter() - .find(|c| c.name == cmd) - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .cmd_from_cli_matches( - matches, - ParentInfo { - method: Vec::new(), - params: Value::Object(Default::default()), - }, - )?) - } else { - Err(yajrc::METHOD_NOT_FOUND_ERROR) - } - } -} - -pub struct CliAppAsync { - app: CliApp, - make_ctx: Box BoxFuture<'static, Result> + Send>, -} -impl CliAppAsync { - pub fn new< - Cmd: FromArgMatches + CommandFactory + Serialize + DeserializeOwned + Send, - F: FnOnce(Cmd) -> Fut + Send + 'static, - Fut: Future> + Send, - >( - make_ctx: F, - commands: Vec>, + pub fn new Result + Send + Sync + 'static>( + make_ctx: MakeCtx, + root_handler: ParentHandler, ) -> Self { Self { - app: CliApp::new::(commands), - make_ctx: Box::new(|args| { - async { make_ctx(imbl_value::from_value(args).map_err(parse_error)?).await }.boxed() - }), + _phantom: PhantomData, + make_ctx: Box::new(make_ctx), + root_handler, } } -} -impl CliAppAsync { - pub async fn run(self, args: Vec) -> Result<(), RpcError> { - let cmd = self - .app - .cli - .cmd - .clone() - .subcommands(self.app.commands.iter().filter_map(|c| c.cli_app())); + pub fn run(self, args: impl IntoIterator) -> Result<(), RpcError> { + let ctx_ty = TypeId::of::(); + let mut cmd = Config::command(); + for (name, handlers) in &self.root_handler.subcommands.0 { + if let (Name(Some(name)), Some(DynHandler::WithCli(handler))) = ( + name, + if let Some(handler) = handlers.get(&Some(ctx_ty)) { + Some(handler) + } else if let Some(handler) = handlers.get(&None) { + Some(handler) + } else { + None + }, + ) { + cmd = cmd.subcommand(handler.cli_command(ctx_ty).name(name)); + } + } let matches = cmd.get_matches_from(args); - let make_ctx_args = (self.app.cli.parser)(&matches)?; - let ctx = (self.make_ctx)(make_ctx_args).await?; - let (parent_method, params, cmd) = self.app.cmd_from_cli_matches(&matches)?; - let display = &cmd - .cli - .as_ref() - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .display; - let res = (cmd - .implementation - .as_ref() - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .async_impl)(ctx.clone(), parent_method.clone(), params.clone()) - .await?; - if let Some(display) = display { - display(ctx, parent_method, params, res).map_err(parse_error) - } else { - Ok(()) - } - } -} - -pub struct CliAppSync { - app: CliApp, - make_ctx: Box Result + Send>, -} -impl CliAppSync { - pub fn new< - Cmd: FromArgMatches + CommandFactory + Serialize + DeserializeOwned + Send, - F: FnOnce(Cmd) -> Result + Send + 'static, - >( - make_ctx: F, - commands: Vec>, - ) -> Self { - Self { - app: CliApp::new::(commands), - make_ctx: Box::new(|args| make_ctx(imbl_value::from_value(args).map_err(parse_error)?)), - } - } -} -impl CliAppSync { - pub async fn run(self, args: Vec) -> Result<(), RpcError> { - let cmd = self - .app - .cli - .cmd - .clone() - .subcommands(self.app.commands.iter().filter_map(|c| c.cli_app())); - let matches = cmd.get_matches_from(args); - let make_ctx_args = (self.app.cli.parser)(&matches)?; - let ctx = (self.make_ctx)(make_ctx_args)?; - let (parent_method, params, cmd) = self.app.cmd_from_cli_matches(&matches)?; - let display = &cmd - .cli - .as_ref() - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .display; - let res = (cmd - .implementation - .as_ref() - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .sync_impl)(ctx.clone(), parent_method.clone(), params.clone())?; - if let Some(display) = display { - display(ctx, parent_method, params, res).map_err(parse_error) - } else { - Ok(()) - } + let config = Config::from_arg_matches(&matches)?; + let ctx = (self.make_ctx)(config)?; + let root_handler = AnyHandler::new(self.root_handler); + let (method, params) = root_handler.cli_parse(&matches, ctx_ty)?; + let res = root_handler.handle_sync(HandleAnyArgs { + context: ctx.clone().upcast(), + parent_method: Vec::new(), + method: method.clone(), + params: params.clone(), + })?; + root_handler.cli_display( + HandleAnyArgs { + context: ctx.upcast(), + parent_method: Vec::new(), + method, + params, + }, + res, + )?; + Ok(()) } } @@ -285,6 +135,15 @@ pub trait CliContextHttp: crate::Context { } } } +#[async_trait::async_trait] +impl CliContext for T +where + T: CliContextHttp, +{ + async fn call_remote(&self, method: &str, params: Value) -> Result { + ::call_remote(&self, method, params).await + } +} #[async_trait::async_trait] pub trait CliContextSocket: crate::Context { @@ -318,66 +177,55 @@ pub trait CliContextSocket: crate::Context { } } -pub trait RemoteCommand: LeafCommand {} +#[derive(Debug, Default)] +pub struct CallRemote(PhantomData<(RemoteContext, RemoteHandler)>); +impl CallRemote { + pub fn new() -> Self { + Self(PhantomData) + } +} +impl Clone for CallRemote { + fn clone(&self) -> Self { + Self(PhantomData) + } +} #[async_trait::async_trait] -impl AsyncCommand for T +impl Handler + for CallRemote where - T: RemoteCommand + Send + Serialize, - T::Parent: Serialize, - T::Ok: DeserializeOwned, - T::Err: From, - Context: CliContext + Send + 'static, + RemoteContext: IntoContext, + RemoteHandler: Handler, + RemoteHandler::Params: Serialize, + RemoteHandler::InheritedParams: Serialize, + RemoteHandler::Ok: DeserializeOwned, + RemoteHandler::Err: From, { - async fn implementation( - self, - ctx: Context, - parent: ParentInfo, + type Params = RemoteHandler::Params; + type InheritedParams = RemoteHandler::InheritedParams; + type Ok = RemoteHandler::Ok; + type Err = RemoteHandler::Err; + async fn handle_async( + &self, + handle_args: HandleArgs, ) -> Result { - let mut method = parent.method; - method.push(Self::NAME); - Ok(imbl_value::from_value( - ctx.call_remote( - &method.join("."), - combine( - imbl_value::to_value(&self).map_err(invalid_params)?, - imbl_value::to_value(&parent.params).map_err(invalid_params)?, - ) - .map_err(invalid_params)?, + let full_method = handle_args + .parent_method + .into_iter() + .chain(handle_args.method) + .collect::>(); + match handle_args + .context + .call_remote( + &full_method.join("."), + imbl_value::to_value(&Flat(handle_args.params, handle_args.inherited_params)) + .map_err(parse_error)?, ) - .await?, - ) - .map_err(parse_error)?) - } -} - -impl SyncCommand for T -where - T: RemoteCommand + Send + Serialize, - T::Parent: Serialize, - T::Ok: DeserializeOwned, - T::Err: From, - Context: CliContext + Send + 'static, -{ - const BLOCKING: bool = true; - fn implementation( - self, - ctx: Context, - parent: ParentInfo, - ) -> Result { - let mut method = parent.method; - method.push(Self::NAME); - Ok(imbl_value::from_value( - ctx.runtime().block_on( - ctx.call_remote( - &method.join("."), - combine( - imbl_value::to_value(&self).map_err(invalid_params)?, - imbl_value::to_value(&parent.params).map_err(invalid_params)?, - ) - .map_err(invalid_params)?, - ), - )?, - ) - .map_err(parse_error)?) + .await + { + Ok(a) => imbl_value::from_value(a) + .map_err(internal_error) + .map_err(Self::Err::from), + Err(e) => Err(Self::Err::from(e)), + } } } diff --git a/rpc-toolkit/src/context.rs b/rpc-toolkit/src/context.rs index 79720f8..f2c0e43 100644 --- a/rpc-toolkit/src/context.rs +++ b/rpc-toolkit/src/context.rs @@ -5,14 +5,15 @@ use tokio::runtime::Handle; use crate::Handler; -pub trait Context: Any + Send + 'static { +pub trait Context: Any + Send + Sync + 'static { fn runtime(&self) -> Handle { Handle::current() } } #[allow(private_bounds)] -pub trait IntoContext: sealed::Sealed + Any + Send + Sized + 'static { +pub trait IntoContext: sealed::Sealed + Any + Send + Sync + Sized + 'static { + fn runtime(&self) -> Handle; fn type_ids_for + ?Sized>(handler: &H) -> Option>; fn inner_type_id(&self) -> TypeId; fn upcast(self) -> AnyContext; @@ -20,7 +21,10 @@ pub trait IntoContext: sealed::Sealed + Any + Send + Sized + 'static { } impl IntoContext for C { - fn type_ids_for + ?Sized>(handler: &H) -> Option> { + fn runtime(&self) -> Handle { + ::runtime(&self) + } + fn type_ids_for + ?Sized>(_: &H) -> Option> { let mut set = BTreeSet::new(); set.insert(TypeId::of::()); Some(set) @@ -45,7 +49,13 @@ pub enum EitherContext { C2(C2), } impl IntoContext for EitherContext { - fn type_ids_for + ?Sized>(handler: &H) -> Option> { + fn runtime(&self) -> Handle { + match self { + Self::C1(a) => a.runtime(), + Self::C2(a) => a.runtime(), + } + } + fn type_ids_for + ?Sized>(_: &H) -> Option> { let mut set = BTreeSet::new(); set.insert(TypeId::of::()); set.insert(TypeId::of::()); @@ -88,6 +98,9 @@ impl AnyContext { } impl IntoContext for AnyContext { + fn runtime(&self) -> Handle { + self.0.runtime() + } fn type_ids_for + ?Sized>(_: &H) -> Option> { None } diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index ab1c498..b9e86f1 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -1,9 +1,11 @@ use std::any::TypeId; use std::collections::{BTreeMap, BTreeSet, VecDeque}; use std::marker::PhantomData; +use std::ops::Deref; use std::sync::Arc; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser}; +use futures::Future; use imbl_value::Value; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -12,11 +14,11 @@ use yajrc::RpcError; use crate::context::{AnyContext, IntoContext}; use crate::util::{combine, internal_error, invalid_params, Flat}; -struct HandleAnyArgs { - context: AnyContext, - parent_method: Vec<&'static str>, - method: VecDeque<&'static str>, - params: Value, +pub(crate) struct HandleAnyArgs { + pub(crate) context: AnyContext, + pub(crate) parent_method: Vec<&'static str>, + pub(crate) method: VecDeque<&'static str>, + pub(crate) params: Value, } impl HandleAnyArgs { fn downcast(self) -> Result, imbl_value::Error> @@ -40,17 +42,31 @@ impl HandleAnyArgs { method, params: imbl_value::from_value(params.clone())?, inherited_params: imbl_value::from_value(params.clone())?, + raw_params: params, }) } } #[async_trait::async_trait] -trait HandleAny { +pub(crate) trait HandleAny: Send + Sync { fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; - // async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result; + async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result; + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option>; +} +#[async_trait::async_trait] +impl HandleAny for Arc { + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + self.deref().handle_sync(handle_args) + } + async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { + self.deref().handle_async(handle_args).await + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.deref().method_from_dots(method, ctx_ty) + } } -trait CliBindingsAny { +pub(crate) trait CliBindingsAny { fn cli_command(&self, ctx_ty: TypeId) -> Command; fn cli_parse( &self, @@ -97,11 +113,21 @@ pub trait PrintCliResult: Handler { // } // } +#[derive(Debug)] struct WithCliBindings { _ctx: PhantomData, handler: H, } +impl Clone for WithCliBindings { + fn clone(&self) -> Self { + Self { + _ctx: PhantomData, + handler: self.handler.clone(), + } + } +} +#[async_trait::async_trait] impl Handler for WithCliBindings where Context: IntoContext, @@ -119,6 +145,7 @@ where method, params, inherited_params, + raw_params, }: HandleArgs, ) -> Result { self.handler.handle_sync(HandleArgs { @@ -127,8 +154,31 @@ where method, params, inherited_params, + raw_params, }) } + async fn handle_async( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.handler + .handle_async(HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + .await + } } impl CliBindings for WithCliBindings @@ -162,6 +212,7 @@ where method, params, inherited_params, + raw_params, }: HandleArgs, result: Self::Ok, ) -> Result<(), Self::Err> { @@ -172,20 +223,22 @@ where method, params, inherited_params, + raw_params, }, result, ) } } -trait HandleAnyWithCli: HandleAny + CliBindingsAny {} +pub(crate) trait HandleAnyWithCli: HandleAny + CliBindingsAny {} impl HandleAnyWithCli for T {} #[derive(Clone)] -enum DynHandler { +pub(crate) enum DynHandler { WithoutCli(Arc), WithCli(Arc), } +#[async_trait::async_trait] impl HandleAny for DynHandler { fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { match self { @@ -193,32 +246,91 @@ impl HandleAny for DynHandler { DynHandler::WithCli(h) => h.handle_sync(handle_args), } } -} - -pub struct HandleArgs + ?Sized> { - context: Context, - parent_method: Vec<&'static str>, - method: VecDeque<&'static str>, - params: H::Params, - inherited_params: H::InheritedParams, -} - -pub trait Handler { - type Params; - type InheritedParams; - type Ok; - type Err; - fn handle_sync(&self, handle_args: HandleArgs) -> Result; - fn contexts(&self) -> Option> { - Context::type_ids_for(self) + async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { + match self { + DynHandler::WithoutCli(h) => h.handle_async(handle_args).await, + DynHandler::WithCli(h) => h.handle_async(handle_args).await, + } + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + match self { + DynHandler::WithoutCli(h) => h.method_from_dots(method, ctx_ty), + DynHandler::WithCli(h) => h.method_from_dots(method, ctx_ty), + } } } -struct AnyHandler { +#[derive(Debug, Clone)] +pub struct HandleArgs + ?Sized> { + pub context: Context, + pub parent_method: Vec<&'static str>, + pub method: VecDeque<&'static str>, + pub params: H::Params, + pub inherited_params: H::InheritedParams, + pub raw_params: Value, +} + +#[async_trait::async_trait] +pub trait Handler: Clone + Send + Sync + 'static { + type Params: Send + Sync; + type InheritedParams: Send + Sync; + type Ok: Send + Sync; + type Err: Send + Sync; + fn handle_sync(&self, handle_args: HandleArgs) -> Result { + handle_args + .context + .runtime() + .block_on(self.handle_async(handle_args)) + } + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result; + async fn handle_async_with_sync( + &self, + handle_args: HandleArgs, + ) -> Result { + self.handle_sync(handle_args) + } + async fn handle_async_with_sync_blocking( + &self, + handle_args: HandleArgs, + ) -> Result { + let s = self.clone(); + handle_args + .context + .runtime() + .spawn_blocking(move || s.handle_sync(handle_args)) + .await + .unwrap() + } + fn contexts(&self) -> Option> { + Context::type_ids_for(self) + } + #[allow(unused_variables)] + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + if method.is_empty() { + Some(VecDeque::new()) + } else { + None + } + } +} + +pub(crate) struct AnyHandler { _ctx: PhantomData, handler: H, } +impl AnyHandler { + pub(crate) fn new(handler: H) -> Self { + Self { + _ctx: PhantomData, + handler, + } + } +} +#[async_trait::async_trait] impl> HandleAny for AnyHandler where H::Params: DeserializeOwned, @@ -234,11 +346,24 @@ where ) .map_err(internal_error) } + async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { + imbl_value::to_value( + &self + .handler + .handle_async(handle_args.downcast().map_err(invalid_params)?) + .await?, + ) + .map_err(internal_error) + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.handler.method_from_dots(method, ctx_ty) + } } impl> CliBindingsAny for AnyHandler where - H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, + H: CliBindings, + H::Params: DeserializeOwned, H::InheritedParams: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, @@ -270,14 +395,15 @@ pub struct NoParams {} pub enum Never {} #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -struct Name(Option<&'static str>); +pub(crate) struct Name(pub(crate) Option<&'static str>); impl<'a> std::borrow::Borrow> for Name { fn borrow(&self) -> &Option<&'a str> { &self.0 } } -struct SubcommandMap(BTreeMap, DynHandler>>); +#[derive(Clone)] +pub(crate) struct SubcommandMap(pub(crate) BTreeMap, DynHandler>>); impl SubcommandMap { fn insert( &mut self, @@ -311,7 +437,7 @@ impl SubcommandMap { pub struct ParentHandler { _phantom: PhantomData<(Params, InheritedParams)>, - subcommands: SubcommandMap, + pub(crate) subcommands: SubcommandMap, } impl ParentHandler { pub fn new() -> Self { @@ -321,24 +447,40 @@ impl ParentHandler { } } } +impl Clone for ParentHandler { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + subcommands: self.subcommands.clone(), + } + } +} -struct InheritanceHandler< - Context: IntoContext, - Params, - InheritedParams, - H: Handler, - F: Fn(Params, InheritedParams) -> H::InheritedParams, -> { +struct InheritanceHandler { _phantom: PhantomData<(Context, Params, InheritedParams)>, handler: H, inherit: F, } +impl Clone + for InheritanceHandler +{ + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + handler: self.handler.clone(), + inherit: self.inherit.clone(), + } + } +} +#[async_trait::async_trait] impl Handler for InheritanceHandler where Context: IntoContext, + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, H: Handler, - F: Fn(Params, InheritedParams) -> H::InheritedParams, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { type Params = H::Params; type InheritedParams = Flat; @@ -352,6 +494,7 @@ where method, params, inherited_params, + raw_params, }: HandleArgs, ) -> Result { self.handler.handle_sync(HandleArgs { @@ -360,6 +503,27 @@ where method, params, inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, + }) + } + async fn handle_async( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.handler.handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, }) } } @@ -368,8 +532,10 @@ impl PrintCliResult for InheritanceHandler where Context: IntoContext, + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, H: Handler + PrintCliResult, - F: Fn(Params, InheritedParams) -> H::InheritedParams, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { fn print( &self, @@ -379,6 +545,7 @@ where method, params, inherited_params, + raw_params, }: HandleArgs, result: Self::Ok, ) -> Result<(), Self::Err> { @@ -389,13 +556,14 @@ where method, params, inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, }, result, ) } } -impl ParentHandler { +impl ParentHandler { pub fn subcommand(mut self, name: &'static str, handler: H) -> Self where Context: IntoContext, @@ -436,7 +604,7 @@ impl ParentHandler { self } } -impl ParentHandler +impl ParentHandler where Params: DeserializeOwned + 'static, InheritedParams: DeserializeOwned + 'static, @@ -453,7 +621,7 @@ where H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, - F: Fn(Params, InheritedParams) -> H::InheritedParams + 'static, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { self.subcommands.insert( handler.contexts(), @@ -484,7 +652,7 @@ where H::Params: DeserializeOwned, H::Ok: Serialize, RpcError: From, - F: Fn(Params, InheritedParams) -> H::InheritedParams + 'static, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { self.subcommands.insert( handler.contexts(), @@ -507,7 +675,7 @@ where H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, - F: Fn(Params, InheritedParams) -> H::InheritedParams + 'static, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { self.subcommands.insert( handler.contexts(), @@ -533,7 +701,7 @@ where H::Params: DeserializeOwned, H::Ok: Serialize, RpcError: From, - F: Fn(Params, InheritedParams) -> H::InheritedParams + 'static, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { self.subcommands.insert( handler.contexts(), @@ -551,8 +719,12 @@ where } } -impl Handler - for ParentHandler +#[async_trait::async_trait] +impl< + Context: IntoContext, + Params: Serialize + Send + Sync + 'static, + InheritedParams: Serialize + Send + Sync + 'static, + > Handler for ParentHandler { type Params = Params; type InheritedParams = InheritedParams; @@ -564,9 +736,9 @@ impl Handler context, mut parent_method, mut method, - params, - inherited_params, - }: HandleArgs, + raw_params, + .. + }: HandleArgs, ) -> Result { let cmd = method.pop_front(); if let Some(cmd) = cmd { @@ -577,13 +749,39 @@ impl Handler context: context.upcast(), parent_method, method, - params: imbl_value::to_value(&Flat(params, inherited_params)) - .map_err(invalid_params)?, + params: raw_params, }) } else { Err(yajrc::METHOD_NOT_FOUND_ERROR) } } + async fn handle_async( + &self, + HandleArgs { + context, + mut parent_method, + mut method, + raw_params, + .. + }: HandleArgs, + ) -> Result { + let cmd = method.pop_front(); + if let Some(cmd) = cmd { + parent_method.push(cmd); + } + if let Some((_, sub_handler)) = self.subcommands.get(context.inner_type_id(), cmd) { + sub_handler + .handle_async(HandleAnyArgs { + context: context.upcast(), + parent_method, + method, + params: raw_params, + }) + .await + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } fn contexts(&self) -> Option> { let mut set = BTreeSet::new(); for ctx_ty in self.subcommands.0.values().flat_map(|c| c.keys()) { @@ -591,12 +789,31 @@ impl Handler } Some(set) } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + let (head, tail) = if method.is_empty() { + (None, None) + } else { + method + .split_once(".") + .map(|(head, tail)| (Some(head), Some(tail))) + .unwrap_or((Some(method), None)) + }; + let (Name(name), h) = self.subcommands.get(ctx_ty, head)?; + let mut res = VecDeque::new(); + if let Some(name) = name { + res.push_back(name); + } + if let Some(tail) = tail { + res.append(&mut h.method_from_dots(tail, ctx_ty)?); + } + Some(res) + } } impl CliBindings for ParentHandler where - Params: FromArgMatches + CommandFactory + Serialize, - InheritedParams: Serialize, + Params: FromArgMatches + CommandFactory + Serialize + Send + Sync + 'static, + InheritedParams: Serialize + Send + Sync + 'static, { fn cli_command(&self, ctx_ty: TypeId) -> Command { let mut base = Params::command(); @@ -648,8 +865,8 @@ where context, mut parent_method, mut method, - params, - inherited_params, + raw_params, + .. }: HandleArgs, result: Self::Ok, ) -> Result<(), Self::Err> { @@ -665,8 +882,7 @@ where context, parent_method, method, - params: imbl_value::to_value(&Flat(params, inherited_params)) - .map_err(invalid_params)?, + params: raw_params, }, result, ) @@ -679,19 +895,61 @@ where pub struct FromFn { _phantom: PhantomData<(T, E, Args)>, function: F, + blocking: bool, +} +impl Clone for FromFn { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + function: self.function.clone(), + blocking: self.blocking, + } + } } pub fn from_fn(function: F) -> FromFn { FromFn { function, _phantom: PhantomData, + blocking: false, } } +pub fn from_fn_blocking(function: F) -> FromFn { + FromFn { + function, + _phantom: PhantomData, + blocking: true, + } +} + +pub struct FromFnAsync { + _phantom: PhantomData<(Fut, T, E, Args)>, + function: F, +} +impl Clone for FromFnAsync { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + function: self.function.clone(), + } + } +} + +pub fn from_fn_async(function: F) -> FromFnAsync { + FromFnAsync { + function, + _phantom: PhantomData, + } +} + +#[async_trait::async_trait] impl Handler for FromFn where Context: IntoContext, - F: Fn() -> Result, + F: Fn() -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, { type Params = NoParams; type InheritedParams = NoParams; @@ -700,12 +958,42 @@ where fn handle_sync(&self, _: HandleArgs) -> Result { (self.function)() } + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + if self.blocking { + self.handle_async_with_sync_blocking(handle_args).await + } else { + self.handle_async_with_sync(handle_args).await + } + } +} +#[async_trait::async_trait] +impl Handler for FromFnAsync +where + Context: IntoContext, + F: Fn() -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = NoParams; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; + async fn handle_async(&self, _: HandleArgs) -> Result { + (self.function)().await + } } +#[async_trait::async_trait] impl Handler for FromFn where Context: IntoContext, - F: Fn(Context) -> Result, + F: Fn(Context) -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, { type Params = NoParams; type InheritedParams = NoParams; @@ -714,12 +1002,46 @@ where fn handle_sync(&self, handle_args: HandleArgs) -> Result { (self.function)(handle_args.context) } + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + if self.blocking { + self.handle_async_with_sync_blocking(handle_args).await + } else { + self.handle_async_with_sync(handle_args).await + } + } } +#[async_trait::async_trait] +impl Handler for FromFnAsync +where + Context: IntoContext, + F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = NoParams; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + (self.function)(handle_args.context).await + } +} + +#[async_trait::async_trait] impl Handler for FromFn where Context: IntoContext, - F: Fn(Context, Params) -> Result, - Params: DeserializeOwned, + F: Fn(Context, Params) -> Result + Send + Sync + Clone + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, { type Params = Params; type InheritedParams = NoParams; @@ -731,14 +1053,53 @@ where } = handle_args; (self.function)(context, params) } + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + if self.blocking { + self.handle_async_with_sync_blocking(handle_args).await + } else { + self.handle_async_with_sync(handle_args).await + } + } } +#[async_trait::async_trait] +impl Handler + for FromFnAsync +where + Context: IntoContext, + F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Params; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + let HandleArgs { + context, params, .. + } = handle_args; + (self.function)(context, params).await + } +} + +#[async_trait::async_trait] impl Handler for FromFn where Context: IntoContext, - F: Fn(Context, Params, InheritedParams) -> Result, - Params: DeserializeOwned, - InheritedParams: DeserializeOwned, + F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, { type Params = Params; type InheritedParams = InheritedParams; @@ -753,44 +1114,43 @@ where } = handle_args; (self.function)(context, params, inherited_params) } + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + if self.blocking { + self.handle_async_with_sync_blocking(handle_args).await + } else { + self.handle_async_with_sync(handle_args).await + } + } +} +#[async_trait::async_trait] +impl Handler + for FromFnAsync +where + Context: IntoContext, + F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = T; + type Err = E; + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + let HandleArgs { + context, + params, + inherited_params, + .. + } = handle_args; + (self.function)(context, params, inherited_params).await + } } - -#[derive(Parser)] -#[command(about = "this is db stuff")] -struct DbParams {} - -// Server::new( -// ParentCommand::new() -// .subcommand("foo", from_fn(foo)) -// .subcommand("db", -// ParentCommand::new::() -// .subcommand("dump", from_fn(dump)) -// ) -// ) - -// Server::new() -// .handle( -// "db", -// with_description("Description maybe?") -// .handle("dump", from_fn(dump_route)) -// ) -// .handle( -// "server", -// no_description() -// .handle("version", from_fn(version)) -// ) - -// #[derive(clap::Parser)] -// struct DumpParams { -// test: Option -// } - -// fn dump_route(context: Context, param: Param) -> Result { -// Ok(json!({ -// "db": {} -// })) -// } - -// fn version() -> &'static str { -// "1.0.0" -// } diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index fec86b5..bcbf617 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -1,4 +1,4 @@ -// pub use cli::*; +pub use cli::*; // pub use command::*; pub use context::*; pub use handler::*; @@ -24,15 +24,11 @@ pub use handler::*; /// /// See also: [arg](rpc_toolkit_macro::arg), [context](rpc_toolkit_macro::context) pub use rpc_toolkit_macro::command; -// pub use server::*; +pub use server::*; pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc}; -// mod cli; -// mod command; -mod handler; -// pub mod command_helpers; +mod cli; mod context; -// mod metadata; -// pub mod rpc_server_helpers; -// mod server; +mod handler; +mod server; mod util; diff --git a/rpc-toolkit/src/server/http.rs b/rpc-toolkit/src/server/http.rs index 29bdee6..ab40aeb 100644 --- a/rpc-toolkit/src/server/http.rs +++ b/rpc-toolkit/src/server/http.rs @@ -19,7 +19,7 @@ pub trait Middleware { async fn process_rpc_request( &self, prev: Self::ProcessHttpRequestResult, - metadata: &Context::Metadata, + // metadata: &Context::Metadata, req: &mut RpcRequest, ) -> Result; type ProcessRpcResponseResult; diff --git a/rpc-toolkit/src/server/mod.rs b/rpc-toolkit/src/server/mod.rs index 2ee1ca1..5920717 100644 --- a/rpc-toolkit/src/server/mod.rs +++ b/rpc-toolkit/src/server/mod.rs @@ -1,13 +1,13 @@ -use std::borrow::Cow; +use std::any::TypeId; use std::sync::Arc; use futures::future::{join_all, BoxFuture}; use futures::{Future, FutureExt, Stream, StreamExt}; use imbl_value::Value; -use yajrc::{AnyParams, AnyRpcMethod, RpcError, RpcMethod}; +use yajrc::{RpcError, RpcMethod}; use crate::util::{invalid_request, JobRunner}; -use crate::DynCommand; +use crate::{AnyHandler, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; type GenericRpcMethod = yajrc::GenericRpcMethod; type RpcRequest = yajrc::RpcRequest; @@ -20,41 +20,21 @@ mod socket; pub use http::*; pub use socket::*; -impl DynCommand { - fn cmd_from_method( - &self, - method: &[&str], - parent_method: Vec<&'static str>, - ) -> Result<(Vec<&'static str>, &DynCommand), RpcError> { - let mut ret_method = parent_method; - ret_method.push(self.name); - if let Some((cmd, rest)) = method.split_first() { - self.subcommands - .iter() - .find(|c| c.name == *cmd) - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .cmd_from_method(rest, ret_method) - } else { - Ok((ret_method, self)) - } - } -} - pub struct Server { - commands: Vec>, make_ctx: Arc BoxFuture<'static, Result> + Send + Sync>, + root_handler: Arc>, } impl Server { pub fn new< - F: Fn() -> Fut + Send + Sync + 'static, + MakeCtx: Fn() -> Fut + Send + Sync + 'static, Fut: Future> + Send + 'static, >( - commands: Vec>, - make_ctx: F, + make_ctx: MakeCtx, + root_handler: ParentHandler, ) -> Self { Server { - commands, make_ctx: Arc::new(move || make_ctx().boxed()), + root_handler: Arc::new(AnyHandler::new(root_handler)), } } @@ -63,30 +43,22 @@ impl Server { method: &str, params: Value, ) -> impl Future> + Send + 'static { - let from_self = (|| { - let method: Vec<_> = method.split(".").collect(); - let (cmd, rest) = method.split_first().ok_or(yajrc::METHOD_NOT_FOUND_ERROR)?; - let (method, cmd) = self - .commands - .iter() - .find(|c| c.name == *cmd) - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .cmd_from_method(rest, Vec::new())?; - Ok::<_, RpcError>(( - cmd.implementation - .as_ref() - .ok_or(yajrc::METHOD_NOT_FOUND_ERROR)? - .async_impl - .clone(), - self.make_ctx.clone(), - method, - params, - )) - })(); + let (make_ctx, root_handler, method) = ( + self.make_ctx.clone(), + self.root_handler.clone(), + self.root_handler + .method_from_dots(method, TypeId::of::()), + ); async move { - let (implementation, make_ctx, method, params) = from_self?; - implementation(make_ctx().await?, method, params).await + root_handler + .handle_async(HandleAnyArgs { + context: make_ctx().await?.upcast(), + parent_method: Vec::new(), + method: method.ok_or_else(|| yajrc::METHOD_NOT_FOUND_ERROR)?, + params, + }) + .await } } diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index 5b0eaeb..bac9e77 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -69,10 +69,8 @@ impl Context for CliContext { // } fn make_api() -> ParentHandler { - ParentHandler::new().subcommand_no_cli( - Some("hello"), - from_fn(|_: CliContext| Ok::<_, RpcError>("world")), - ) + ParentHandler::new() + .subcommand_no_cli("hello", from_fn(|_: CliContext| Ok::<_, RpcError>("world"))) } pub fn internal_error(e: impl Display) -> RpcError { From 6acf4204a5a6bfc22347ce08df86034c099f6e72 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 22 Dec 2023 12:36:33 -0700 Subject: [PATCH 13/79] fix implementation --- rpc-toolkit/src/handler.rs | 36 +++++++++++++++++++++--------------- rpc-toolkit/tests/handler.rs | 34 ++++++++++++++++++---------------- 2 files changed, 39 insertions(+), 31 deletions(-) diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index b9e86f1..7187348 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -1,5 +1,6 @@ use std::any::TypeId; use std::collections::{BTreeMap, BTreeSet, VecDeque}; +use std::fmt::Display; use std::marker::PhantomData; use std::ops::Deref; use std::sync::Arc; @@ -98,21 +99,6 @@ pub trait PrintCliResult: Handler { ) -> Result<(), Self::Err>; } -// impl PrintCliResult for H -// where -// Context: IntoContext, -// H: Handler, -// H::Ok: Display, -// { -// fn print( -// &self, -// handle_args: HandleArgs, -// result: Self::Ok, -// ) -> Result<(), Self::Err> { -// Ok(println!("{result}")) -// } -// } - #[derive(Debug)] struct WithCliBindings { _ctx: PhantomData, @@ -906,6 +892,16 @@ impl Clone for FromFn { } } } +impl PrintCliResult for FromFn +where + Context: IntoContext, + Self: Handler, + >::Ok: Display, +{ + fn print(&self, _: HandleArgs, result: Self::Ok) -> Result<(), Self::Err> { + Ok(println!("{result}")) + } +} pub fn from_fn(function: F) -> FromFn { FromFn { @@ -935,6 +931,16 @@ impl Clone for FromFnAsync { } } } +impl PrintCliResult for FromFnAsync +where + Context: IntoContext, + Self: Handler, + >::Ok: Display, +{ + fn print(&self, _: HandleArgs, result: Self::Ok) -> Result<(), Self::Err> { + Ok(println!("{result}")) + } +} pub fn from_fn_async(function: F) -> FromFnAsync { FromFnAsync { diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index bac9e77..70025af 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use std::sync::Arc; use clap::Parser; -use rpc_toolkit::{from_fn, Context, ParentHandler}; +use rpc_toolkit::{from_fn, AnyContext, CliApp, Context, ParentHandler}; use serde::Deserialize; use tokio::runtime::{Handle, Runtime}; use tokio::sync::OnceCell; @@ -54,23 +54,25 @@ impl Context for CliContext { } } -// fn make_cli() -> CliApp { -// CliApp::new::<_, CliConfig>(|mut config| { -// config.load_rec()?; -// Ok(CliContext(Arc::new(CliContextSeed { -// host: config -// .host -// .unwrap_or_else("http://localhost:8080/rpc".parse().unwrap()), -// rt: OnceCell::new(), -// }))) -// }) -// .subcommands(make_api()) -// .subcommands(ParentHandler::new().subcommand("hello", from_fn(|| Ok("world")))); -// } +fn make_cli() -> CliApp { + CliApp::new( + |mut config: CliConfig| { + config.load_rec()?; + Ok(CliContext(Arc::new(CliContextSeed { + host: config + .host + .map(|h| h.parse().unwrap()) + .unwrap_or_else(|| "http://localhost:8080/rpc".parse().unwrap()), + rt: OnceCell::new(), + }))) + }, + make_api(), + ) +} -fn make_api() -> ParentHandler { +fn make_api() -> ParentHandler { ParentHandler::new() - .subcommand_no_cli("hello", from_fn(|_: CliContext| Ok::<_, RpcError>("world"))) + .subcommand::("hello", from_fn(|| Ok::<_, RpcError>("world".to_owned()))) } pub fn internal_error(e: impl Display) -> RpcError { From d5e81b760d7155b47d5b940f353f7958daf14042 Mon Sep 17 00:00:00 2001 From: J H Date: Fri, 22 Dec 2023 12:56:09 -0700 Subject: [PATCH 14/79] wip --- rpc-toolkit/tests/handler.rs | 44 +++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index 70025af..b45937c 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -3,8 +3,8 @@ use std::path::PathBuf; use std::sync::Arc; use clap::Parser; -use rpc_toolkit::{from_fn, AnyContext, CliApp, Context, ParentHandler}; -use serde::Deserialize; +use rpc_toolkit::{from_fn, from_fn_async, AnyContext, CliApp, Context, ParentHandler}; +use serde::{Deserialize, Serialize}; use tokio::runtime::{Handle, Runtime}; use tokio::sync::OnceCell; use url::Url; @@ -71,8 +71,46 @@ fn make_cli() -> CliApp { } fn make_api() -> ParentHandler { + impl CliContext { + fn host(&self) -> &Url { + &self.0.host + } + } + async fn a_hello(_: CliContext) -> Result { + Ok::<_, RpcError>("Async Subcommand".to_string()) + } + #[derive(Debug, Clone, Deserialize, Serialize, Parser)] + struct HelloParams { + whom: String, + } ParentHandler::new() - .subcommand::("hello", from_fn(|| Ok::<_, RpcError>("world".to_owned()))) + .subcommand( + "echo", + ParentHandler::new() + .subcommand_no_cli( + "echo_no_cli", + from_fn(|c: CliContext| { + Ok::<_, RpcError>( + format!("Subcommand No Cli: Host {host}", host = c.host()).to_string(), + ) + }), + ) + .subcommand_no_cli( + "echo_cli", + from_fn(|c: CliContext| { + Ok::<_, RpcError>( + format!("Subcommand Cli: Host {host}", host = c.host()).to_string(), + ) + }), + ), + ) + .subcommand( + "hello", + from_fn(|_: CliContext, HelloParams { whom }: HelloParams| { + Ok::<_, RpcError>(format!("Hello {whom}").to_string()) + }), + ) + .subcommand("a_hello", from_fn_async(a_hello)) } pub fn internal_error(e: impl Display) -> RpcError { From df86610153288b1623a2935bf9d0fae9e39ae5d2 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 22 Dec 2023 13:19:07 -0700 Subject: [PATCH 15/79] fix cli binding inference --- rpc-toolkit/src/handler.rs | 272 ++++++++++++++++------------------- rpc-toolkit/tests/handler.rs | 2 +- 2 files changed, 128 insertions(+), 146 deletions(-) diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index 7187348..e93846a 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -99,123 +99,6 @@ pub trait PrintCliResult: Handler { ) -> Result<(), Self::Err>; } -#[derive(Debug)] -struct WithCliBindings { - _ctx: PhantomData, - handler: H, -} -impl Clone for WithCliBindings { - fn clone(&self) -> Self { - Self { - _ctx: PhantomData, - handler: self.handler.clone(), - } - } -} - -#[async_trait::async_trait] -impl Handler for WithCliBindings -where - Context: IntoContext, - H: Handler, -{ - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; - fn handle_sync( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - ) -> Result { - self.handler.handle_sync(HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - } - async fn handle_async( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - ) -> Result { - self.handler - .handle_async(HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - .await - } -} - -impl CliBindings for WithCliBindings -where - Context: IntoContext, - H: Handler, - H::Params: FromArgMatches + CommandFactory + Serialize, - H: PrintCliResult, -{ - fn cli_command(&self, _: TypeId) -> Command { - H::Params::command() - } - fn cli_parse( - &self, - matches: &ArgMatches, - _: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - H::Params::from_arg_matches(matches).and_then(|a| { - Ok(( - VecDeque::new(), - imbl_value::to_value(&a) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, - )) - }) - } - fn cli_display( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.handler.print( - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }, - result, - ) - } -} - pub(crate) trait HandleAnyWithCli: HandleAny + CliBindingsAny {} impl HandleAnyWithCli for T {} @@ -514,16 +397,26 @@ where } } -impl PrintCliResult +impl CliBindings for InheritanceHandler where Context: IntoContext, Params: Send + Sync + 'static, InheritedParams: Send + Sync + 'static, - H: Handler + PrintCliResult, + H: CliBindings, F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { - fn print( + fn cli_command(&self, ctx_ty: TypeId) -> Command { + self.handler.cli_command(ctx_ty) + } + fn cli_parse( + &self, + matches: &ArgMatches, + ctx_ty: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + self.handler.cli_parse(matches, ctx_ty) + } + fn cli_display( &self, HandleArgs { context, @@ -535,7 +428,7 @@ where }: HandleArgs, result: Self::Ok, ) -> Result<(), Self::Err> { - self.handler.print( + self.handler.cli_display( HandleArgs { context, parent_method, @@ -553,8 +446,8 @@ impl ParentHandler(mut self, name: &'static str, handler: H) -> Self where Context: IntoContext, - H: Handler + PrintCliResult + 'static, - H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, + H: CliBindings + 'static, + H::Params: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, { @@ -563,10 +456,7 @@ impl ParentHandler Self where Context: IntoContext, - H: Handler + PrintCliResult + 'static, - H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, + H: CliBindings + 'static, + H::Params: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, @@ -614,13 +504,10 @@ where name.into(), DynHandler::WithCli(Arc::new(AnyHandler { _ctx: PhantomData, - handler: WithCliBindings { - _ctx: PhantomData, - handler: InheritanceHandler:: { - _phantom: PhantomData, - handler, - inherit, - }, + handler: InheritanceHandler:: { + _phantom: PhantomData, + handler, + inherit, }, })), ); @@ -657,8 +544,8 @@ where pub fn root_handler(mut self, handler: H, inherit: F) -> Self where Context: IntoContext, - H: Handler + PrintCliResult + 'static, - H::Params: FromArgMatches + CommandFactory + Serialize + DeserializeOwned, + H: CliBindings + 'static, + H::Params: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, @@ -668,13 +555,10 @@ where None, DynHandler::WithCli(Arc::new(AnyHandler { _ctx: PhantomData, - handler: WithCliBindings { - _ctx: PhantomData, - handler: InheritanceHandler:: { - _phantom: PhantomData, - handler, - inherit, - }, + handler: InheritanceHandler:: { + _phantom: PhantomData, + handler, + inherit, }, })), ); @@ -1160,3 +1044,101 @@ where (self.function)(context, params, inherited_params).await } } + +impl CliBindings for FromFn +where + Context: IntoContext, + Self: Handler, + Self::Params: FromArgMatches + CommandFactory + Serialize, + Self: PrintCliResult, +{ + fn cli_command(&self, _: TypeId) -> Command { + Self::Params::command() + } + fn cli_parse( + &self, + matches: &ArgMatches, + _: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.print( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) + } +} + +impl CliBindings for FromFnAsync +where + Context: IntoContext, + Self: Handler, + Self::Params: FromArgMatches + CommandFactory + Serialize, + Self: PrintCliResult, +{ + fn cli_command(&self, _: TypeId) -> Command { + Self::Params::command() + } + fn cli_parse( + &self, + matches: &ArgMatches, + _: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.print( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) + } +} diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index b45937c..6621f36 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use std::sync::Arc; use clap::Parser; -use rpc_toolkit::{from_fn, from_fn_async, AnyContext, CliApp, Context, ParentHandler}; +use rpc_toolkit::{from_fn, from_fn_async, AnyContext, CliApp, Context, NoParams, ParentHandler}; use serde::{Deserialize, Serialize}; use tokio::runtime::{Handle, Runtime}; use tokio::sync::OnceCell; From 6f3e964907f8289ddd4652f1b913e4735b41b1a1 Mon Sep 17 00:00:00 2001 From: J H Date: Fri, 22 Dec 2023 14:29:48 -0700 Subject: [PATCH 16/79] wip: Inherited? --- rpc-toolkit/tests/handler.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index 6621f36..6c8b46a 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -83,10 +83,14 @@ fn make_api() -> ParentHandler { struct HelloParams { whom: String, } + #[derive(Debug, Clone, Deserialize, Serialize, Parser)] + struct InheritParams { + donde: String, + } ParentHandler::new() .subcommand( "echo", - ParentHandler::new() + ParentHandler::::new() .subcommand_no_cli( "echo_no_cli", from_fn(|c: CliContext| { @@ -111,6 +115,18 @@ fn make_api() -> ParentHandler { }), ) .subcommand("a_hello", from_fn_async(a_hello)) + // .subcommand_with_inherited( + // "inherited", + // ParentHandler::::new().subcommand_no_cli( + // "echo_no_cli", + // from_fn(|c: CliContext| { + // Ok::<_, RpcError>( + // format!("Subcommand No Cli: Host {host}", host = c.host()).to_string(), + // ) + // }), + // ), + // |InheritParams { donde }, _| InheritParams { donde }, + // ) } pub fn internal_error(e: impl Display) -> RpcError { From b2e0e885657f396329eaa1f2fef7a3a61fda8356 Mon Sep 17 00:00:00 2001 From: J H Date: Fri, 22 Dec 2023 15:11:15 -0700 Subject: [PATCH 17/79] wip: Fixed with it compiling --- rpc-toolkit/tests/handler.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index 6c8b46a..9e8f51e 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -115,18 +115,18 @@ fn make_api() -> ParentHandler { }), ) .subcommand("a_hello", from_fn_async(a_hello)) - // .subcommand_with_inherited( - // "inherited", - // ParentHandler::::new().subcommand_no_cli( - // "echo_no_cli", - // from_fn(|c: CliContext| { - // Ok::<_, RpcError>( - // format!("Subcommand No Cli: Host {host}", host = c.host()).to_string(), - // ) - // }), - // ), - // |InheritParams { donde }, _| InheritParams { donde }, - // ) + .subcommand( + "dondes", + ParentHandler::::new().subcommand_with_inherited_no_cli( + "donde", + from_fn(|c: CliContext, _: (), donde| { + Ok::<_, RpcError>( + format!("Subcommand No Cli: Host {host}", host = c.host()).to_string(), + ) + }), + |InheritParams { donde }, _| donde, + ), + ) } pub fn internal_error(e: impl Display) -> RpcError { From 097ea88139be393d015e86c229f23487960e31d5 Mon Sep 17 00:00:00 2001 From: J H Date: Fri, 22 Dec 2023 15:17:41 -0700 Subject: [PATCH 18/79] wip: Bring in the donde --- rpc-toolkit/tests/handler.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index 9e8f51e..1410845 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -121,7 +121,11 @@ fn make_api() -> ParentHandler { "donde", from_fn(|c: CliContext, _: (), donde| { Ok::<_, RpcError>( - format!("Subcommand No Cli: Host {host}", host = c.host()).to_string(), + format!( + "Subcommand No Cli: Host {host} Donde = {donde}", + host = c.host() + ) + .to_string(), ) }), |InheritParams { donde }, _| donde, From 1442d36e5e68cfd765e26c3c9f2446cbab66c68b Mon Sep 17 00:00:00 2001 From: J H Date: Fri, 22 Dec 2023 15:53:47 -0700 Subject: [PATCH 19/79] wip: Working on the root handler and have an error --- rpc-toolkit/tests/handler.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index 1410845..299100a 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -90,7 +90,7 @@ fn make_api() -> ParentHandler { ParentHandler::new() .subcommand( "echo", - ParentHandler::::new() + ParentHandler::::new() .subcommand_no_cli( "echo_no_cli", from_fn(|c: CliContext| { @@ -131,6 +131,34 @@ fn make_api() -> ParentHandler { |InheritParams { donde }, _| donde, ), ) + .subcommand( + "fizz", + ParentHandler::::new().root_handler( + from_fn(|c: CliContext, _, InheritParams { donde }| { + Ok::<_, RpcError>( + format!( + "Subcommand No Cli: Host {host} Donde = {donde}", + host = c.host(), + ) + .to_string(), + ) + }), + |id, _| id, + ), + ) + .subcommand( + "error", + ParentHandler::::new().root_handler_no_cli( + from_fn(|c: CliContext, _, InheritParams { donde }| { + Err::(RpcError { + code: 1, + message: "This is an example message".into(), + data: None, + }) + }), + |id, _| id, + ), + ) } pub fn internal_error(e: impl Display) -> RpcError { From 434d521c7484db3256fbbfe4188203e5b35d1ba5 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 27 Dec 2023 22:53:59 -0700 Subject: [PATCH 20/79] macros wip --- Cargo.lock | 37 + rpc-toolkit-macro-internals/Cargo.toml | 1 + .../src/command/build.rs | 2494 +++++++++-------- .../src/command/mod.rs | 15 +- .../src/command/parse.rs | 204 +- rpc-toolkit-macro-internals/src/lib.rs | 9 - .../src/rpc_handler/build.rs | 129 - .../src/rpc_handler/mod.rs | 12 - .../src/rpc_handler/parse.rs | 50 - .../src/rpc_server/build.rs | 25 - .../src/rpc_server/mod.rs | 5 - .../src/run_cli/build.rs | 81 - .../src/run_cli/mod.rs | 22 - .../src/run_cli/parse.rs | 68 - rpc-toolkit-macro/src/lib.rs | 18 - rpc-toolkit/Cargo.toml | 1 + rpc-toolkit/src/cli.rs | 263 +- rpc-toolkit/src/command_helpers.rs | 33 + rpc-toolkit/src/context.rs | 13 +- rpc-toolkit/src/handler.rs | 330 ++- rpc-toolkit/src/lib.rs | 1 + rpc-toolkit/src/server/socket.rs | 12 +- rpc-toolkit/src/util.rs | 52 +- rpc-toolkit/tests/compat.rs | 182 +- rpc-toolkit/tests/handler.rs | 153 +- 25 files changed, 2157 insertions(+), 2053 deletions(-) delete mode 100644 rpc-toolkit-macro-internals/src/rpc_handler/build.rs delete mode 100644 rpc-toolkit-macro-internals/src/rpc_handler/mod.rs delete mode 100644 rpc-toolkit-macro-internals/src/rpc_handler/parse.rs delete mode 100644 rpc-toolkit-macro-internals/src/rpc_server/build.rs delete mode 100644 rpc-toolkit-macro-internals/src/rpc_server/mod.rs delete mode 100644 rpc-toolkit-macro-internals/src/run_cli/build.rs delete mode 100644 rpc-toolkit-macro-internals/src/run_cli/mod.rs delete mode 100644 rpc-toolkit-macro-internals/src/run_cli/parse.rs create mode 100644 rpc-toolkit/src/command_helpers.rs diff --git a/Cargo.lock b/Cargo.lock index a2d76e7..5b323fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -251,6 +251,12 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + [[package]] name = "encoding_rs" version = "0.8.33" @@ -675,6 +681,15 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "itertools" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.10" @@ -888,6 +903,26 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.41", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1001,6 +1036,7 @@ dependencies = [ "lazy_format", "lazy_static", "openssl", + "pin-project", "reqwest", "rpc-toolkit-macro", "serde", @@ -1026,6 +1062,7 @@ dependencies = [ name = "rpc-toolkit-macro-internals" version = "0.2.2" dependencies = [ + "itertools", "proc-macro2", "quote", "syn 1.0.109", diff --git a/rpc-toolkit-macro-internals/Cargo.toml b/rpc-toolkit-macro-internals/Cargo.toml index 87552e5..dde611b 100644 --- a/rpc-toolkit-macro-internals/Cargo.toml +++ b/rpc-toolkit-macro-internals/Cargo.toml @@ -11,3 +11,4 @@ license = "MIT" proc-macro2 = "1.0" quote = "1.0" syn = { version = "1.0", features = ["full", "fold"] } +itertools = "0.12" diff --git a/rpc-toolkit-macro-internals/src/command/build.rs b/rpc-toolkit-macro-internals/src/command/build.rs index 2692af0..15d45dd 100644 --- a/rpc-toolkit-macro-internals/src/command/build.rs +++ b/rpc-toolkit-macro-internals/src/command/build.rs @@ -1,5 +1,6 @@ use std::collections::HashSet; +use itertools::MultiUnzip; use proc_macro2::*; use quote::*; use syn::fold::Fold; @@ -10,355 +11,355 @@ use syn::token::{Add, Comma, Where}; use super::parse::*; use super::*; -fn ctx_trait(ctx_ty: Option, opt: &mut Options) -> TokenStream { - let mut bounds: Punctuated = Punctuated::new(); - bounds.push(macro_try!(parse2(quote! { ::rpc_toolkit::Context }))); - let mut rpc_bounds = bounds.clone(); - let mut cli_bounds = bounds; +// fn ctx_trait(ctx_ty: Option, opt: &mut Options) -> TokenStream { +// let mut bounds: Punctuated = Punctuated::new(); +// bounds.push(macro_try!(parse2(quote! { ::rpc_toolkit::Context }))); +// let mut rpc_bounds = bounds.clone(); +// let mut cli_bounds = bounds; - let (use_cli, use_rpc) = match &opt.common().exec_ctx { - ExecutionContext::CliOnly(_) => (Some(None), false), - ExecutionContext::RpcOnly(_) | ExecutionContext::Standard => (None, true), - ExecutionContext::Local(_) => (Some(None), true), - ExecutionContext::CustomCli { context, .. } => (Some(Some(context.clone())), true), - }; +// let (use_cli, use_rpc) = match &opt.common().exec_ctx { +// ExecutionContext::CliOnly(_) => (Some(None), false), +// ExecutionContext::RpcOnly(_) | ExecutionContext::Standard => (None, true), +// ExecutionContext::Local(_) => (Some(None), true), +// ExecutionContext::CustomCli { context, .. } => (Some(Some(context.clone())), true), +// }; - if let Options::Parent(ParentOptions { - subcommands, - self_impl, - .. - }) = opt - { - if let Some(ctx_ty) = ctx_ty { - cli_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); - cli_bounds.push(macro_try!(parse2(quote! { Clone }))); - rpc_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); - rpc_bounds.push(macro_try!(parse2(quote! { Clone }))); - } - if let Some(SelfImplInfo { context, .. }) = self_impl { - if let Some(cli_ty) = use_cli.as_ref() { - if let Some(cli_ty) = cli_ty { - cli_bounds.push(macro_try!(parse2(quote! { Into<#cli_ty> }))); - } else { - cli_bounds.push(macro_try!(parse2(quote! { Into<#context> }))); - } - } - if use_rpc { - rpc_bounds.push(macro_try!(parse2(quote! { Into<#context> }))); - } - } - for subcmd in subcommands { - let mut path = subcmd.clone(); - std::mem::take(&mut path.segments.last_mut().unwrap().arguments); - cli_bounds.push(macro_try!(parse2(quote! { #path::CommandContextCli }))); - rpc_bounds.push(macro_try!(parse2(quote! { #path::CommandContextRpc }))); - } - } else { - if let Some(cli_ty) = use_cli.as_ref() { - if let Some(cli_ty) = cli_ty { - cli_bounds.push(macro_try!(parse2(quote! { Into<#cli_ty> }))); - } else if let Some(ctx_ty) = &ctx_ty { - cli_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); - } - } - if use_rpc { - if let Some(ctx_ty) = &ctx_ty { - rpc_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); - } - } - } +// if let Options::Parent(ParentOptions { +// subcommands, +// self_impl, +// .. +// }) = opt +// { +// if let Some(ctx_ty) = ctx_ty { +// cli_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); +// cli_bounds.push(macro_try!(parse2(quote! { Clone }))); +// rpc_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); +// rpc_bounds.push(macro_try!(parse2(quote! { Clone }))); +// } +// if let Some(SelfImplInfo { context, .. }) = self_impl { +// if let Some(cli_ty) = use_cli.as_ref() { +// if let Some(cli_ty) = cli_ty { +// cli_bounds.push(macro_try!(parse2(quote! { Into<#cli_ty> }))); +// } else { +// cli_bounds.push(macro_try!(parse2(quote! { Into<#context> }))); +// } +// } +// if use_rpc { +// rpc_bounds.push(macro_try!(parse2(quote! { Into<#context> }))); +// } +// } +// for subcmd in subcommands { +// let mut path = subcmd.clone(); +// std::mem::take(&mut path.segments.last_mut().unwrap().arguments); +// cli_bounds.push(macro_try!(parse2(quote! { #path::CommandContextCli }))); +// rpc_bounds.push(macro_try!(parse2(quote! { #path::CommandContextRpc }))); +// } +// } else { +// if let Some(cli_ty) = use_cli.as_ref() { +// if let Some(cli_ty) = cli_ty { +// cli_bounds.push(macro_try!(parse2(quote! { Into<#cli_ty> }))); +// } else if let Some(ctx_ty) = &ctx_ty { +// cli_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); +// } +// } +// if use_rpc { +// if let Some(ctx_ty) = &ctx_ty { +// rpc_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); +// } +// } +// } - let res = quote! { - pub trait CommandContextCli: #cli_bounds {} - impl CommandContextCli for T where T: #cli_bounds {} +// let res = quote! { +// pub trait CommandContextCli: #cli_bounds {} +// impl CommandContextCli for T where T: #cli_bounds {} - pub trait CommandContextRpc: #rpc_bounds {} - impl CommandContextRpc for T where T: #rpc_bounds {} - }; - res -} +// pub trait CommandContextRpc: #rpc_bounds {} +// impl CommandContextRpc for T where T: #rpc_bounds {} +// }; +// res +// } -fn metadata(full_options: &Options) -> TokenStream { - let options = match full_options { - Options::Leaf(a) => a, - Options::Parent(ParentOptions { common, .. }) => common, - }; - let fallthrough = |ty: &str| { - let getter_name = Ident::new(&format!("get_{}", ty), Span::call_site()); - match &*full_options { - Options::Parent(ParentOptions { subcommands, .. }) => { - let subcmd_handler = subcommands.iter().map(|subcmd| { - let mut subcmd = subcmd.clone(); - subcmd.segments.last_mut().unwrap().arguments = PathArguments::None; - quote_spanned!{ subcmd.span() => - [#subcmd::NAME, rest] => if let Some(val) = #subcmd::Metadata.#getter_name(rest, key) { - return Some(val); - }, - } - }); - quote! { - if !command.is_empty() { - match command.splitn(2, ".").chain(std::iter::repeat("")).take(2).collect::>().as_slice() { - #( - #subcmd_handler - )* - _ => () - } - } - } - } - _ => quote! {}, - } - }; - fn impl_getter>( - ty: &str, - metadata: I, - fallthrough: TokenStream, - ) -> TokenStream { - let getter_name = Ident::new(&format!("get_{}", ty), Span::call_site()); - let ty: Type = syn::parse_str(ty).unwrap(); - quote! { - fn #getter_name(&self, command: &str, key: &str) -> Option<#ty> { - #fallthrough - match key { - #(#metadata)* - _ => None, - } - } - } - } - let bool_metadata = options - .metadata - .iter() - .filter(|(_, lit)| matches!(lit, Lit::Bool(_))) - .map(|(name, value)| { - let name = LitStr::new(&name.to_string(), name.span()); - quote! { - #name => Some(#value), - } - }); - let number_metadata = |ty: &str| { - let ty: Type = syn::parse_str(ty).unwrap(); - options - .metadata - .iter() - .filter(|(_, lit)| matches!(lit, Lit::Int(_) | Lit::Float(_) | Lit::Byte(_))) - .map(move |(name, value)| { - let name = LitStr::new(&name.to_string(), name.span()); - quote! { - #name => Some(#value as #ty), - } - }) - }; - let char_metadata = options - .metadata - .iter() - .filter(|(_, lit)| matches!(lit, Lit::Char(_))) - .map(|(name, value)| { - let name = LitStr::new(&name.to_string(), name.span()); - quote! { - #name => Some(#value), - } - }); - let str_metadata = options - .metadata - .iter() - .filter(|(_, lit)| matches!(lit, Lit::Str(_))) - .map(|(name, value)| { - let name = LitStr::new(&name.to_string(), name.span()); - quote! { - #name => Some(#value), - } - }); - let bstr_metadata = options - .metadata - .iter() - .filter(|(_, lit)| matches!(lit, Lit::ByteStr(_))) - .map(|(name, value)| { - let name = LitStr::new(&name.to_string(), name.span()); - quote! { - #name => Some(#value), - } - }); +// fn metadata(full_options: &Options) -> TokenStream { +// let options = match full_options { +// Options::Leaf(a) => a, +// Options::Parent(ParentOptions { common, .. }) => common, +// }; +// let fallthrough = |ty: &str| { +// let getter_name = Ident::new(&format!("get_{}", ty), Span::call_site()); +// match &*full_options { +// Options::Parent(ParentOptions { subcommands, .. }) => { +// let subcmd_handler = subcommands.iter().map(|subcmd| { +// let mut subcmd = subcmd.clone(); +// subcmd.segments.last_mut().unwrap().arguments = PathArguments::None; +// quote_spanned!{ subcmd.span() => +// [#subcmd::NAME, rest] => if let Some(val) = #subcmd::Metadata.#getter_name(rest, key) { +// return Some(val); +// }, +// } +// }); +// quote! { +// if !command.is_empty() { +// match command.splitn(2, ".").chain(std::iter::repeat("")).take(2).collect::>().as_slice() { +// #( +// #subcmd_handler +// )* +// _ => () +// } +// } +// } +// } +// _ => quote! {}, +// } +// }; +// fn impl_getter>( +// ty: &str, +// metadata: I, +// fallthrough: TokenStream, +// ) -> TokenStream { +// let getter_name = Ident::new(&format!("get_{}", ty), Span::call_site()); +// let ty: Type = syn::parse_str(ty).unwrap(); +// quote! { +// fn #getter_name(&self, command: &str, key: &str) -> Option<#ty> { +// #fallthrough +// match key { +// #(#metadata)* +// _ => None, +// } +// } +// } +// } +// let bool_metadata = options +// .metadata +// .iter() +// .filter(|(_, lit)| matches!(lit, Lit::Bool(_))) +// .map(|(name, value)| { +// let name = LitStr::new(&name.to_string(), name.span()); +// quote! { +// #name => Some(#value), +// } +// }); +// let number_metadata = |ty: &str| { +// let ty: Type = syn::parse_str(ty).unwrap(); +// options +// .metadata +// .iter() +// .filter(|(_, lit)| matches!(lit, Lit::Int(_) | Lit::Float(_) | Lit::Byte(_))) +// .map(move |(name, value)| { +// let name = LitStr::new(&name.to_string(), name.span()); +// quote! { +// #name => Some(#value as #ty), +// } +// }) +// }; +// let char_metadata = options +// .metadata +// .iter() +// .filter(|(_, lit)| matches!(lit, Lit::Char(_))) +// .map(|(name, value)| { +// let name = LitStr::new(&name.to_string(), name.span()); +// quote! { +// #name => Some(#value), +// } +// }); +// let str_metadata = options +// .metadata +// .iter() +// .filter(|(_, lit)| matches!(lit, Lit::Str(_))) +// .map(|(name, value)| { +// let name = LitStr::new(&name.to_string(), name.span()); +// quote! { +// #name => Some(#value), +// } +// }); +// let bstr_metadata = options +// .metadata +// .iter() +// .filter(|(_, lit)| matches!(lit, Lit::ByteStr(_))) +// .map(|(name, value)| { +// let name = LitStr::new(&name.to_string(), name.span()); +// quote! { +// #name => Some(#value), +// } +// }); - let bool_getter = impl_getter("bool", bool_metadata, fallthrough("bool")); - let u8_getter = impl_getter("u8", number_metadata("u8"), fallthrough("u8")); - let u16_getter = impl_getter("u16", number_metadata("u16"), fallthrough("u16")); - let u32_getter = impl_getter("u32", number_metadata("u32"), fallthrough("u32")); - let u64_getter = impl_getter("u64", number_metadata("u64"), fallthrough("u64")); - let usize_getter = impl_getter("usize", number_metadata("usize"), fallthrough("usize")); - let i8_getter = impl_getter("i8", number_metadata("i8"), fallthrough("i8")); - let i16_getter = impl_getter("i16", number_metadata("i16"), fallthrough("i16")); - let i32_getter = impl_getter("i32", number_metadata("i32"), fallthrough("i32")); - let i64_getter = impl_getter("i64", number_metadata("i64"), fallthrough("i64")); - let isize_getter = impl_getter("isize", number_metadata("isize"), fallthrough("isize")); - let f32_getter = impl_getter("f32", number_metadata("f32"), fallthrough("f32")); - let f64_getter = impl_getter("f64", number_metadata("f64"), fallthrough("f64")); - let char_getter = impl_getter("char", char_metadata, fallthrough("char")); - let str_fallthrough = fallthrough("str"); - let str_getter = quote! { - fn get_str(&self, command: &str, key: &str) -> Option<&'static str> { - #str_fallthrough - match key { - #(#str_metadata)* - _ => None, - } - } - }; - let bstr_fallthrough = fallthrough("bstr"); - let bstr_getter = quote! { - fn get_bstr(&self, command: &str, key: &str) -> Option<&'static [u8]> { - #bstr_fallthrough - match key { - #(#bstr_metadata)* - _ => None, - } - } - }; +// let bool_getter = impl_getter("bool", bool_metadata, fallthrough("bool")); +// let u8_getter = impl_getter("u8", number_metadata("u8"), fallthrough("u8")); +// let u16_getter = impl_getter("u16", number_metadata("u16"), fallthrough("u16")); +// let u32_getter = impl_getter("u32", number_metadata("u32"), fallthrough("u32")); +// let u64_getter = impl_getter("u64", number_metadata("u64"), fallthrough("u64")); +// let usize_getter = impl_getter("usize", number_metadata("usize"), fallthrough("usize")); +// let i8_getter = impl_getter("i8", number_metadata("i8"), fallthrough("i8")); +// let i16_getter = impl_getter("i16", number_metadata("i16"), fallthrough("i16")); +// let i32_getter = impl_getter("i32", number_metadata("i32"), fallthrough("i32")); +// let i64_getter = impl_getter("i64", number_metadata("i64"), fallthrough("i64")); +// let isize_getter = impl_getter("isize", number_metadata("isize"), fallthrough("isize")); +// let f32_getter = impl_getter("f32", number_metadata("f32"), fallthrough("f32")); +// let f64_getter = impl_getter("f64", number_metadata("f64"), fallthrough("f64")); +// let char_getter = impl_getter("char", char_metadata, fallthrough("char")); +// let str_fallthrough = fallthrough("str"); +// let str_getter = quote! { +// fn get_str(&self, command: &str, key: &str) -> Option<&'static str> { +// #str_fallthrough +// match key { +// #(#str_metadata)* +// _ => None, +// } +// } +// }; +// let bstr_fallthrough = fallthrough("bstr"); +// let bstr_getter = quote! { +// fn get_bstr(&self, command: &str, key: &str) -> Option<&'static [u8]> { +// #bstr_fallthrough +// match key { +// #(#bstr_metadata)* +// _ => None, +// } +// } +// }; - let res = quote! { - #[derive(Clone, Copy, Default)] - pub struct Metadata; +// let res = quote! { +// #[derive(Clone, Copy, Default)] +// pub struct Metadata; - #[allow(overflowing_literals)] - impl ::rpc_toolkit::Metadata for Metadata { - #bool_getter - #u8_getter - #u16_getter - #u32_getter - #u64_getter - #usize_getter - #i8_getter - #i16_getter - #i32_getter - #i64_getter - #isize_getter - #f32_getter - #f64_getter - #char_getter - #str_getter - #bstr_getter - } - }; - // panic!("{}", res); - res -} +// #[allow(overflowing_literals)] +// impl ::rpc_toolkit::Metadata for Metadata { +// #bool_getter +// #u8_getter +// #u16_getter +// #u32_getter +// #u64_getter +// #usize_getter +// #i8_getter +// #i16_getter +// #i32_getter +// #i64_getter +// #isize_getter +// #f32_getter +// #f64_getter +// #char_getter +// #str_getter +// #bstr_getter +// } +// }; +// // panic!("{}", res); +// res +// } -fn build_app(name: LitStr, opt: &mut Options, params: &mut [ParamType]) -> TokenStream { - let about = opt.common().about.clone().into_iter(); - let (subcommand, subcommand_required) = if let Options::Parent(opt) = opt { - ( - opt.subcommands - .iter() - .map(|subcmd| { - let mut path = subcmd.clone(); - path.segments.last_mut().unwrap().arguments = PathArguments::None; - path - }) - .collect(), - opt.self_impl.is_none(), - ) - } else { - (Vec::new(), false) - }; - let arg = params - .iter_mut() - .filter_map(|param| { - if let ParamType::Arg(arg) = param { - if arg.stdin.is_some() { - return None; - } - let name = arg.name.clone().unwrap(); - let name_str = arg.rename.clone().unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); - let help = arg.help.clone().into_iter(); - let short = arg.short.clone().into_iter(); - let long = arg.long.clone().into_iter(); - let mut modifications = TokenStream::default(); - let ty_span = arg.ty.span(); - if let Type::Path(p) = &mut arg.ty { - if p.path.is_ident("bool") - && arg.parse.is_none() - && (arg.short.is_some() || arg.long.is_some()) - { - arg.check_is_present = true; - modifications.extend(quote_spanned! { ty_span => - arg = arg.takes_value(false); - }); - } else if arg.count.is_some() { - modifications.extend(quote_spanned! { ty_span => - arg = arg.takes_value(false); - arg = arg.multiple(true); - }); - } else { - modifications.extend(quote_spanned! { ty_span => - arg = arg.takes_value(true); - }); - if let Some(_) = &arg.default { - modifications.extend(quote_spanned! { ty_span => - arg = arg.required(false); - }); - } else if p.path.segments.last().unwrap().ident == "Option" { - arg.optional = true; - modifications.extend(quote_spanned! { ty_span => - arg = arg.required(false); - }); - } else if arg.multiple.is_some() { - modifications.extend(quote_spanned! { ty_span => - arg = arg.multiple(true); - }); - } else { - modifications.extend(quote_spanned! { ty_span => - arg = arg.required(true); - }); - } - } - }; - Some(quote! { - { - let mut arg = ::rpc_toolkit::command_helpers::prelude::Arg::with_name(#name_str); - #( - arg = arg.help(#help); - )* - #( - arg = arg.short(#short); - )* - #( - arg = arg.long(#long); - )* - #modifications +// fn build_app(name: LitStr, opt: &mut Options, params: &mut [ParamType]) -> TokenStream { +// let about = opt.common().about.clone().into_iter(); +// let (subcommand, subcommand_required) = if let Options::Parent(opt) = opt { +// ( +// opt.subcommands +// .iter() +// .map(|subcmd| { +// let mut path = subcmd.clone(); +// path.segments.last_mut().unwrap().arguments = PathArguments::None; +// path +// }) +// .collect(), +// opt.self_impl.is_none(), +// ) +// } else { +// (Vec::new(), false) +// }; +// let arg = params +// .iter_mut() +// .filter_map(|param| { +// if let ParamType::Arg(arg) = param { +// if arg.stdin.is_some() { +// return None; +// } +// let name = arg.name.clone().unwrap(); +// let name_str = arg.rename.clone().unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); +// let help = arg.help.clone().into_iter(); +// let short = arg.short.clone().into_iter(); +// let long = arg.long.clone().into_iter(); +// let mut modifications = TokenStream::default(); +// let ty_span = arg.ty.span(); +// if let Type::Path(p) = &mut arg.ty { +// if p.path.is_ident("bool") +// && arg.parse.is_none() +// && (arg.short.is_some() || arg.long.is_some()) +// { +// arg.check_is_present = true; +// modifications.extend(quote_spanned! { ty_span => +// arg = arg.takes_value(false); +// }); +// } else if arg.count.is_some() { +// modifications.extend(quote_spanned! { ty_span => +// arg = arg.takes_value(false); +// arg = arg.multiple(true); +// }); +// } else { +// modifications.extend(quote_spanned! { ty_span => +// arg = arg.takes_value(true); +// }); +// if let Some(_) = &arg.default { +// modifications.extend(quote_spanned! { ty_span => +// arg = arg.required(false); +// }); +// } else if p.path.segments.last().unwrap().ident == "Option" { +// arg.optional = true; +// modifications.extend(quote_spanned! { ty_span => +// arg = arg.required(false); +// }); +// } else if arg.multiple.is_some() { +// modifications.extend(quote_spanned! { ty_span => +// arg = arg.multiple(true); +// }); +// } else { +// modifications.extend(quote_spanned! { ty_span => +// arg = arg.required(true); +// }); +// } +// } +// }; +// Some(quote! { +// { +// let mut arg = ::rpc_toolkit::command_helpers::prelude::Arg::with_name(#name_str); +// #( +// arg = arg.help(#help); +// )* +// #( +// arg = arg.short(#short); +// )* +// #( +// arg = arg.long(#long); +// )* +// #modifications - arg - } - }) - } else { - None - } - }) - .collect::>(); - let required = LitBool::new(subcommand_required, Span::call_site()); - let alias = &opt.common().aliases; - quote! { - pub fn build_app() -> ::rpc_toolkit::command_helpers::prelude::App<'static> { - let mut app = ::rpc_toolkit::command_helpers::prelude::App::new(#name); - #( - app = app.about(#about); - )* - #( - app = app.alias(#alias); - )* - #( - app = app.arg(#arg); - )* - #( - app = app.subcommand(#subcommand::build_app()); - )* - if #required { - app = app.setting(::rpc_toolkit::command_helpers::prelude::AppSettings::SubcommandRequired); - } - app - } - } -} +// arg +// } +// }) +// } else { +// None +// } +// }) +// .collect::>(); +// let required = LitBool::new(subcommand_required, Span::call_site()); +// let alias = &opt.common().aliases; +// quote! { +// pub fn build_app() -> ::rpc_toolkit::command_helpers::prelude::App<'static> { +// let mut app = ::rpc_toolkit::command_helpers::prelude::App::new(#name); +// #( +// app = app.about(#about); +// )* +// #( +// app = app.alias(#alias); +// )* +// #( +// app = app.arg(#arg); +// )* +// #( +// app = app.subcommand(#subcommand::build_app()); +// )* +// if #required { +// app = app.setting(::rpc_toolkit::command_helpers::prelude::AppSettings::SubcommandRequired); +// } +// app +// } +// } +// } struct GenericFilter<'a> { src: &'a Generics, @@ -406,7 +407,7 @@ impl<'a> GenericFilter<'a> { WherePredicate::Type(PredicateType { bounded_ty: Type::Path(t), .. - }) if self.types.contains(&t.path.segments.last().unwrap().ident) => { + }) if self.types.contains(&t.path.segments.first().unwrap().ident) => { where_clause.predicates.push(src_predicate.clone()) } _ => (), @@ -447,7 +448,9 @@ impl<'a> Fold for GenericFilter<'a> { .params .iter() .filter_map(|param| match (param, &i) { - (GenericParam::Type(t), Type::Path(i)) if i.path.is_ident(&t.ident) => { + (GenericParam::Type(t), Type::Path(i)) + if &i.path.segments.first().unwrap().ident == &t.ident => + { Some(t.ident.clone()) } _ => None, @@ -457,831 +460,1052 @@ impl<'a> Fold for GenericFilter<'a> { } } -fn rpc_handler( - fn_name: &Ident, - fn_generics: &Generics, - opt: &Options, - params: &[ParamType], -) -> TokenStream { - let mut parent_data_ty = quote! { () }; - let mut generics = fn_generics.clone(); - generics.params.push(macro_try!(syn::parse2( - quote! { GenericContext: CommandContextRpc } - ))); - if generics.lt_token.is_none() { - generics.lt_token = Some(Default::default()); - } - if generics.gt_token.is_none() { - generics.gt_token = Some(Default::default()); - } - let mut param_def = Vec::new(); - for param in params { - match param { - ParamType::Arg(arg) => { - let name = arg.name.clone().unwrap(); - let rename = arg - .rename - .clone() - .unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); - let field_name = Ident::new(&format!("arg_{}", name), name.span()); - let ty = arg.ty.clone(); - let def = quote! { - #[serde(rename = #rename)] - #field_name: #ty, - }; - let def = match &arg.default { - Some(Some(default)) => { - quote! { - #[serde(default = #default)] - #def - } - } - Some(None) => { - quote! { - #[serde(default)] - #def - } - } - None => def, - }; - param_def.push(def); - } - ParamType::ParentData(ty) => parent_data_ty = quote! { #ty }, - _ => (), - } - } - let (_, fn_type_generics, _) = fn_generics.split_for_impl(); - let fn_turbofish = fn_type_generics.as_turbofish(); - let fn_path: Path = macro_try!(syn::parse2(quote! { super::#fn_name#fn_turbofish })); - let mut param_generics_filter = GenericFilter::new(fn_generics); - for param in params { - if let ParamType::Arg(a) = param { - param_generics_filter.fold_type(a.ty.clone()); - } +// fn rpc_handler( +// fn_name: &Ident, +// fn_generics: &Generics, +// opt: &Options, +// params: &[ParamType], +// ) -> TokenStream { +// let mut parent_data_ty = quote! { () }; +// let mut generics = fn_generics.clone(); +// generics.params.push(macro_try!(syn::parse2( +// quote! { GenericContext: CommandContextRpc } +// ))); +// if generics.lt_token.is_none() { +// generics.lt_token = Some(Default::default()); +// } +// if generics.gt_token.is_none() { +// generics.gt_token = Some(Default::default()); +// } +// let mut param_def = Vec::new(); +// for param in params { +// match param { +// ParamType::Arg(arg) => { +// let name = arg.name.clone().unwrap(); +// let rename = arg +// .rename +// .clone() +// .unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); +// let field_name = Ident::new(&format!("arg_{}", name), name.span()); +// let ty = arg.ty.clone(); +// let def = quote! { +// #[serde(rename = #rename)] +// #field_name: #ty, +// }; +// let def = match &arg.default { +// Some(Some(default)) => { +// quote! { +// #[serde(default = #default)] +// #def +// } +// } +// Some(None) => { +// quote! { +// #[serde(default)] +// #def +// } +// } +// None => def, +// }; +// param_def.push(def); +// } +// ParamType::ParentData(ty) => parent_data_ty = quote! { #ty }, +// _ => (), +// } +// } +// let (_, fn_type_generics, _) = fn_generics.split_for_impl(); +// let fn_turbofish = fn_type_generics.as_turbofish(); +// let fn_path: Path = macro_try!(syn::parse2(quote! { super::#fn_name#fn_turbofish })); +// let mut param_generics_filter = GenericFilter::new(fn_generics); +// for param in params { +// if let ParamType::Arg(a) = param { +// param_generics_filter.fold_type(a.ty.clone()); +// } +// } +// let param_generics = param_generics_filter.finish(); +// let (_, param_ty_generics, _) = param_generics.split_for_impl(); +// let param_struct_def = quote! { +// #[allow(dead_code)] +// #[derive(::rpc_toolkit::command_helpers::prelude::Deserialize)] +// pub struct Params#param_ty_generics { +// #( +// #param_def +// )* +// #[serde(flatten)] +// #[serde(default)] +// rest: ::rpc_toolkit::command_helpers::prelude::Value, +// } +// }; +// let param = params.iter().map(|param| match param { +// ParamType::Arg(arg) => { +// let name = arg.name.clone().unwrap(); +// let field_name = Ident::new(&format!("arg_{}", name), name.span()); +// quote! { args.#field_name } +// } +// ParamType::Context(ty) => { +// if matches!(opt, Options::Parent { .. }) { +// quote! { >::into(ctx.clone()) } +// } else { +// quote! { >::into(ctx) } +// } +// } +// ParamType::ParentData(_) => { +// quote! { parent_data } +// } +// ParamType::Request => quote! { request }, +// ParamType::Response => quote! { response }, +// ParamType::None => unreachable!(), +// }); +// match opt { +// Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::CliOnly(_)) => quote! { +// #param_struct_def + +// pub async fn rpc_handler#generics( +// _ctx: GenericContext, +// _parent_data: #parent_data_ty, +// _request: &::rpc_toolkit::command_helpers::prelude::RequestParts, +// _response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, +// method: &str, +// _args: Params#param_ty_generics, +// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { +// Err(::rpc_toolkit::command_helpers::prelude::RpcError { +// data: Some(method.into()), +// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR +// }) +// } +// }, +// Options::Leaf(opt) => { +// let invocation = if opt.is_async { +// quote! { +// #fn_path(#(#param),*).await? +// } +// } else if opt.blocking.is_some() { +// quote! { +// ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #fn_path(#(#param),*)).await? +// } +// } else { +// quote! { +// #fn_path(#(#param),*)? +// } +// }; +// quote! { +// #param_struct_def + +// pub async fn rpc_handler#generics( +// ctx: GenericContext, +// parent_data: #parent_data_ty, +// request: &::rpc_toolkit::command_helpers::prelude::RequestParts, +// response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, +// method: &str, +// args: Params#param_ty_generics, +// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { +// if method.is_empty() { +// Ok(::rpc_toolkit::command_helpers::prelude::to_value(#invocation)?) +// } else { +// Err(::rpc_toolkit::command_helpers::prelude::RpcError { +// data: Some(method.into()), +// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR +// }) +// } +// } +// } +// } +// Options::Parent(ParentOptions { +// common, +// subcommands, +// self_impl, +// }) => { +// let cmd_preprocess = if common.is_async { +// quote! { +// let parent_data = #fn_path(#(#param),*).await?; +// } +// } else if common.blocking.is_some() { +// quote! { +// let parent_data = ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #fn_path(#(#param),*)).await?; +// } +// } else { +// quote! { +// let parent_data = #fn_path(#(#param),*)?; +// } +// }; +// let subcmd_impl = subcommands.iter().map(|subcommand| { +// let mut subcommand = subcommand.clone(); +// let mut rpc_handler = PathSegment { +// ident: Ident::new("rpc_handler", Span::call_site()), +// arguments: std::mem::replace( +// &mut subcommand.segments.last_mut().unwrap().arguments, +// PathArguments::None, +// ), +// }; +// rpc_handler.arguments = match rpc_handler.arguments { +// PathArguments::None => PathArguments::AngleBracketed( +// syn::parse2(quote! { :: }) +// .unwrap(), +// ), +// PathArguments::AngleBracketed(mut a) => { +// a.args.push(syn::parse2(quote! { GenericContext }).unwrap()); +// PathArguments::AngleBracketed(a) +// } +// _ => unreachable!(), +// }; +// quote_spanned!{ subcommand.span() => +// [#subcommand::NAME, rest] => #subcommand::#rpc_handler(ctx, parent_data, request, response, rest, ::rpc_toolkit::command_helpers::prelude::from_value(args.rest)?).await +// } +// }); +// let subcmd_impl = quote! { +// match method.splitn(2, ".").chain(std::iter::repeat("")).take(2).collect::>().as_slice() { +// #( +// #subcmd_impl, +// )* +// _ => Err(::rpc_toolkit::command_helpers::prelude::RpcError { +// data: Some(method.into()), +// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR +// }) +// } +// }; +// match self_impl { +// Some(self_impl) if !matches!(common.exec_ctx, ExecutionContext::CliOnly(_)) => { +// let self_impl_fn = &self_impl.path; +// let self_impl = if self_impl.is_async { +// quote_spanned! { self_impl_fn.span() => +// #self_impl_fn(Into::into(ctx), parent_data).await? +// } +// } else if self_impl.blocking { +// quote_spanned! { self_impl_fn.span() => +// { +// let ctx = Into::into(ctx); +// ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #self_impl_fn(ctx, parent_data)).await? +// } +// } +// } else { +// quote_spanned! { self_impl_fn.span() => +// #self_impl_fn(Into::into(ctx), parent_data)? +// } +// }; +// quote! { +// #param_struct_def + +// pub async fn rpc_handler#generics( +// ctx: GenericContext, +// parent_data: #parent_data_ty, +// request: &::rpc_toolkit::command_helpers::prelude::RequestParts, +// response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, +// method: &str, +// args: Params#param_ty_generics, +// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { +// #cmd_preprocess + +// if method.is_empty() { +// Ok(::rpc_toolkit::command_helpers::prelude::to_value(&#self_impl)?) +// } else { +// #subcmd_impl +// } +// } +// } +// } +// _ => { +// quote! { +// #param_struct_def + +// pub async fn rpc_handler#generics( +// ctx: GenericContext, +// parent_data: #parent_data_ty, +// request: &::rpc_toolkit::command_helpers::prelude::RequestParts, +// response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, +// method: &str, +// args: Params#param_ty_generics, +// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { +// #cmd_preprocess + +// #subcmd_impl +// } +// } +// } +// } +// } +// } +// } + +// fn cli_handler( +// fn_name: &Ident, +// fn_generics: &Generics, +// opt: &mut Options, +// params: &[ParamType], +// ) -> TokenStream { +// let mut parent_data_ty = quote! { () }; +// let mut generics = fn_generics.clone(); +// generics.params.push(macro_try!(syn::parse2( +// quote! { ParentParams: ::rpc_toolkit::command_helpers::prelude::Serialize } +// ))); +// generics.params.push(macro_try!(syn::parse2( +// quote! { GenericContext: CommandContextCli } +// ))); +// if generics.lt_token.is_none() { +// generics.lt_token = Some(Default::default()); +// } +// if generics.gt_token.is_none() { +// generics.gt_token = Some(Default::default()); +// } +// let (_, fn_type_generics, _) = fn_generics.split_for_impl(); +// let fn_turbofish = fn_type_generics.as_turbofish(); +// let fn_path: Path = macro_try!(syn::parse2(quote! { super::#fn_name#fn_turbofish })); +// let is_parent = matches!(opt, Options::Parent { .. }); +// let param: Vec<_> = params +// .iter() +// .map(|param| match param { +// ParamType::Arg(arg) => { +// let name = arg.name.clone().unwrap(); +// let field_name = Ident::new(&format!("arg_{}", name), name.span()); +// quote! { params.#field_name.clone() } +// } +// ParamType::Context(ty) => { +// if is_parent { +// quote! { >::into(ctx.clone()) } +// } else { +// quote! { >::into(ctx) } +// } +// } +// ParamType::ParentData(ty) => { +// parent_data_ty = quote! { #ty }; +// quote! { parent_data } +// } +// ParamType::Request => quote! { request }, +// ParamType::Response => quote! { response }, +// ParamType::None => unreachable!(), +// }) +// .collect(); +// let mut param_generics_filter = GenericFilter::new(fn_generics); +// for param in params { +// if let ParamType::Arg(a) = param { +// param_generics_filter.fold_type(a.ty.clone()); +// } +// } +// let mut param_generics = param_generics_filter.finish(); +// param_generics.params.push(macro_try!(syn::parse2(quote! { +// ParentParams: ::rpc_toolkit::command_helpers::prelude::Serialize +// }))); +// if param_generics.lt_token.is_none() { +// param_generics.lt_token = Some(Default::default()); +// } +// if param_generics.gt_token.is_none() { +// param_generics.gt_token = Some(Default::default()); +// } +// let (_, param_ty_generics, _) = param_generics.split_for_impl(); +// let mut arg_def = Vec::new(); +// for param in params { +// match param { +// ParamType::Arg(arg) => { +// let name = arg.name.clone().unwrap(); +// let rename = arg +// .rename +// .clone() +// .unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); +// let field_name = Ident::new(&format!("arg_{}", name), name.span()); +// let ty = arg.ty.clone(); +// arg_def.push(quote! { +// #[serde(rename = #rename)] +// #field_name: #ty, +// }) +// } +// _ => (), +// } +// } +// let arg = params +// .iter() +// .filter_map(|param| { +// if let ParamType::Arg(a) = param { +// Some(a) +// } else { +// None +// } +// }) +// .map(|arg| { +// let name = arg.name.clone().unwrap(); +// let arg_name = arg.rename.clone().unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); +// let field_name = Ident::new(&format!("arg_{}", name), name.span()); +// if arg.stdin.is_some() { +// if let Some(parse) = &arg.parse { +// quote! { +// #field_name: #parse(&mut std::io::stdin(), matches)?, +// } +// } else { +// quote! { +// #field_name: ::rpc_toolkit::command_helpers::prelude::default_stdin_parser(&mut std::io::stdin(), matches)?, +// } +// } +// } else if arg.check_is_present { +// quote! { +// #field_name: matches.is_present(#arg_name), +// } +// } else if arg.count.is_some() { +// quote! { +// #field_name: matches.occurrences_of(#arg_name), +// } +// } else { +// let parse_val = if let Some(parse) = &arg.parse { +// quote! { +// #parse(arg_val, matches) +// } +// } else { +// quote! { +// ::rpc_toolkit::command_helpers::prelude::default_arg_parser(arg_val, matches) +// } +// }; +// if arg.optional { +// quote! { +// #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { +// Some(#parse_val?) +// } else { +// None +// }, +// } +// } else if let Some(default) = &arg.default { +// if let Some(default) = default { +// let path: Path = match syn::parse_str(&default.value()) { +// Ok(a) => a, +// Err(e) => return e.into_compile_error(), +// }; +// quote! { +// #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { +// #parse_val? +// } else { +// #path() +// }, +// } +// } else { +// quote! { +// #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { +// #parse_val? +// } else { +// Default::default() +// }, +// } +// } +// } else if arg.multiple.is_some() { +// quote! { +// #field_name: matches.values_of(#arg_name).iter().flatten().map(|arg_val| #parse_val).collect::>()?, +// } +// } else { +// quote! { +// #field_name: { +// let arg_val = matches.value_of(#arg_name).unwrap(); +// #parse_val? +// }, +// } +// } +// } +// }); +// let param_struct_def = quote! { +// #[derive(::rpc_toolkit::command_helpers::prelude::Serialize)] +// struct Params#param_ty_generics { +// #( +// #arg_def +// )* +// #[serde(flatten)] +// rest: ParentParams, +// } +// let params: Params#param_ty_generics = Params { +// #( +// #arg +// )* +// rest: parent_params, +// }; +// }; +// let create_rt = quote! { +// let rt_ref = if let Some(rt) = rt.as_mut() { +// &*rt +// } else { +// rt = Some(::rpc_toolkit::command_helpers::prelude::Runtime::new().map_err(|e| ::rpc_toolkit::command_helpers::prelude::RpcError { +// data: Some(format!("{}", e).into()), +// ..::rpc_toolkit::command_helpers::prelude::yajrc::INTERNAL_ERROR +// })?); +// rt.as_ref().unwrap() +// }; +// }; +// let display = if let Some(display) = &opt.common().display { +// quote! { #display } +// } else { +// quote! { ::rpc_toolkit::command_helpers::prelude::default_display } +// }; +// match opt { +// Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::RpcOnly(_)) => quote! { +// pub fn cli_handler#generics( +// _ctx: GenericContext, +// _parent_data: #parent_data_ty, +// _rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, +// _matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, +// method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, +// _parent_params: ParentParams, +// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { +// Err(::rpc_toolkit::command_helpers::prelude::RpcError { +// data: Some(method.into()), +// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR +// }) +// } +// }, +// Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::Standard) => { +// let param = param.into_iter().map(|_| quote! { unreachable!() }); +// let invocation = if opt.is_async { +// quote! { +// rt_ref.block_on(#fn_path(#(#param),*))? +// } +// } else { +// quote! { +// #fn_path(#(#param),*)? +// } +// }; +// quote! { +// pub fn cli_handler#generics( +// ctx: GenericContext, +// parent_data: #parent_data_ty, +// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, +// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, +// method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, +// parent_params: ParentParams, +// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { +// #param_struct_def + +// #create_rt + +// #[allow(unreachable_code)] +// let return_ty = if true { +// ::rpc_toolkit::command_helpers::prelude::PhantomData +// } else { +// let ctx_new = unreachable!(); +// ::rpc_toolkit::command_helpers::prelude::match_types(&ctx, &ctx_new); +// let ctx = ctx_new; +// ::rpc_toolkit::command_helpers::prelude::make_phantom(#invocation) +// }; + +// let res = rt_ref.block_on(::rpc_toolkit::command_helpers::prelude::call_remote(ctx, method.as_ref(), params, return_ty))?; +// Ok(#display(res.result?, matches)) +// } +// } +// } +// Options::Leaf(opt) => { +// if let ExecutionContext::CustomCli { +// ref cli, is_async, .. +// } = opt.exec_ctx +// { +// let fn_path = cli; +// let cli_param = params.iter().filter_map(|param| match param { +// ParamType::Arg(arg) => { +// let name = arg.name.clone().unwrap(); +// let field_name = Ident::new(&format!("arg_{}", name), name.span()); +// Some(quote! { params.#field_name.clone() }) +// } +// ParamType::Context(_) => Some(quote! { Into::into(ctx) }), +// ParamType::ParentData(_) => Some(quote! { parent_data }), +// ParamType::Request => None, +// ParamType::Response => None, +// ParamType::None => unreachable!(), +// }); +// let invocation = if is_async { +// quote! { +// rt_ref.block_on(#fn_path(#(#cli_param),*))? +// } +// } else { +// quote! { +// #fn_path(#(#cli_param),*)? +// } +// }; +// let display_res = if let Some(display_fn) = &opt.display { +// quote! { +// #display_fn(#invocation, matches) +// } +// } else { +// quote! { +// ::rpc_toolkit::command_helpers::prelude::default_display(#invocation, matches) +// } +// }; +// let rt_action = if is_async { +// create_rt +// } else { +// quote! { +// drop(rt); +// } +// }; +// quote! { +// pub fn cli_handler#generics( +// ctx: GenericContext, +// parent_data: #parent_data_ty, +// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, +// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, +// _method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, +// parent_params: ParentParams +// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { +// #param_struct_def + +// #rt_action + +// Ok(#display_res) +// } +// } +// } else { +// let invocation = if opt.is_async { +// quote! { +// rt_ref.block_on(#fn_path(#(#param),*))? +// } +// } else { +// quote! { +// #fn_path(#(#param),*)? +// } +// }; +// let display_res = if let Some(display_fn) = &opt.display { +// quote! { +// #display_fn(#invocation, matches) +// } +// } else { +// quote! { +// ::rpc_toolkit::command_helpers::prelude::default_display(#invocation, matches) +// } +// }; +// let rt_action = if opt.is_async { +// create_rt +// } else { +// quote! { +// drop(rt); +// } +// }; +// quote! { +// pub fn cli_handler#generics( +// ctx: GenericContext, +// parent_data: #parent_data_ty, +// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, +// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, +// _method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, +// parent_params: ParentParams +// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { +// #param_struct_def + +// #rt_action + +// Ok(#display_res) +// } +// } +// } +// } +// Options::Parent(ParentOptions { +// common, +// subcommands, +// self_impl, +// }) => { +// let cmd_preprocess = if common.is_async { +// quote! { +// #create_rt +// let parent_data = rt_ref.block_on(#fn_path(#(#param),*))?; +// } +// } else { +// quote! { +// let parent_data = #fn_path(#(#param),*)?; +// } +// }; +// let subcmd_impl = subcommands.iter().map(|subcommand| { +// let mut subcommand = subcommand.clone(); +// let mut cli_handler = PathSegment { +// ident: Ident::new("cli_handler", Span::call_site()), +// arguments: std::mem::replace( +// &mut subcommand.segments.last_mut().unwrap().arguments, +// PathArguments::None, +// ), +// }; +// cli_handler.arguments = match cli_handler.arguments { +// PathArguments::None => PathArguments::AngleBracketed( +// syn::parse2(quote! { :: }) +// .unwrap(), +// ), +// PathArguments::AngleBracketed(mut a) => { +// a.args +// .push(syn::parse2(quote! { Params#param_ty_generics }).unwrap()); +// a.args.push(syn::parse2(quote! { GenericContext }).unwrap()); +// PathArguments::AngleBracketed(a) +// } +// _ => unreachable!(), +// }; +// quote_spanned! { subcommand.span() => +// Some((#subcommand::NAME, sub_m)) => { +// let method = if method.is_empty() { +// #subcommand::NAME.into() +// } else { +// method + "." + #subcommand::NAME +// }; +// #subcommand::#cli_handler(ctx, parent_data, rt, sub_m, method, params) +// }, +// } +// }); +// let self_impl = match (self_impl, &common.exec_ctx) { +// (Some(self_impl), ExecutionContext::CliOnly(_)) +// | (Some(self_impl), ExecutionContext::Local(_)) +// | (Some(self_impl), ExecutionContext::CustomCli { .. }) => { +// let (self_impl_fn, is_async) = +// if let ExecutionContext::CustomCli { cli, is_async, .. } = &common.exec_ctx +// { +// (cli, *is_async) +// } else { +// (&self_impl.path, self_impl.is_async) +// }; +// let create_rt = if common.is_async { +// None +// } else { +// Some(create_rt) +// }; +// let self_impl = if is_async { +// quote_spanned! { self_impl_fn.span() => +// #create_rt +// rt_ref.block_on(#self_impl_fn(Into::into(ctx), parent_data))? +// } +// } else { +// quote_spanned! { self_impl_fn.span() => +// #self_impl_fn(Into::into(ctx), parent_data)? +// } +// }; +// quote! { +// Ok(#display(#self_impl, matches)), +// } +// } +// (Some(self_impl), ExecutionContext::Standard) => { +// let self_impl_fn = &self_impl.path; +// let self_impl = if self_impl.is_async { +// quote! { +// rt_ref.block_on(#self_impl_fn(unreachable!(), parent_data)) +// } +// } else { +// quote! { +// #self_impl_fn(unreachable!(), parent_data) +// } +// }; +// let create_rt = if common.is_async { +// None +// } else { +// Some(create_rt) +// }; +// quote! { +// { +// #create_rt + +// #[allow(unreachable_code)] +// let return_ty = if true { +// ::rpc_toolkit::command_helpers::prelude::PhantomData +// } else { +// ::rpc_toolkit::command_helpers::prelude::make_phantom(#self_impl?) +// }; + +// let res = rt_ref.block_on(::rpc_toolkit::command_helpers::prelude::call_remote(ctx, method.as_ref(), params, return_ty))?; +// Ok(#display(res.result?, matches)) +// } +// } +// } +// (None, _) | (Some(_), ExecutionContext::RpcOnly(_)) => quote! { +// Err(::rpc_toolkit::command_helpers::prelude::RpcError { +// data: Some(method.into()), +// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR +// }), +// }, +// }; +// quote! { +// pub fn cli_handler#generics( +// ctx: GenericContext, +// parent_data: #parent_data_ty, +// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, +// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, +// method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, +// parent_params: ParentParams, +// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { +// #param_struct_def + +// #cmd_preprocess + +// match matches.subcommand() { +// #( +// #subcmd_impl +// )* +// _ => #self_impl +// } +// } +// } +// } +// } +// } + +fn build_params(params: Vec, generics: &Generics) -> (TokenStream, Generics) { + let mut param_generics_filter = GenericFilter::new(generics); + for param in ¶ms { + param_generics_filter.fold_type(param.ty.clone()); } let param_generics = param_generics_filter.finish(); - let (_, param_ty_generics, _) = param_generics.split_for_impl(); - let param_struct_def = quote! { - #[allow(dead_code)] - #[derive(::rpc_toolkit::command_helpers::prelude::Deserialize)] - pub struct Params#param_ty_generics { - #( - #param_def - )* - #[serde(flatten)] - #[serde(default)] - rest: ::rpc_toolkit::command_helpers::prelude::Value, + let (impl_generics, ty_generics, where_clause) = param_generics.split_for_impl(); + let param_arg = params.iter().enumerate().map(|(idx, p)| { + let mut res = TokenStream::new(); + let p_ty = &p.ty; + if let Some(rename) = &p.rename { + res.extend(quote! { #[serde(rename = #rename)] }); + } else if let Some(name) = &p.name { + let name = LitStr::new(&name.to_string(), name.span()); + res.extend(quote! { #[serde(rename = #name)] }); + }; + if let Some(default) = &p.default { + res.extend(quote! { #[serde(#default)] }); } - }; - let param = params.iter().map(|param| match param { - ParamType::Arg(arg) => { - let name = arg.name.clone().unwrap(); - let field_name = Ident::new(&format!("arg_{}", name), name.span()); - quote! { args.#field_name } - } - ParamType::Context(ty) => { - if matches!(opt, Options::Parent { .. }) { - quote! { >::into(ctx.clone()) } - } else { - quote! { >::into(ctx) } - } - } - ParamType::ParentData(_) => { - quote! { parent_data } - } - ParamType::Request => quote! { request }, - ParamType::Response => quote! { response }, - ParamType::None => unreachable!(), + let arg_ident = Ident::new(&format!("arg_{idx}"), Span::call_site()); + let p_name = p.name.as_ref().unwrap_or(&arg_ident); + res.extend(quote! { pub #p_name: #p_ty, }); + res }); - match opt { - Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::CliOnly(_)) => quote! { - #param_struct_def - - pub async fn rpc_handler#generics( - _ctx: GenericContext, - _parent_data: #parent_data_ty, - _request: &::rpc_toolkit::command_helpers::prelude::RequestParts, - _response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, - method: &str, - _args: Params#param_ty_generics, - ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { - Err(::rpc_toolkit::command_helpers::prelude::RpcError { - data: Some(method.into()), - ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR - }) - } - }, - Options::Leaf(opt) => { - let invocation = if opt.is_async { - quote! { - #fn_path(#(#param),*).await? - } - } else if opt.blocking.is_some() { - quote! { - ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #fn_path(#(#param),*)).await? - } + let (clap_param_arg, clap_param_from_matches): (Vec<_>, Vec<_>) = params + .iter() + .enumerate() + .map(|(idx, p)| { + let (mut arg, mut from_matches) = (TokenStream::new(), TokenStream::new()); + let arg_ident = Ident::new(&format!("arg_{idx}"), Span::call_site()); + let p_name = p + .name + .as_ref() + .unwrap_or(&arg_ident); + if p.stdin.is_some() { + let parser = p + .parse.as_ref() + .map(|p| quote! { #p }) + .unwrap_or(quote!(::rpc_toolkit::command_helpers::default_stdin_parser)); + from_matches.extend(quote! { #p_name: #parser(&mut std::io::stdin(), matches)? }); + } else if matches!(&p.ty, Type::Path(p) if p.path.is_ident("bool")) { + arg.extend(if p.clap_attr.is_empty() { + quote! { #[arg] } + } else { + let clap_attr = &p.clap_attr; + quote! { #[arg(#(#clap_attr),*)] } + }); + arg.extend(quote! { #p_name: bool, }); + from_matches.extend(quote! { #p_name: clap_args.#p_name, }); + } else if matches!(&p.ty, Type::Path(p) if p.path.segments.first().unwrap().ident == "Option") { + let parser = p + .parse.as_ref() + .map(|p| quote!(#p)) + .unwrap_or(quote!(::rpc_toolkit::command_helpers::default_arg_parser)); + arg.extend(if p.clap_attr.is_empty() { + quote! { #[arg] } + } else { + let clap_attr = &p.clap_attr; + quote! { #[arg(#(#clap_attr),*)] } + }); + arg.extend(quote! { #p_name: Option, }); + from_matches.extend(quote! { #p_name: clap_args.#p_name.as_ref().map(|arg_str| #parser(arg_str, matches)).transpose()?, }); } else { - quote! { - #fn_path(#(#param),*)? - } - }; - quote! { - #param_struct_def - - pub async fn rpc_handler#generics( - ctx: GenericContext, - parent_data: #parent_data_ty, - request: &::rpc_toolkit::command_helpers::prelude::RequestParts, - response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, - method: &str, - args: Params#param_ty_generics, - ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { - if method.is_empty() { - Ok(::rpc_toolkit::command_helpers::prelude::to_value(#invocation)?) - } else { - Err(::rpc_toolkit::command_helpers::prelude::RpcError { - data: Some(method.into()), - ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR - }) - } - } + let parser = p + .parse.as_ref() + .map(|p| quote!(#p)) + .unwrap_or(quote!(::rpc_toolkit::command_helpers::default_arg_parser)); + arg.extend(if p.clap_attr.is_empty() { + quote! { #[arg] } + } else { + let clap_attr = &p.clap_attr; + quote! { #[arg(#(#clap_attr),*)] } + }); + arg.extend(quote! { #p_name: String, }); + from_matches.extend(quote! { #p_name: #parser(&clap_args.#p_name, matches)?, }); } - } - Options::Parent(ParentOptions { - common, - subcommands, - self_impl, - }) => { - let cmd_preprocess = if common.is_async { - quote! { - let parent_data = #fn_path(#(#param),*).await?; - } - } else if common.blocking.is_some() { - quote! { - let parent_data = ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #fn_path(#(#param),*)).await?; - } - } else { - quote! { - let parent_data = #fn_path(#(#param),*)?; - } - }; - let subcmd_impl = subcommands.iter().map(|subcommand| { - let mut subcommand = subcommand.clone(); - let mut rpc_handler = PathSegment { - ident: Ident::new("rpc_handler", Span::call_site()), - arguments: std::mem::replace( - &mut subcommand.segments.last_mut().unwrap().arguments, - PathArguments::None, - ), - }; - rpc_handler.arguments = match rpc_handler.arguments { - PathArguments::None => PathArguments::AngleBracketed( - syn::parse2(quote! { :: }) - .unwrap(), - ), - PathArguments::AngleBracketed(mut a) => { - a.args.push(syn::parse2(quote! { GenericContext }).unwrap()); - PathArguments::AngleBracketed(a) - } - _ => unreachable!(), - }; - quote_spanned!{ subcommand.span() => - [#subcommand::NAME, rest] => #subcommand::#rpc_handler(ctx, parent_data, request, response, rest, ::rpc_toolkit::command_helpers::prelude::from_value(args.rest)?).await - } - }); - let subcmd_impl = quote! { - match method.splitn(2, ".").chain(std::iter::repeat("")).take(2).collect::>().as_slice() { - #( - #subcmd_impl, - )* - _ => Err(::rpc_toolkit::command_helpers::prelude::RpcError { - data: Some(method.into()), - ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR + (arg, from_matches) + }) + .unzip(); + ( + quote! { + #[derive(::rpc_toolkit::serde::Serialize, ::rpc_toolkit::serde::Deserialize)] + pub struct Params #ty_generics { + #( + #param_arg + )* + } + #[derive(::rpc_toolkit::clap::Parser)] + struct ClapParams { + #( + #clap_param_arg + )* + } + impl #impl_generics ::rpc_toolkit::command_helpers::clap::FromArgMatches for Params #ty_generics #where_clause { + fn from_arg_matches(matches: &::rpc_toolkit::command_helpers::clap::ArgMatches) -> Result { + let clap_args = ClapParams::from_arg_matches(matches)?; + Ok(Self { + #( + #clap_param_from_matches + )* }) } - }; - match self_impl { - Some(self_impl) if !matches!(common.exec_ctx, ExecutionContext::CliOnly(_)) => { - let self_impl_fn = &self_impl.path; - let self_impl = if self_impl.is_async { - quote_spanned! { self_impl_fn.span() => - #self_impl_fn(Into::into(ctx), parent_data).await? - } - } else if self_impl.blocking { - quote_spanned! { self_impl_fn.span() => - { - let ctx = Into::into(ctx); - ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #self_impl_fn(ctx, parent_data)).await? - } - } - } else { - quote_spanned! { self_impl_fn.span() => - #self_impl_fn(Into::into(ctx), parent_data)? - } - }; - quote! { - #param_struct_def - - pub async fn rpc_handler#generics( - ctx: GenericContext, - parent_data: #parent_data_ty, - request: &::rpc_toolkit::command_helpers::prelude::RequestParts, - response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, - method: &str, - args: Params#param_ty_generics, - ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { - #cmd_preprocess - - if method.is_empty() { - Ok(::rpc_toolkit::command_helpers::prelude::to_value(&#self_impl)?) - } else { - #subcmd_impl - } - } - } - } - _ => { - quote! { - #param_struct_def - - pub async fn rpc_handler#generics( - ctx: GenericContext, - parent_data: #parent_data_ty, - request: &::rpc_toolkit::command_helpers::prelude::RequestParts, - response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, - method: &str, - args: Params#param_ty_generics, - ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { - #cmd_preprocess - - #subcmd_impl - } - } + fn update_from_arg_matches(&mut self, matches: &::rpc_toolkit::command_helpers::clap::ArgMatches) -> Result<(), ::rpc_toolkit::command_helpers::clap::Error> { + unimplemented!() } } - } - } -} - -fn cli_handler( - fn_name: &Ident, - fn_generics: &Generics, - opt: &mut Options, - params: &[ParamType], -) -> TokenStream { - let mut parent_data_ty = quote! { () }; - let mut generics = fn_generics.clone(); - generics.params.push(macro_try!(syn::parse2( - quote! { ParentParams: ::rpc_toolkit::command_helpers::prelude::Serialize } - ))); - generics.params.push(macro_try!(syn::parse2( - quote! { GenericContext: CommandContextCli } - ))); - if generics.lt_token.is_none() { - generics.lt_token = Some(Default::default()); - } - if generics.gt_token.is_none() { - generics.gt_token = Some(Default::default()); - } - let (_, fn_type_generics, _) = fn_generics.split_for_impl(); - let fn_turbofish = fn_type_generics.as_turbofish(); - let fn_path: Path = macro_try!(syn::parse2(quote! { super::#fn_name#fn_turbofish })); - let is_parent = matches!(opt, Options::Parent { .. }); - let param: Vec<_> = params - .iter() - .map(|param| match param { - ParamType::Arg(arg) => { - let name = arg.name.clone().unwrap(); - let field_name = Ident::new(&format!("arg_{}", name), name.span()); - quote! { params.#field_name.clone() } - } - ParamType::Context(ty) => { - if is_parent { - quote! { >::into(ctx.clone()) } - } else { - quote! { >::into(ctx) } + impl #impl_generics ::rpc_toolkit::command_helpers::clap::CommandFactory for Params #ty_generics #where_clause { + fn command() -> ::rpc_toolkit::command_helpers::clap::Command { + ClapParams::command() } - } - ParamType::ParentData(ty) => { - parent_data_ty = quote! { #ty }; - quote! { parent_data } - } - ParamType::Request => quote! { request }, - ParamType::Response => quote! { response }, - ParamType::None => unreachable!(), - }) - .collect(); - let mut param_generics_filter = GenericFilter::new(fn_generics); - for param in params { - if let ParamType::Arg(a) = param { - param_generics_filter.fold_type(a.ty.clone()); - } - } - let mut param_generics = param_generics_filter.finish(); - param_generics.params.push(macro_try!(syn::parse2(quote! { - ParentParams: ::rpc_toolkit::command_helpers::prelude::Serialize - }))); - if param_generics.lt_token.is_none() { - param_generics.lt_token = Some(Default::default()); - } - if param_generics.gt_token.is_none() { - param_generics.gt_token = Some(Default::default()); - } - let (_, param_ty_generics, _) = param_generics.split_for_impl(); - let mut arg_def = Vec::new(); - for param in params { - match param { - ParamType::Arg(arg) => { - let name = arg.name.clone().unwrap(); - let rename = arg - .rename - .clone() - .unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); - let field_name = Ident::new(&format!("arg_{}", name), name.span()); - let ty = arg.ty.clone(); - arg_def.push(quote! { - #[serde(rename = #rename)] - #field_name: #ty, - }) - } - _ => (), - } - } - let arg = params - .iter() - .filter_map(|param| { - if let ParamType::Arg(a) = param { - Some(a) - } else { - None - } - }) - .map(|arg| { - let name = arg.name.clone().unwrap(); - let arg_name = arg.rename.clone().unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); - let field_name = Ident::new(&format!("arg_{}", name), name.span()); - if arg.stdin.is_some() { - if let Some(parse) = &arg.parse { - quote! { - #field_name: #parse(&mut std::io::stdin(), matches)?, - } - } else { - quote! { - #field_name: ::rpc_toolkit::command_helpers::prelude::default_stdin_parser(&mut std::io::stdin(), matches)?, - } + fn command_for_update() -> ::rpc_toolkit::command_helpers::clap::Command { + ClapParams::command_for_update() } - } else if arg.check_is_present { - quote! { - #field_name: matches.is_present(#arg_name), - } - } else if arg.count.is_some() { - quote! { - #field_name: matches.occurrences_of(#arg_name), - } - } else { - let parse_val = if let Some(parse) = &arg.parse { - quote! { - #parse(arg_val, matches) - } - } else { - quote! { - ::rpc_toolkit::command_helpers::prelude::default_arg_parser(arg_val, matches) - } - }; - if arg.optional { - quote! { - #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { - Some(#parse_val?) - } else { - None - }, - } - } else if let Some(default) = &arg.default { - if let Some(default) = default { - let path: Path = match syn::parse_str(&default.value()) { - Ok(a) => a, - Err(e) => return e.into_compile_error(), - }; - quote! { - #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { - #parse_val? - } else { - #path() - }, - } - } else { - quote! { - #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { - #parse_val? - } else { - Default::default() - }, - } - } - } else if arg.multiple.is_some() { - quote! { - #field_name: matches.values_of(#arg_name).iter().flatten().map(|arg_val| #parse_val).collect::>()?, - } - } else { - quote! { - #field_name: { - let arg_val = matches.value_of(#arg_name).unwrap(); - #parse_val? - }, - } - } - } - }); - let param_struct_def = quote! { - #[derive(::rpc_toolkit::command_helpers::prelude::Serialize)] - struct Params#param_ty_generics { - #( - #arg_def - )* - #[serde(flatten)] - rest: ParentParams, - } - let params: Params#param_ty_generics = Params { - #( - #arg - )* - rest: parent_params, - }; - }; - let create_rt = quote! { - let rt_ref = if let Some(rt) = rt.as_mut() { - &*rt - } else { - rt = Some(::rpc_toolkit::command_helpers::prelude::Runtime::new().map_err(|e| ::rpc_toolkit::command_helpers::prelude::RpcError { - data: Some(format!("{}", e).into()), - ..::rpc_toolkit::command_helpers::prelude::yajrc::INTERNAL_ERROR - })?); - rt.as_ref().unwrap() - }; - }; - let display = if let Some(display) = &opt.common().display { - quote! { #display } - } else { - quote! { ::rpc_toolkit::command_helpers::prelude::default_display } - }; - match opt { - Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::RpcOnly(_)) => quote! { - pub fn cli_handler#generics( - _ctx: GenericContext, - _parent_data: #parent_data_ty, - _rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, - _matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, - method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, - _parent_params: ParentParams, - ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { - Err(::rpc_toolkit::command_helpers::prelude::RpcError { - data: Some(method.into()), - ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR - }) } }, - Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::Standard) => { - let param = param.into_iter().map(|_| quote! { unreachable!() }); - let invocation = if opt.is_async { - quote! { - rt_ref.block_on(#fn_path(#(#param),*))? - } - } else { - quote! { - #fn_path(#(#param),*)? - } - }; + param_generics, + ) +} + +fn build_inherited(parent_data: Option, generics: &Generics) -> (TokenStream, Generics) { + let mut inherited_generics_filter = GenericFilter::new(generics); + if let Some(inherited) = &parent_data { + inherited_generics_filter.fold_type(inherited.clone()); + } + let inherited_generics = inherited_generics_filter.finish(); + if let Some(inherited) = parent_data { + let (_, ty_generics, _) = inherited_generics.split_for_impl(); + ( quote! { - pub fn cli_handler#generics( - ctx: GenericContext, - parent_data: #parent_data_ty, - mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, - matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, - method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, - parent_params: ParentParams, - ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { - #param_struct_def - - #create_rt - - #[allow(unreachable_code)] - let return_ty = if true { - ::rpc_toolkit::command_helpers::prelude::PhantomData - } else { - let ctx_new = unreachable!(); - ::rpc_toolkit::command_helpers::prelude::match_types(&ctx, &ctx_new); - let ctx = ctx_new; - ::rpc_toolkit::command_helpers::prelude::make_phantom(#invocation) - }; - - let res = rt_ref.block_on(::rpc_toolkit::command_helpers::prelude::call_remote(ctx, method.as_ref(), params, return_ty))?; - Ok(#display(res.result?, matches)) - } - } - } - Options::Leaf(opt) => { - if let ExecutionContext::CustomCli { - ref cli, is_async, .. - } = opt.exec_ctx - { - let fn_path = cli; - let cli_param = params.iter().filter_map(|param| match param { - ParamType::Arg(arg) => { - let name = arg.name.clone().unwrap(); - let field_name = Ident::new(&format!("arg_{}", name), name.span()); - Some(quote! { params.#field_name.clone() }) - } - ParamType::Context(_) => Some(quote! { Into::into(ctx) }), - ParamType::ParentData(_) => Some(quote! { parent_data }), - ParamType::Request => None, - ParamType::Response => None, - ParamType::None => unreachable!(), - }); - let invocation = if is_async { - quote! { - rt_ref.block_on(#fn_path(#(#cli_param),*))? - } - } else { - quote! { - #fn_path(#(#cli_param),*)? - } - }; - let display_res = if let Some(display_fn) = &opt.display { - quote! { - #display_fn(#invocation, matches) - } - } else { - quote! { - ::rpc_toolkit::command_helpers::prelude::default_display(#invocation, matches) - } - }; - let rt_action = if is_async { - create_rt - } else { - quote! { - drop(rt); - } - }; - quote! { - pub fn cli_handler#generics( - ctx: GenericContext, - parent_data: #parent_data_ty, - mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, - matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, - _method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, - parent_params: ParentParams - ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { - #param_struct_def - - #rt_action - - Ok(#display_res) - } - } - } else { - let invocation = if opt.is_async { - quote! { - rt_ref.block_on(#fn_path(#(#param),*))? - } - } else { - quote! { - #fn_path(#(#param),*)? - } - }; - let display_res = if let Some(display_fn) = &opt.display { - quote! { - #display_fn(#invocation, matches) - } - } else { - quote! { - ::rpc_toolkit::command_helpers::prelude::default_display(#invocation, matches) - } - }; - let rt_action = if opt.is_async { - create_rt - } else { - quote! { - drop(rt); - } - }; - quote! { - pub fn cli_handler#generics( - ctx: GenericContext, - parent_data: #parent_data_ty, - mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, - matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, - _method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, - parent_params: ParentParams - ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { - #param_struct_def - - #rt_action - - Ok(#display_res) - } - } - } - } - Options::Parent(ParentOptions { - common, - subcommands, - self_impl, - }) => { - let cmd_preprocess = if common.is_async { - quote! { - #create_rt - let parent_data = rt_ref.block_on(#fn_path(#(#param),*))?; - } - } else { - quote! { - let parent_data = #fn_path(#(#param),*)?; - } - }; - let subcmd_impl = subcommands.iter().map(|subcommand| { - let mut subcommand = subcommand.clone(); - let mut cli_handler = PathSegment { - ident: Ident::new("cli_handler", Span::call_site()), - arguments: std::mem::replace( - &mut subcommand.segments.last_mut().unwrap().arguments, - PathArguments::None, - ), - }; - cli_handler.arguments = match cli_handler.arguments { - PathArguments::None => PathArguments::AngleBracketed( - syn::parse2(quote! { :: }) - .unwrap(), - ), - PathArguments::AngleBracketed(mut a) => { - a.args - .push(syn::parse2(quote! { Params#param_ty_generics }).unwrap()); - a.args.push(syn::parse2(quote! { GenericContext }).unwrap()); - PathArguments::AngleBracketed(a) - } - _ => unreachable!(), - }; - quote_spanned! { subcommand.span() => - Some((#subcommand::NAME, sub_m)) => { - let method = if method.is_empty() { - #subcommand::NAME.into() - } else { - method + "." + #subcommand::NAME - }; - #subcommand::#cli_handler(ctx, parent_data, rt, sub_m, method, params) - }, - } - }); - let self_impl = match (self_impl, &common.exec_ctx) { - (Some(self_impl), ExecutionContext::CliOnly(_)) - | (Some(self_impl), ExecutionContext::Local(_)) - | (Some(self_impl), ExecutionContext::CustomCli { .. }) => { - let (self_impl_fn, is_async) = - if let ExecutionContext::CustomCli { cli, is_async, .. } = &common.exec_ctx - { - (cli, *is_async) - } else { - (&self_impl.path, self_impl.is_async) - }; - let create_rt = if common.is_async { - None - } else { - Some(create_rt) - }; - let self_impl = if is_async { - quote_spanned! { self_impl_fn.span() => - #create_rt - rt_ref.block_on(#self_impl_fn(Into::into(ctx), parent_data))? - } - } else { - quote_spanned! { self_impl_fn.span() => - #self_impl_fn(Into::into(ctx), parent_data)? - } - }; - quote! { - Ok(#display(#self_impl, matches)), - } - } - (Some(self_impl), ExecutionContext::Standard) => { - let self_impl_fn = &self_impl.path; - let self_impl = if self_impl.is_async { - quote! { - rt_ref.block_on(#self_impl_fn(unreachable!(), parent_data)) - } - } else { - quote! { - #self_impl_fn(unreachable!(), parent_data) - } - }; - let create_rt = if common.is_async { - None - } else { - Some(create_rt) - }; - quote! { - { - #create_rt - - #[allow(unreachable_code)] - let return_ty = if true { - ::rpc_toolkit::command_helpers::prelude::PhantomData - } else { - ::rpc_toolkit::command_helpers::prelude::make_phantom(#self_impl?) - }; - - let res = rt_ref.block_on(::rpc_toolkit::command_helpers::prelude::call_remote(ctx, method.as_ref(), params, return_ty))?; - Ok(#display(res.result?, matches)) - } - } - } - (None, _) | (Some(_), ExecutionContext::RpcOnly(_)) => quote! { - Err(::rpc_toolkit::command_helpers::prelude::RpcError { - data: Some(method.into()), - ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR - }), - }, - }; - quote! { - pub fn cli_handler#generics( - ctx: GenericContext, - parent_data: #parent_data_ty, - mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, - matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, - method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, - parent_params: ParentParams, - ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { - #param_struct_def - - #cmd_preprocess - - match matches.subcommand() { - #( - #subcmd_impl - )* - _ => #self_impl - } - } - } - } + type InheritedParams #ty_generics = #inherited; + }, + inherited_generics, + ) + } else { + ( + quote! { type InheritedParams = ::rpc_toolkit::NoParams; }, + inherited_generics, + ) } } pub fn build(args: AttributeArgs, mut item: ItemFn) -> TokenStream { - let mut params = macro_try!(parse_param_attrs(&mut item)); + let params = macro_try!(parse_param_attrs(&mut item)); let mut opt = macro_try!(parse_command_attr(args)); - if let Some(a) = &opt.common().blocking { - if item.sig.asyncness.is_some() { - return Error::new(a.span(), "cannot use `blocking` on an async fn").to_compile_error(); - } - } - opt.common().is_async = item.sig.asyncness.is_some(); let fn_vis = &item.vis; let fn_name = &item.sig.ident; - let fn_generics = &item.sig.generics; - let command_name_str = opt + let (params_impl, params_generics) = build_params( + params + .iter() + .filter_map(|a| { + if let ParamType::Arg(a) = a { + Some(a.clone()) + } else { + None + } + }) + .collect(), + &item.sig.generics, + ); + let (_, params_ty_generics, _) = params_generics.split_for_impl(); + let (inherited_impl, inherited_generics) = build_inherited( + params.iter().find_map(|a| { + if let ParamType::ParentData(a) = a { + Some(a.clone()) + } else { + None + } + }), + &item.sig.generics, + ); + let (_, inherited_ty_generics, _) = inherited_generics.split_for_impl(); + let mut params_and_inherited_generics_filter = GenericFilter::new(&item.sig.generics); + params_and_inherited_generics_filter.fold_generics(params_generics.clone()); + params_and_inherited_generics_filter.fold_generics(inherited_generics.clone()); + let params_and_inherited_generics = params_and_inherited_generics_filter.finish(); + let (_, params_and_inherited_ty_generics, _) = params_and_inherited_generics.split_for_impl(); + let phantom_ty = params_and_inherited_generics + .type_params() + .map(|t| &t.ident) + .map(|t| quote! { #t }) + .chain( + params_and_inherited_generics + .lifetimes() + .map(|l| &l.lifetime) + .map(|l| quote! { &#l () }), + ); + let phantom_ty = quote! { ( #( #phantom_ty, )* ) }; + let module = if let Options::Parent(parent) = &opt { + // let subcommands = parent.subcommands.iter().map(|c| quote! { .subcommand_with_inherited_remote_cli(c::handler(), |a, b| ) }) + // quote! { + // pub type Handler #params_and_inherited_ty_generics = ::rpc_toolkit::ParentHandler; + // #params_impl + // #inherited_impl + // pub fn handler #params_and_inherited_ty_generics () -> Handler #params_and_inherited_ty_generics { + // Handler::new() + // #( + // #subcommands + // )* + // } + // } + Error::new(Span::call_site(), "derived parent handlers not implemented").to_compile_error() + } else { + let (ok_ty, err_ty) = match &item.sig.output { + ReturnType::Type(_, p) => match &**p { + Type::Path(p) if p.path.segments.last().unwrap().ident == "Result" => { + match &p.path.segments.last().unwrap().arguments { + PathArguments::AngleBracketed(a) if a.args.len() == 2 => { + match (a.args.first(), a.args.last()) { + ( + Some(GenericArgument::Type(ok)), + Some(GenericArgument::Type(err)), + ) => (ok, err), + _ => { + return Error::new(a.span(), "return type must be a Result") + .to_compile_error() + } + } + } + a => { + return Error::new(a.span(), "return type must be a Result") + .to_compile_error() + } + } + } + a => { + return Error::new(a.span(), "return type must be a Result").to_compile_error(); + } + }, + a => return Error::new(a.span(), "return type must be a Result").to_compile_error(), + }; + let handler_impl: TokenStream = todo!(); + let cli_bindings_impl: TokenStream = todo!(); + quote! { + pub struct Handler #params_and_inherited_ty_generics (::core::marker::PhantomData<#phantom_ty>); + impl #params_and_inherited_ty_generics ::core::fmt::Debug for Handler #params_and_inherited_ty_generics { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + f.debug_tuple("Handler") + .finish() + } + } + impl #params_and_inherited_ty_generics ::core::clone::Clone for Handler #params_and_inherited_ty_generics { + fn clone(&self) -> Self { + Self(::core::marker::PhantomData) + } + } + impl #params_and_inherited_ty_generics ::core::marker::Copy for Handler #params_and_inherited_ty_generics { } + #params_impl + #inherited_impl + impl #params_and_inherited_ty_generics ::rpc_toolkit::HandlerTypes for Handler #params_and_inherited_ty_generics { + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = #ok_ty; + type Err = #err_ty; + } + #handler_impl + #cli_bindings_impl + pub fn handler #params_and_inherited_ty_generics () -> Handler #params_and_inherited_ty_generics { + Handler(::core::marker::PhantomData) + } + } + }; + + let fn_rename = opt .common() .rename .clone() - .unwrap_or_else(|| LitStr::new(&fn_name.to_string(), fn_name.span())); - let is_async = LitBool::new( - opt.common().is_async, - item.sig - .asyncness - .map(|a| a.span()) - .unwrap_or_else(Span::call_site), - ); - let ctx_ty = params.iter().find_map(|a| { - if let ParamType::Context(ty) = a { - Some(ty.clone()) - } else { - None - } - }); - let ctx_trait = ctx_trait(ctx_ty, &mut opt); - let metadata = metadata(&mut opt); - let build_app = build_app(command_name_str.clone(), &mut opt, &mut params); - let rpc_handler = rpc_handler(fn_name, fn_generics, &opt, ¶ms); - let cli_handler = cli_handler(fn_name, fn_generics, &mut opt, ¶ms); + .unwrap_or(LitStr::new(&fn_name.to_string(), fn_name.span())); let res = quote! { #item #fn_vis mod #fn_name { use super::*; - - pub const NAME: &'static str = #command_name_str; - pub const ASYNC: bool = #is_async; - - #ctx_trait - - #metadata - - #build_app - - #rpc_handler - - #cli_handler + pub const NAME: &str = #fn_rename; + #module } }; - if opt.common().macro_debug { - panic!("EXPANDED MACRO:\n{}", res); + if let Some(debug) = &opt.common().macro_debug { + Error::new(debug.span(), res).into_compile_error() + } else { + res } - res } diff --git a/rpc-toolkit-macro-internals/src/command/mod.rs b/rpc-toolkit-macro-internals/src/command/mod.rs index 3876d78..662064a 100644 --- a/rpc-toolkit-macro-internals/src/command/mod.rs +++ b/rpc-toolkit-macro-internals/src/command/mod.rs @@ -25,7 +25,7 @@ impl Default for ExecutionContext { #[derive(Default)] pub struct LeafOptions { - macro_debug: bool, + macro_debug: Option, blocking: Option, is_async: bool, aliases: Vec, @@ -34,6 +34,7 @@ pub struct LeafOptions { exec_ctx: ExecutionContext, display: Option, metadata: HashMap, + clap_attr: Vec, } pub struct SelfImplInfo { @@ -79,22 +80,18 @@ impl Options { } } +#[derive(Clone)] pub struct ArgOptions { ty: Type, - optional: bool, - check_is_present: bool, - help: Option, name: Option, rename: Option, - short: Option, - long: Option, parse: Option, - default: Option>, - count: Option, - multiple: Option, stdin: Option, + default: Option, + clap_attr: Vec, } +#[derive(Clone)] pub enum ParamType { None, Arg(ArgOptions), diff --git a/rpc-toolkit-macro-internals/src/command/parse.rs b/rpc-toolkit-macro-internals/src/command/parse.rs index fe45a03..c504b7d 100644 --- a/rpc-toolkit-macro-internals/src/command/parse.rs +++ b/rpc-toolkit-macro-internals/src/command/parse.rs @@ -7,7 +7,7 @@ pub fn parse_command_attr(args: AttributeArgs) -> Result { for arg in args { match arg { NestedMeta::Meta(Meta::Path(p)) if p.is_ident("macro_debug") => { - opt.common().macro_debug = true; + opt.common().macro_debug = Some(p); } NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("subcommands") => { let inner = opt.to_parent()?; @@ -536,25 +536,20 @@ pub fn parse_command_attr(args: AttributeArgs) -> Result { pub fn parse_arg_attr(attr: Attribute, arg: PatType) -> Result { let arg_span = arg.span(); + let meta = attr.parse_meta()?; let mut opt = ArgOptions { ty: *arg.ty, - optional: false, - check_is_present: false, - help: None, name: match *arg.pat { Pat::Ident(i) => Some(i.ident), _ => None, }, rename: None, - short: None, - long: None, parse: None, - default: None, - count: None, - multiple: None, stdin: None, + default: None, + clap_attr: Vec::new(), }; - match attr.parse_meta()? { + match meta { Meta::List(list) => { for arg in list.nested { match arg { @@ -591,34 +586,10 @@ pub fn parse_arg_attr(attr: Attribute, arg: PatType) -> Result { return Err(Error::new(nv.path.span(), "`parse` cannot be assigned to")); } NestedMeta::Meta(Meta::Path(p)) if p.is_ident("stdin") => { - if opt.short.is_some() { + if !opt.clap_attr.is_empty() { return Err(Error::new( p.span(), - "`stdin` and `short` are mutually exclusive", - )); - } - if opt.long.is_some() { - return Err(Error::new( - p.span(), - "`stdin` and `long` are mutually exclusive", - )); - } - if opt.help.is_some() { - return Err(Error::new( - p.span(), - "`stdin` and `help` are mutually exclusive", - )); - } - if opt.count.is_some() { - return Err(Error::new( - p.span(), - "`stdin` and `count` are mutually exclusive", - )); - } - if opt.multiple.is_some() { - return Err(Error::new( - p.span(), - "`stdin` and `multiple` are mutually exclusive", + "`stdin` and clap parser attributes are mutually exclusive", )); } opt.stdin = Some(p); @@ -632,79 +603,6 @@ pub fn parse_arg_attr(attr: Attribute, arg: PatType) -> Result { NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("stdin") => { return Err(Error::new(nv.path.span(), "`stdin` cannot be assigned to")); } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("count") => { - if opt.stdin.is_some() { - return Err(Error::new( - p.span(), - "`stdin` and `count` are mutually exclusive", - )); - } - if opt.multiple.is_some() { - return Err(Error::new( - p.span(), - "`count` and `multiple` are mutually exclusive", - )); - } - opt.count = Some(p); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("count") => { - return Err(Error::new( - list.path.span(), - "`count` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("count") => { - return Err(Error::new(nv.path.span(), "`count` cannot be assigned to")); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("multiple") => { - if opt.stdin.is_some() { - return Err(Error::new( - p.span(), - "`stdin` and `multiple` are mutually exclusive", - )); - } - if opt.count.is_some() { - return Err(Error::new( - p.span(), - "`count` and `multiple` are mutually exclusive", - )); - } - opt.multiple = Some(p); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("multiple") => { - return Err(Error::new( - list.path.span(), - "`multiple` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("count") => { - return Err(Error::new(nv.path.span(), "`count` cannot be assigned to")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("help") => { - if let Lit::Str(help) = nv.lit { - if opt.help.is_some() { - return Err(Error::new(help.span(), "duplicate argument `help`")); - } - if opt.stdin.is_some() { - return Err(Error::new( - help.span(), - "`stdin` and `help` are mutually exclusive", - )); - } - opt.help = Some(help); - } else { - return Err(Error::new(nv.lit.span(), "help message must be a string")); - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("help") => { - return Err(Error::new( - list.path.span(), - "`help` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("help") => { - return Err(Error::new(p.span(), "`help` must be assigned to")); - } NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rename") => { if let Lit::Str(rename) = nv.lit { if opt.rename.is_some() { @@ -713,6 +611,12 @@ pub fn parse_arg_attr(attr: Attribute, arg: PatType) -> Result { "duplicate argument `rename`", )); } + opt.clap_attr + .push(NestedMeta::Meta(Meta::NameValue(MetaNameValue { + path: Path::from(Ident::new("name", nv.path.span())), + eq_token: nv.eq_token, + lit: Lit::Str(rename.clone()), + }))); opt.rename = Some(rename); } else { return Err(Error::new(nv.lit.span(), "`rename` must be a string")); @@ -727,68 +631,8 @@ pub fn parse_arg_attr(attr: Attribute, arg: PatType) -> Result { NestedMeta::Meta(Meta::Path(p)) if p.is_ident("rename") => { return Err(Error::new(p.span(), "`rename` must be assigned to")); } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("short") => { - if let Lit::Char(short) = nv.lit { - if opt.short.is_some() { - return Err(Error::new(short.span(), "duplicate argument `short`")); - } - if opt.stdin.is_some() { - return Err(Error::new( - short.span(), - "`stdin` and `short` are mutually exclusive", - )); - } - opt.short = Some(short); - } else { - return Err(Error::new(nv.lit.span(), "`short` must be a char")); - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("short") => { - return Err(Error::new( - list.path.span(), - "`short` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("short") => { - return Err(Error::new(p.span(), "`short` must be assigned to")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("long") => { - if let Lit::Str(long) = nv.lit { - if opt.long.is_some() { - return Err(Error::new(long.span(), "duplicate argument `long`")); - } - if opt.stdin.is_some() { - return Err(Error::new( - long.span(), - "`stdin` and `long` are mutually exclusive", - )); - } - opt.long = Some(long); - } else { - return Err(Error::new(nv.lit.span(), "`long` must be a string")); - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("long") => { - return Err(Error::new( - list.path.span(), - "`long` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("long") => { - return Err(Error::new(p.span(), "`long` must be assigned to")); - } NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("default") => { - if let Lit::Str(default) = nv.lit { - if opt.default.is_some() { - return Err(Error::new( - default.span(), - "duplicate argument `default`", - )); - } - opt.default = Some(Some(default)); - } else { - return Err(Error::new(nv.lit.span(), "`default` must be a string")); - } + return Err(Error::new(nv.lit.span(), "`default` cannot be assigned to")); } NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("default") => { return Err(Error::new( @@ -797,13 +641,21 @@ pub fn parse_arg_attr(attr: Attribute, arg: PatType) -> Result { )); } NestedMeta::Meta(Meta::Path(p)) if p.is_ident("default") => { - if opt.default.is_some() { - return Err(Error::new(p.span(), "duplicate argument `default`")); - } - opt.default = Some(None); + opt.clap_attr + .push(NestedMeta::Meta(Meta::Path(Path::from(Ident::new( + "default_value_t", + p.span(), + ))))); + opt.default = Some(p); } - _ => { - return Err(Error::new(arg.span(), "unknown argument")); + unknown => { + if opt.stdin.is_some() { + return Err(Error::new( + unknown.span(), + "`stdin` and clap parser attributes are mutually exclusive", + )); + } + opt.clap_attr.push(unknown); } } } diff --git a/rpc-toolkit-macro-internals/src/lib.rs b/rpc-toolkit-macro-internals/src/lib.rs index c8535d7..2bbe0cb 100644 --- a/rpc-toolkit-macro-internals/src/lib.rs +++ b/rpc-toolkit-macro-internals/src/lib.rs @@ -8,14 +8,5 @@ macro_rules! macro_try { } mod command; -mod rpc_handler; -mod rpc_server; -mod run_cli; pub use command::build::build as build_command; -pub use rpc_handler::build::build as build_rpc_handler; -pub use rpc_handler::RpcHandlerArgs; -pub use rpc_server::build::build as build_rpc_server; -pub use rpc_server::RpcServerArgs; -pub use run_cli::build::build as build_run_cli; -pub use run_cli::RunCliArgs; diff --git a/rpc-toolkit-macro-internals/src/rpc_handler/build.rs b/rpc-toolkit-macro-internals/src/rpc_handler/build.rs deleted file mode 100644 index eb7d101..0000000 --- a/rpc-toolkit-macro-internals/src/rpc_handler/build.rs +++ /dev/null @@ -1,129 +0,0 @@ -use proc_macro2::{Span, TokenStream}; -use quote::quote; -use syn::spanned::Spanned; - -use super::*; - -pub fn build(args: RpcHandlerArgs) -> TokenStream { - let mut command = args.command; - let mut arguments = std::mem::replace( - &mut command.segments.last_mut().unwrap().arguments, - PathArguments::None, - ); - let command_module = command.clone(); - if let PathArguments::AngleBracketed(a) = &mut arguments { - a.args.push(syn::parse2(quote! { _ }).unwrap()); - } - command.segments.push(PathSegment { - ident: Ident::new("rpc_handler", command.span()), - arguments, - }); - let ctx = args.ctx; - let parent_data = if let Some(data) = args.parent_data { - quote! { #data } - } else { - quote! { () } - }; - let status_fn = args.status_fn.unwrap_or_else(|| { - syn::parse2(quote! { |_| ::rpc_toolkit::hyper::StatusCode::OK }).unwrap() - }); - let middleware_name_clone = (0..) - .map(|i| { - Ident::new( - &format!("__rpc_toolkit__rpc_handler__middleware_clone_{}", i), - Span::call_site(), - ) - }) - .take(args.middleware.len()); - let middleware_name_clone2 = middleware_name_clone.clone(); - let middleware_name_clone3 = middleware_name_clone.clone(); - let middleware_name_clone4 = middleware_name_clone.clone(); - let middleware_name_pre = (0..) - .map(|i| Ident::new(&format!("middleware_pre_{}", i), Span::call_site())) - .take(args.middleware.len()); - let middleware_name_pre2 = middleware_name_pre.clone(); - let middleware_name_post = (0..) - .map(|i| Ident::new(&format!("middleware_post_{}", i), Span::call_site())) - .take(args.middleware.len()); - let middleware_name_post_inv = middleware_name_post - .clone() - .collect::>() - .into_iter() - .rev(); - let middleware_name = (0..) - .map(|i| Ident::new(&format!("middleware_{}", i), Span::call_site())) - .take(args.middleware.len()); - let middleware_name2 = middleware_name.clone(); - let middleware = args.middleware.iter(); - let res = quote! { - { - let __rpc_toolkit__rpc_handler__context = #ctx; - let __rpc_toolkit__rpc_handler__parent_data = #parent_data; - let __rpc_toolkit__rpc_handler__status_fn = #status_fn; - #( - let #middleware_name_clone = ::std::sync::Arc::new(#middleware); - )* - let res: ::rpc_toolkit::RpcHandler = ::std::sync::Arc::new(move |mut req| { - let ctx = __rpc_toolkit__rpc_handler__context.clone(); - let parent_data = __rpc_toolkit__rpc_handler__parent_data.clone(); - let metadata = #command_module::Metadata::default(); - #( - let #middleware_name_clone3 = #middleware_name_clone2.clone(); - )* - ::rpc_toolkit::futures::FutureExt::boxed(async move { - #( - let #middleware_name_pre = match ::rpc_toolkit::rpc_server_helpers::constrain_middleware(&*#middleware_name_clone4)(&mut req, metadata).await? { - Ok(a) => a, - Err(res) => return Ok(res), - }; - )* - let (mut req_parts, req_body) = req.into_parts(); - let (mut res_parts, _) = ::rpc_toolkit::hyper::Response::new(()).into_parts(); - let rpc_req = ::rpc_toolkit::rpc_server_helpers::make_request(&req_parts, req_body).await; - match rpc_req { - Ok(mut rpc_req) => { - #( - let #middleware_name_post = match #middleware_name_pre2(&mut req_parts, &mut rpc_req).await? { - Ok(a) => a, - Err(res) => return Ok(res), - }; - )* - let mut rpc_res = match ::rpc_toolkit::serde_json::from_value(::rpc_toolkit::serde_json::Value::Object(rpc_req.params)) { - Ok(params) => #command(ctx, parent_data, &req_parts, &mut res_parts, ::rpc_toolkit::yajrc::RpcMethod::as_str(&rpc_req.method), params).await, - Err(e) => Err(e.into()) - }; - #( - let #middleware_name = match #middleware_name_post_inv(&mut res_parts, &mut rpc_res).await? { - Ok(a) => a, - Err(res) => return Ok(res), - }; - )* - let mut res = ::rpc_toolkit::rpc_server_helpers::to_response( - &req_parts.headers, - res_parts, - Ok(( - rpc_req.id, - rpc_res, - )), - __rpc_toolkit__rpc_handler__status_fn, - )?; - #( - #middleware_name2(&mut res).await?; - )* - Ok::<_, ::rpc_toolkit::hyper::http::Error>(res) - } - Err(e) => ::rpc_toolkit::rpc_server_helpers::to_response( - &req_parts.headers, - res_parts, - Err(e), - __rpc_toolkit__rpc_handler__status_fn, - ), - } - }) - }); - res - } - }; - // panic!("{}", res); - res -} diff --git a/rpc-toolkit-macro-internals/src/rpc_handler/mod.rs b/rpc-toolkit-macro-internals/src/rpc_handler/mod.rs deleted file mode 100644 index 2683733..0000000 --- a/rpc-toolkit-macro-internals/src/rpc_handler/mod.rs +++ /dev/null @@ -1,12 +0,0 @@ -use syn::*; - -pub struct RpcHandlerArgs { - pub(crate) command: Path, - pub(crate) ctx: Expr, - pub(crate) parent_data: Option, - pub(crate) status_fn: Option, - pub(crate) middleware: punctuated::Punctuated, -} - -pub mod build; -mod parse; diff --git a/rpc-toolkit-macro-internals/src/rpc_handler/parse.rs b/rpc-toolkit-macro-internals/src/rpc_handler/parse.rs deleted file mode 100644 index e9879d8..0000000 --- a/rpc-toolkit-macro-internals/src/rpc_handler/parse.rs +++ /dev/null @@ -1,50 +0,0 @@ -use syn::parse::{Parse, ParseStream}; -use syn::punctuated::Punctuated; - -use super::*; - -impl Parse for RpcHandlerArgs { - fn parse(input: ParseStream) -> Result { - let args; - braced!(args in input); - let mut command = None; - let mut ctx = None; - let mut parent_data = None; - let mut status_fn = None; - let mut middleware = Punctuated::new(); - while !args.is_empty() { - let arg_name: syn::Ident = args.parse()?; - let _: token::Colon = args.parse()?; - match arg_name.to_string().as_str() { - "command" => { - command = Some(args.parse()?); - } - "context" => { - ctx = Some(args.parse()?); - } - "parent_data" => { - parent_data = Some(args.parse()?); - } - "status" => { - status_fn = Some(args.parse()?); - } - "middleware" => { - let middlewares; - bracketed!(middlewares in args); - middleware = middlewares.parse_terminated(Expr::parse)?; - } - _ => return Err(Error::new(arg_name.span(), "unknown argument")), - } - if !args.is_empty() { - let _: token::Comma = args.parse()?; - } - } - Ok(RpcHandlerArgs { - command: command.expect("`command` is required"), - ctx: ctx.expect("`context` is required"), - parent_data, - status_fn, - middleware, - }) - } -} diff --git a/rpc-toolkit-macro-internals/src/rpc_server/build.rs b/rpc-toolkit-macro-internals/src/rpc_server/build.rs deleted file mode 100644 index e195999..0000000 --- a/rpc-toolkit-macro-internals/src/rpc_server/build.rs +++ /dev/null @@ -1,25 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; - -use super::*; - -pub fn build(mut args: RpcServerArgs) -> TokenStream { - let ctx = std::mem::replace( - &mut args.ctx, - parse2(quote! { __rpc_toolkit__rpc_server__context }).unwrap(), - ); - let handler = crate::rpc_handler::build::build(args); - let res = quote! { - { - let __rpc_toolkit__rpc_server__context = #ctx; - let __rpc_toolkit__rpc_server__builder = ::rpc_toolkit::rpc_server_helpers::make_builder(&__rpc_toolkit__rpc_server__context); - let handler = #handler; - __rpc_toolkit__rpc_server__builder.serve(::rpc_toolkit::hyper::service::make_service_fn(move |_| { - let handler = handler.clone(); - async move { Ok::<_, ::std::convert::Infallible>(::rpc_toolkit::hyper::service::service_fn(move |req| handler(req))) } - })) - } - }; - // panic!("{}", res); - res -} diff --git a/rpc-toolkit-macro-internals/src/rpc_server/mod.rs b/rpc-toolkit-macro-internals/src/rpc_server/mod.rs deleted file mode 100644 index 0f6ca0c..0000000 --- a/rpc-toolkit-macro-internals/src/rpc_server/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -use syn::*; - -pub type RpcServerArgs = crate::RpcHandlerArgs; - -pub mod build; diff --git a/rpc-toolkit-macro-internals/src/run_cli/build.rs b/rpc-toolkit-macro-internals/src/run_cli/build.rs deleted file mode 100644 index bbf56af..0000000 --- a/rpc-toolkit-macro-internals/src/run_cli/build.rs +++ /dev/null @@ -1,81 +0,0 @@ -use proc_macro2::TokenStream; -use quote::quote; -use syn::spanned::Spanned; - -use super::*; - -pub fn build(args: RunCliArgs) -> TokenStream { - let mut command_handler = args.command.clone(); - let mut arguments = std::mem::replace( - &mut command_handler.segments.last_mut().unwrap().arguments, - PathArguments::None, - ); - let command = command_handler.clone(); - if let PathArguments::AngleBracketed(a) = &mut arguments { - a.args.push(syn::parse2(quote! { () }).unwrap()); - a.args.push(syn::parse2(quote! { _ }).unwrap()); - } - command_handler.segments.push(PathSegment { - ident: Ident::new("cli_handler", command.span()), - arguments, - }); - let app = if let Some(mut_app) = args.mut_app { - let ident = mut_app.app_ident; - let body = mut_app.body; - quote! { - { - let #ident = #command::build_app(); - #body - } - } - } else { - quote! { #command::build_app() } - }; - let make_ctx = if let Some(make_ctx) = args.make_ctx { - let ident = make_ctx.matches_ident; - let body = make_ctx.body; - quote! { - { - let #ident = &rpc_toolkit_matches; - #body - } - } - } else { - quote! { &rpc_toolkit_matches } - }; - let parent_data = if let Some(data) = args.parent_data { - quote! { #data } - } else { - quote! { () } - }; - let exit_fn = args.exit_fn.unwrap_or_else(|| { - syn::parse2(quote! { |err: ::rpc_toolkit::yajrc::RpcError| { - eprintln!("{}", err.message); - if let Some(data) = err.data { - eprintln!("{}", data); - } - std::process::exit(err.code); - } }) - .unwrap() - }); - quote! { - { - let rpc_toolkit_matches = #app.get_matches(); - let rpc_toolkit_ctx = #make_ctx; - let rpc_toolkit_parent_data = #parent_data; - if let Err(err) = #command_handler( - rpc_toolkit_ctx, - rpc_toolkit_parent_data, - None, - &rpc_toolkit_matches, - "".into(), - (), - ) { - drop(rpc_toolkit_matches); - (#exit_fn)(err); - } else { - drop(rpc_toolkit_matches); - } - } - } -} diff --git a/rpc-toolkit-macro-internals/src/run_cli/mod.rs b/rpc-toolkit-macro-internals/src/run_cli/mod.rs deleted file mode 100644 index 780ae90..0000000 --- a/rpc-toolkit-macro-internals/src/run_cli/mod.rs +++ /dev/null @@ -1,22 +0,0 @@ -use syn::*; - -pub struct MakeCtx { - matches_ident: Ident, - body: Expr, -} - -pub struct MutApp { - app_ident: Ident, - body: Expr, -} - -pub struct RunCliArgs { - command: Path, - mut_app: Option, - make_ctx: Option, - parent_data: Option, - exit_fn: Option, -} - -pub mod build; -mod parse; diff --git a/rpc-toolkit-macro-internals/src/run_cli/parse.rs b/rpc-toolkit-macro-internals/src/run_cli/parse.rs deleted file mode 100644 index 2ca05b1..0000000 --- a/rpc-toolkit-macro-internals/src/run_cli/parse.rs +++ /dev/null @@ -1,68 +0,0 @@ -use syn::parse::{Parse, ParseStream}; - -use super::*; - -impl Parse for MakeCtx { - fn parse(input: ParseStream) -> Result { - let matches_ident = input.parse()?; - let _: token::FatArrow = input.parse()?; - let body = input.parse()?; - Ok(MakeCtx { - matches_ident, - body, - }) - } -} - -impl Parse for MutApp { - fn parse(input: ParseStream) -> Result { - let app_ident = input.parse()?; - let _: token::FatArrow = input.parse()?; - let body = input.parse()?; - Ok(MutApp { app_ident, body }) - } -} - -impl Parse for RunCliArgs { - fn parse(input: ParseStream) -> Result { - let args; - braced!(args in input); - let mut command = None; - let mut mut_app = None; - let mut make_ctx = None; - let mut parent_data = None; - let mut exit_fn = None; - while !args.is_empty() { - let arg_name: syn::Ident = args.parse()?; - let _: token::Colon = args.parse()?; - match arg_name.to_string().as_str() { - "command" => { - command = Some(args.parse()?); - } - "app" => { - mut_app = Some(args.parse()?); - } - "context" => { - make_ctx = Some(args.parse()?); - } - "parent_data" => { - parent_data = Some(args.parse()?); - } - "exit" => { - exit_fn = Some(args.parse()?); - } - _ => return Err(Error::new(arg_name.span(), "unknown argument")), - } - if !args.is_empty() { - let _: token::Comma = args.parse()?; - } - } - Ok(RunCliArgs { - command: command.expect("`command` is required"), - mut_app, - make_ctx, - parent_data, - exit_fn, - }) - } -} diff --git a/rpc-toolkit-macro/src/lib.rs b/rpc-toolkit-macro/src/lib.rs index 83d3682..63965e3 100644 --- a/rpc-toolkit-macro/src/lib.rs +++ b/rpc-toolkit-macro/src/lib.rs @@ -42,21 +42,3 @@ pub fn context(_: TokenStream, _: TokenStream) -> TokenStream { .to_compile_error() .into() } - -#[proc_macro] -pub fn rpc_handler(item: TokenStream) -> TokenStream { - let item = syn::parse_macro_input!(item as RpcHandlerArgs); - build_rpc_handler(item).into() -} - -#[proc_macro] -pub fn rpc_server(item: TokenStream) -> TokenStream { - let item = syn::parse_macro_input!(item as RpcServerArgs); - build_rpc_server(item).into() -} - -#[proc_macro] -pub fn run_cli(item: TokenStream) -> TokenStream { - let item = syn::parse_macro_input!(item as RunCliArgs); - build_run_cli(item).into() -} diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml index 6b225b2..146f205 100644 --- a/rpc-toolkit/Cargo.toml +++ b/rpc-toolkit/Cargo.toml @@ -27,6 +27,7 @@ imbl-value = "0.1" lazy_format = "2" lazy_static = "1.4" openssl = { version = "0.10", features = ["vendored"] } +pin-project = "1" reqwest = { version = "0.11" } rpc-toolkit-macro = { version = "0.2.2", path = "../rpc-toolkit-macro" } serde = { version = "1.0", features = ["derive"] } diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 6f11f9f..da2ce96 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -13,8 +13,8 @@ use yajrc::{Id, RpcError}; use crate::util::{internal_error, parse_error, Flat}; use crate::{ - AnyHandler, CliBindingsAny, DynHandler, HandleAny, HandleAnyArgs, HandleArgs, Handler, - IntoContext, Name, ParentHandler, + AnyHandler, CliBindings, CliBindingsAny, DynHandler, HandleAny, HandleAnyArgs, HandleArgs, + Handler, HandlerTypes, IntoContext, Name, ParentHandler, }; type GenericRpcMethod<'a> = yajrc::GenericRpcMethod<&'a str, Value, Value>; @@ -81,120 +81,123 @@ impl } #[async_trait::async_trait] -pub trait CliContext: crate::Context { +pub trait CallRemote: crate::Context { async fn call_remote(&self, method: &str, params: Value) -> Result; } -#[async_trait::async_trait] -pub trait CliContextHttp: crate::Context { - fn client(&self) -> &Client; - fn url(&self) -> Url; - async fn call_remote(&self, method: &str, params: Value) -> Result { - let rpc_req = RpcRequest { - id: Some(Id::Number(0.into())), - method: GenericRpcMethod::new(method), - params, - }; - let mut req = self.client().request(Method::POST, self.url()); - let body; +pub async fn call_remote_http( + client: &Client, + url: Url, + method: &str, + params: Value, +) -> Result { + let rpc_req = RpcRequest { + id: Some(Id::Number(0.into())), + method: GenericRpcMethod::new(method), + params, + }; + let mut req = client.request(Method::POST, url); + let body; + #[cfg(feature = "cbor")] + { + req = req.header("content-type", "application/cbor"); + req = req.header("accept", "application/cbor, application/json"); + body = serde_cbor::to_vec(&rpc_req)?; + } + #[cfg(not(feature = "cbor"))] + { + req = req.header("content-type", "application/json"); + req = req.header("accept", "application/json"); + body = serde_json::to_vec(&req)?; + } + let res = req + .header("content-length", body.len()) + .body(body) + .send() + .await?; + + match res + .headers() + .get("content-type") + .and_then(|v| v.to_str().ok()) + { + Some("application/json") => { + serde_json::from_slice::(&*res.bytes().await.map_err(internal_error)?) + .map_err(parse_error)? + .result + } #[cfg(feature = "cbor")] - { - req = req.header("content-type", "application/cbor"); - req = req.header("accept", "application/cbor, application/json"); - body = serde_cbor::to_vec(&rpc_req)?; + Some("application/cbor") => { + serde_cbor::from_slice::(&*res.bytes().await.map_err(internal_error)?) + .map_err(parse_error)? + .result } - #[cfg(not(feature = "cbor"))] - { - req = req.header("content-type", "application/json"); - req = req.header("accept", "application/json"); - body = serde_json::to_vec(&req)?; - } - let res = req - .header("content-length", body.len()) - .body(body) - .send() - .await?; + _ => Err(internal_error("missing content type")), + } +} - match res - .headers() - .get("content-type") - .and_then(|v| v.to_str().ok()) - { - Some("application/json") => { - serde_json::from_slice::(&*res.bytes().await.map_err(internal_error)?) - .map_err(parse_error)? - .result - } - #[cfg(feature = "cbor")] - Some("application/cbor") => { - serde_cbor::from_slice::(&*res.bytes().await.map_err(internal_error)?) - .map_err(parse_error)? - .result - } - _ => Err(internal_error("missing content type")), +pub async fn call_remote_socket( + connection: impl AsyncRead + AsyncWrite, + method: &str, + params: Value, +) -> Result { + let rpc_req = RpcRequest { + id: Some(Id::Number(0.into())), + method: GenericRpcMethod::new(method), + params, + }; + let conn = connection; + tokio::pin!(conn); + let mut buf = serde_json::to_vec(&rpc_req).map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + })?; + buf.push(b'\n'); + conn.write_all(&buf).await.map_err(|e| RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + })?; + let mut line = String::new(); + BufReader::new(conn).read_line(&mut line).await?; + serde_json::from_str::(&line) + .map_err(parse_error)? + .result +} + +pub struct CallRemoteHandler { + _phantom: PhantomData, + handler: RemoteHandler, +} +impl CallRemoteHandler { + pub fn new(handler: RemoteHandler) -> Self { + Self { + _phantom: PhantomData, + handler: handler, } } } -#[async_trait::async_trait] -impl CliContext for T -where - T: CliContextHttp, +impl Clone + for CallRemoteHandler { - async fn call_remote(&self, method: &str, params: Value) -> Result { - ::call_remote(&self, method, params).await - } -} - -#[async_trait::async_trait] -pub trait CliContextSocket: crate::Context { - type Stream: AsyncRead + AsyncWrite + Send; - async fn connect(&self) -> std::io::Result; - async fn call_remote(&self, method: &str, params: Value) -> Result { - let rpc_req = RpcRequest { - id: Some(Id::Number(0.into())), - method: GenericRpcMethod::new(method), - params, - }; - let conn = self.connect().await.map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INTERNAL_ERROR - })?; - tokio::pin!(conn); - let mut buf = serde_json::to_vec(&rpc_req).map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INTERNAL_ERROR - })?; - buf.push(b'\n'); - conn.write_all(&buf).await.map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INTERNAL_ERROR - })?; - let mut line = String::new(); - BufReader::new(conn).read_line(&mut line).await?; - serde_json::from_str::(&line) - .map_err(parse_error)? - .result - } -} - -#[derive(Debug, Default)] -pub struct CallRemote(PhantomData<(RemoteContext, RemoteHandler)>); -impl CallRemote { - pub fn new() -> Self { - Self(PhantomData) - } -} -impl Clone for CallRemote { fn clone(&self) -> Self { - Self(PhantomData) + Self { + _phantom: PhantomData, + handler: self.handler.clone(), + } } } -#[async_trait::async_trait] -impl Handler - for CallRemote +impl std::fmt::Debug + for CallRemoteHandler +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("CallRemoteHandler").finish() + } +} + +impl HandlerTypes for CallRemoteHandler where RemoteContext: IntoContext, - RemoteHandler: Handler, + RemoteHandler: HandlerTypes, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, @@ -204,6 +207,18 @@ where type InheritedParams = RemoteHandler::InheritedParams; type Ok = RemoteHandler::Ok; type Err = RemoteHandler::Err; +} +#[async_trait::async_trait] +impl Handler + for CallRemoteHandler +where + RemoteContext: IntoContext, + RemoteHandler: Handler, + RemoteHandler::Params: Serialize, + RemoteHandler::InheritedParams: Serialize, + RemoteHandler::Ok: DeserializeOwned, + RemoteHandler::Err: From, +{ async fn handle_async( &self, handle_args: HandleArgs, @@ -229,3 +244,49 @@ where } } } +// #[async_trait::async_trait] +impl CliBindings + for CallRemoteHandler +where + RemoteContext: IntoContext, + RemoteHandler: Handler + CliBindings, + RemoteHandler::Params: Serialize, + RemoteHandler::InheritedParams: Serialize, + RemoteHandler::Ok: DeserializeOwned, + RemoteHandler::Err: From, +{ + fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { + self.handler.cli_command(ctx_ty) + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + ctx_ty: TypeId, + ) -> Result<(std::collections::VecDeque<&'static str>, Value), clap::Error> { + self.handler.cli_parse(matches, ctx_ty) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.handler.cli_display( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) + } +} diff --git a/rpc-toolkit/src/command_helpers.rs b/rpc-toolkit/src/command_helpers.rs new file mode 100644 index 0000000..87f9cda --- /dev/null +++ b/rpc-toolkit/src/command_helpers.rs @@ -0,0 +1,33 @@ +use std::fmt::Display; +use std::io::Stdin; +use std::str::FromStr; + +use clap::ArgMatches; +pub use {clap, serde}; + +pub fn default_arg_parser(arg: &str, _: &ArgMatches) -> Result +where + T: FromStr, + T::Err: Display, +{ + arg.parse() + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e)) +} + +pub fn default_stdin_parser(stdin: &mut Stdin, _: &ArgMatches) -> Result +where + T: FromStr, + T::Err: Display, +{ + let mut s = String::new(); + stdin + .read_line(&mut s) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::Io, e))?; + if let Some(s) = s.strip_suffix("\n") { + s + } else { + &s + } + .parse() + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e)) +} diff --git a/rpc-toolkit/src/context.rs b/rpc-toolkit/src/context.rs index f2c0e43..0683fd2 100644 --- a/rpc-toolkit/src/context.rs +++ b/rpc-toolkit/src/context.rs @@ -6,6 +6,9 @@ use tokio::runtime::Handle; use crate::Handler; pub trait Context: Any + Send + Sync + 'static { + fn inner_type_id(&self) -> TypeId { + ::type_id(&self) + } fn runtime(&self) -> Handle { Handle::current() } @@ -36,7 +39,7 @@ impl IntoContext for C { AnyContext::new(self) } fn downcast(value: AnyContext) -> Result { - if value.0.type_id() == TypeId::of::() { + if value.0.inner_type_id() == TypeId::of::() { unsafe { Ok(value.downcast_unchecked::()) } } else { Err(value) @@ -90,10 +93,8 @@ impl AnyContext { Self(Box::new(value)) } unsafe fn downcast_unchecked(self) -> C { - unsafe { - let raw: *mut dyn Context = Box::into_raw(self.0); - *Box::from_raw(raw as *mut C) - } + let raw: *mut dyn Context = Box::into_raw(self.0); + *Box::from_raw(raw as *mut C) } } @@ -105,7 +106,7 @@ impl IntoContext for AnyContext { None } fn inner_type_id(&self) -> TypeId { - self.0.type_id() + self.0.inner_type_id() } fn downcast(value: AnyContext) -> Result { Ok(value) diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index e93846a..6086342 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -14,6 +14,7 @@ use yajrc::RpcError; use crate::context::{AnyContext, IntoContext}; use crate::util::{combine, internal_error, invalid_params, Flat}; +use crate::{CallRemote, CallRemoteHandler}; pub(crate) struct HandleAnyArgs { pub(crate) context: AnyContext, @@ -24,7 +25,7 @@ pub(crate) struct HandleAnyArgs { impl HandleAnyArgs { fn downcast(self) -> Result, imbl_value::Error> where - H: Handler, + H: HandlerTypes, H::Params: DeserializeOwned, H::InheritedParams: DeserializeOwned, { @@ -49,7 +50,7 @@ impl HandleAnyArgs { } #[async_trait::async_trait] -pub(crate) trait HandleAny: Send + Sync { +pub(crate) trait HandleAny: std::fmt::Debug + Send + Sync { fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result; fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option>; @@ -77,7 +78,7 @@ pub(crate) trait CliBindingsAny { fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError>; } -pub trait CliBindings: Handler { +pub trait CliBindings: HandlerTypes { fn cli_command(&self, ctx_ty: TypeId) -> Command; fn cli_parse( &self, @@ -91,7 +92,7 @@ pub trait CliBindings: Handler { ) -> Result<(), Self::Err>; } -pub trait PrintCliResult: Handler { +pub trait PrintCliResult: HandlerTypes { fn print( &self, handle_args: HandleArgs, @@ -102,7 +103,7 @@ pub trait PrintCliResult: Handler { pub(crate) trait HandleAnyWithCli: HandleAny + CliBindingsAny {} impl HandleAnyWithCli for T {} -#[derive(Clone)] +#[derive(Debug, Clone)] pub(crate) enum DynHandler { WithoutCli(Arc), WithCli(Arc), @@ -130,7 +131,7 @@ impl HandleAny for DynHandler { } #[derive(Debug, Clone)] -pub struct HandleArgs + ?Sized> { +pub struct HandleArgs { pub context: Context, pub parent_method: Vec<&'static str>, pub method: VecDeque<&'static str>, @@ -139,12 +140,17 @@ pub struct HandleArgs + ?Sized> { pub raw_params: Value, } -#[async_trait::async_trait] -pub trait Handler: Clone + Send + Sync + 'static { +pub trait HandlerTypes { type Params: Send + Sync; type InheritedParams: Send + Sync; type Ok: Send + Sync; type Err: Send + Sync; +} + +#[async_trait::async_trait] +pub trait Handler: + HandlerTypes + std::fmt::Debug + Clone + Send + Sync + 'static +{ fn handle_sync(&self, handle_args: HandleArgs) -> Result { handle_args .context @@ -198,6 +204,11 @@ impl AnyHandler { } } } +impl std::fmt::Debug for AnyHandler { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("AnyHandler").field(&self.handler).finish() + } +} #[async_trait::async_trait] impl> HandleAny for AnyHandler @@ -271,7 +282,7 @@ impl<'a> std::borrow::Borrow> for Name { } } -#[derive(Clone)] +#[derive(Debug, Clone)] pub(crate) struct SubcommandMap(pub(crate) BTreeMap, DynHandler>>); impl SubcommandMap { fn insert( @@ -324,6 +335,13 @@ impl Clone for ParentHandler { } } } +impl std::fmt::Debug for ParentHandler { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("ParentHandler") + .field(&self.subcommands) + .finish() + } +} struct InheritanceHandler { _phantom: PhantomData<(Context, Params, InheritedParams)>, @@ -341,6 +359,28 @@ impl Clone } } } +impl std::fmt::Debug + for InheritanceHandler +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("InheritanceHandler") + .field(&self.handler) + .finish() + } +} +impl HandlerTypes + for InheritanceHandler +where + Context: IntoContext, + H: HandlerTypes, + Params: Send + Sync, + InheritedParams: Send + Sync, +{ + type Params = H::Params; + type InheritedParams = Flat; + type Ok = H::Ok; + type Err = H::Err; +} #[async_trait::async_trait] impl Handler for InheritanceHandler @@ -351,10 +391,6 @@ where H: Handler, F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { - type Params = H::Params; - type InheritedParams = Flat; - type Ok = H::Ok; - type Err = H::Err; fn handle_sync( &self, HandleArgs { @@ -446,7 +482,10 @@ impl ParentHandler(mut self, name: &'static str, handler: H) -> Self where Context: IntoContext, - H: CliBindings + 'static, + H: HandlerTypes + + Handler + + CliBindings + + 'static, H::Params: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, @@ -454,10 +493,42 @@ impl ParentHandler( + mut self, + name: &'static str, + handler: H, + ) -> Self + where + ServerContext: IntoContext, + CliContext: IntoContext + CallRemote, + H: HandlerTypes + + Handler + + CliBindings + + 'static, + H::Params: Serialize + DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, + H::Err: From, + CallRemoteHandler: Handler, + as HandlerTypes>::Ok: Serialize + DeserializeOwned, + as HandlerTypes>::Params: Serialize + DeserializeOwned, + as HandlerTypes>::InheritedParams: DeserializeOwned, + RpcError: From< as HandlerTypes>::Err>, + { + self.subcommands.insert( + handler.contexts(), + name.into(), + DynHandler::WithoutCli(Arc::new(AnyHandler::new(handler.clone()))), + ); + let call_remote = CallRemoteHandler::::new(handler); + self.subcommands.insert( + call_remote.contexts(), + name.into(), + DynHandler::WithCli(Arc::new(AnyHandler::new(call_remote))), ); self } @@ -472,10 +543,7 @@ impl ParentHandler Self where Context: IntoContext, - H: CliBindings + 'static, + H: Handler + CliBindings + 'static, H::Params: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, @@ -513,6 +581,62 @@ where ); self } + pub fn subcommand_with_inherited_remote_cli( + mut self, + name: &'static str, + handler: H, + inherit: F, + ) -> Self + where + ServerContext: IntoContext, + CliContext: IntoContext + CallRemote, + H: HandlerTypes + Handler + CliBindings + 'static, + H::Params: Serialize + DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, + H::Err: From, + CallRemoteHandler: + Handler, + as HandlerTypes>::Ok: Serialize + DeserializeOwned, + as HandlerTypes>::Params: Serialize + DeserializeOwned, + as HandlerTypes>::InheritedParams: + Serialize + DeserializeOwned, + RpcError: From< as HandlerTypes>::Err>, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, + { + self.subcommands.insert( + handler.contexts(), + name.into(), + DynHandler::WithoutCli(Arc::new(AnyHandler::new(InheritanceHandler::< + ServerContext, + Params, + InheritedParams, + H, + F, + > { + _phantom: PhantomData, + handler: handler.clone(), + inherit: inherit.clone(), + }))), + ); + let call_remote = CallRemoteHandler::::new(handler); + self.subcommands.insert( + call_remote.contexts(), + name.into(), + DynHandler::WithCli(Arc::new(AnyHandler::new(InheritanceHandler::< + CliContext, + Params, + InheritedParams, + CallRemoteHandler, + F, + > { + _phantom: PhantomData, + handler: call_remote, + inherit, + }))), + ); + self + } pub fn subcommand_with_inherited_no_cli( mut self, name: &'static str, @@ -544,7 +668,7 @@ where pub fn root_handler(mut self, handler: H, inherit: F) -> Self where Context: IntoContext, - H: CliBindings + 'static, + H: HandlerTypes + Handler + CliBindings + 'static, H::Params: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, @@ -589,17 +713,23 @@ where } } -#[async_trait::async_trait] -impl< - Context: IntoContext, - Params: Serialize + Send + Sync + 'static, - InheritedParams: Serialize + Send + Sync + 'static, - > Handler for ParentHandler +impl HandlerTypes for ParentHandler +where + Params: Send + Sync, + InheritedParams: Send + Sync, { type Params = Params; type InheritedParams = InheritedParams; type Ok = Value; type Err = RpcError; +} +#[async_trait::async_trait] +impl Handler for ParentHandler +where + Context: IntoContext, + Params: Serialize + Send + Sync + 'static, + InheritedParams: Serialize + Send + Sync + 'static, +{ fn handle_sync( &self, HandleArgs { @@ -614,7 +744,7 @@ impl< if let Some(cmd) = cmd { parent_method.push(cmd); } - if let Some((_, sub_handler)) = self.subcommands.get(context.inner_type_id(), cmd) { + if let Some((_, sub_handler)) = &self.subcommands.get(context.inner_type_id(), cmd) { sub_handler.handle_sync(HandleAnyArgs { context: context.upcast(), parent_method, @@ -776,11 +906,18 @@ impl Clone for FromFn { } } } +impl std::fmt::Debug for FromFn { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FromFn") + .field("blocking", &self.blocking) + .finish() + } +} impl PrintCliResult for FromFn where Context: IntoContext, - Self: Handler, - >::Ok: Display, + Self: HandlerTypes, + ::Ok: Display, { fn print(&self, _: HandleArgs, result: Self::Ok) -> Result<(), Self::Err> { Ok(println!("{result}")) @@ -815,11 +952,16 @@ impl Clone for FromFnAsync { } } } +impl std::fmt::Debug for FromFnAsync { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FromFnAsync").finish() + } +} impl PrintCliResult for FromFnAsync where Context: IntoContext, - Self: Handler, - >::Ok: Display, + Self: HandlerTypes, + ::Ok: Display, { fn print(&self, _: HandleArgs, result: Self::Ok) -> Result<(), Self::Err> { Ok(println!("{result}")) @@ -833,10 +975,8 @@ pub fn from_fn_async(function: F) -> FromFnAsync Handler for FromFn +impl HandlerTypes for FromFn where - Context: IntoContext, F: Fn() -> Result + Send + Sync + Clone + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, @@ -845,6 +985,15 @@ where type InheritedParams = NoParams; type Ok = T; type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFn +where + Context: IntoContext, + F: Fn() -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ fn handle_sync(&self, _: HandleArgs) -> Result { (self.function)() } @@ -859,10 +1008,8 @@ where } } } -#[async_trait::async_trait] -impl Handler for FromFnAsync +impl HandlerTypes for FromFnAsync where - Context: IntoContext, F: Fn() -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + Sync + 'static, T: Send + Sync + 'static, @@ -872,13 +1019,22 @@ where type InheritedParams = NoParams; type Ok = T; type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFnAsync +where + Context: IntoContext, + F: Fn() -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ async fn handle_async(&self, _: HandleArgs) -> Result { (self.function)().await } } -#[async_trait::async_trait] -impl Handler for FromFn +impl HandlerTypes for FromFn where Context: IntoContext, F: Fn(Context) -> Result + Send + Sync + Clone + 'static, @@ -889,6 +1045,15 @@ where type InheritedParams = NoParams; type Ok = T; type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFn +where + Context: IntoContext, + F: Fn(Context) -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ fn handle_sync(&self, handle_args: HandleArgs) -> Result { (self.function)(handle_args.context) } @@ -903,8 +1068,7 @@ where } } } -#[async_trait::async_trait] -impl Handler for FromFnAsync +impl HandlerTypes for FromFnAsync where Context: IntoContext, F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, @@ -916,6 +1080,16 @@ where type InheritedParams = NoParams; type Ok = T; type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFnAsync +where + Context: IntoContext, + F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ async fn handle_async( &self, handle_args: HandleArgs, @@ -924,8 +1098,7 @@ where } } -#[async_trait::async_trait] -impl Handler for FromFn +impl HandlerTypes for FromFn where Context: IntoContext, F: Fn(Context, Params) -> Result + Send + Sync + Clone + 'static, @@ -937,6 +1110,16 @@ where type InheritedParams = NoParams; type Ok = T; type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFn +where + Context: IntoContext, + F: Fn(Context, Params) -> Result + Send + Sync + Clone + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ fn handle_sync(&self, handle_args: HandleArgs) -> Result { let HandleArgs { context, params, .. @@ -954,9 +1137,8 @@ where } } } -#[async_trait::async_trait] -impl Handler - for FromFnAsync + +impl HandlerTypes for FromFnAsync where Context: IntoContext, F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, @@ -969,6 +1151,18 @@ where type InheritedParams = NoParams; type Ok = T; type Err = E; +} +#[async_trait::async_trait] +impl Handler + for FromFnAsync +where + Context: IntoContext, + F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ async fn handle_async( &self, handle_args: HandleArgs, @@ -980,8 +1174,7 @@ where } } -#[async_trait::async_trait] -impl Handler +impl HandlerTypes for FromFn where Context: IntoContext, @@ -995,6 +1188,18 @@ where type InheritedParams = InheritedParams; type Ok = T; type Err = E; +} +#[async_trait::async_trait] +impl Handler + for FromFn +where + Context: IntoContext, + F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ fn handle_sync(&self, handle_args: HandleArgs) -> Result { let HandleArgs { context, @@ -1015,8 +1220,8 @@ where } } } -#[async_trait::async_trait] -impl Handler + +impl HandlerTypes for FromFnAsync where Context: IntoContext, @@ -1031,6 +1236,19 @@ where type InheritedParams = InheritedParams; type Ok = T; type Err = E; +} +#[async_trait::async_trait] +impl Handler + for FromFnAsync +where + Context: IntoContext, + F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ async fn handle_async( &self, handle_args: HandleArgs, @@ -1048,7 +1266,7 @@ where impl CliBindings for FromFn where Context: IntoContext, - Self: Handler, + Self: HandlerTypes, Self::Params: FromArgMatches + CommandFactory + Serialize, Self: PrintCliResult, { @@ -1097,7 +1315,7 @@ where impl CliBindings for FromFnAsync where Context: IntoContext, - Self: Handler, + Self: HandlerTypes, Self::Params: FromArgMatches + CommandFactory + Serialize, Self: PrintCliResult, { diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index bcbf617..d1a60c2 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -28,6 +28,7 @@ pub use server::*; pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc}; mod cli; +pub mod command_helpers; mod context; mod handler; mod server; diff --git a/rpc-toolkit/src/server/socket.rs b/rpc-toolkit/src/server/socket.rs index e42b42c..d51201b 100644 --- a/rpc-toolkit/src/server/socket.rs +++ b/rpc-toolkit/src/server/socket.rs @@ -5,17 +5,17 @@ use futures::{Future, Stream, StreamExt, TryStreamExt}; use imbl_value::Value; use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}; use tokio::net::{TcpListener, ToSocketAddrs, UnixListener}; -use tokio::sync::OnceCell; +use tokio::sync::Notify; use yajrc::RpcError; -use crate::util::{parse_error, JobRunner}; +use crate::util::{parse_error, JobRunner, StreamUntil}; use crate::Server; #[derive(Clone)] -pub struct ShutdownHandle(Arc>); +pub struct ShutdownHandle(Arc); impl ShutdownHandle { pub fn shutdown(self) { - let _ = self.0.set(()); + self.0.notify_one(); } } @@ -25,10 +25,10 @@ impl Server { listener: impl Stream> + 'a, error_handler: impl Fn(std::io::Error) + Sync + 'a, ) -> (ShutdownHandle, impl Future + 'a) { - let shutdown = Arc::new(OnceCell::new()); + let shutdown = Arc::new(Notify::new()); (ShutdownHandle(shutdown.clone()), async move { let mut runner = JobRunner::>::new(); - let jobs = listener.map(|pipe| async { + let jobs = StreamUntil::new(listener, shutdown.notified()).map(|pipe| async { let pipe = pipe?; let (r, mut w) = tokio::io::split(pipe); let stream = self.stream( diff --git a/rpc-toolkit/src/util.rs b/rpc-toolkit/src/util.rs index 133b8cf..7667930 100644 --- a/rpc-toolkit/src/util.rs +++ b/rpc-toolkit/src/util.rs @@ -1,6 +1,7 @@ use std::fmt::Display; -use futures::future::BoxFuture; +use futures::future::{BoxFuture, FusedFuture}; +use futures::stream::FusedStream; use futures::{Future, FutureExt, Stream, StreamExt}; use imbl_value::Value; use serde::de::DeserializeOwned; @@ -148,6 +149,9 @@ impl<'a, T> JobRunner<'a, T> { self.running.push(job.boxed()); } else { self.closed = true; + if self.running.is_empty() { + return None; + } } } res = self.next() => { @@ -172,6 +176,52 @@ impl<'a, T> Stream for JobRunner<'a, T> { } } +#[pin_project::pin_project] +pub struct StreamUntil { + #[pin] + stream: S, + #[pin] + until: F, + done: bool, +} +impl StreamUntil { + pub fn new(stream: S, until: F) -> Self { + Self { + stream, + until, + done: false, + } + } +} +impl Stream for StreamUntil +where + S: Stream, + F: Future, +{ + type Item = S::Item; + fn poll_next( + self: std::pin::Pin<&mut Self>, + cx: &mut std::task::Context<'_>, + ) -> std::task::Poll> { + let this = self.project(); + *this.done = *this.done || this.until.poll(cx).is_ready(); + if *this.done { + std::task::Poll::Ready(None) + } else { + this.stream.poll_next(cx) + } + } +} +impl FusedStream for StreamUntil +where + S: FusedStream, + F: FusedFuture, +{ + fn is_terminated(&self) -> bool { + self.done || self.stream.is_terminated() || self.until.is_terminated() + } +} + // #[derive(Debug)] // pub enum Infallible {} // impl From for T { diff --git a/rpc-toolkit/tests/compat.rs b/rpc-toolkit/tests/compat.rs index a96d00c..cd97121 100644 --- a/rpc-toolkit/tests/compat.rs +++ b/rpc-toolkit/tests/compat.rs @@ -1,119 +1,83 @@ -// use std::fmt::Display; -// use std::str::FromStr; -// use std::sync::Arc; +use std::fmt::Display; +use std::str::FromStr; +use std::sync::Arc; -// use futures::FutureExt; -// use hyper::Request; -// use rpc_toolkit::clap::Arg; -// use rpc_toolkit::hyper::http::Error as HttpError; -// use rpc_toolkit::hyper::{Body, Response}; -// use rpc_toolkit::rpc_server_helpers::{ -// DynMiddlewareStage2, DynMiddlewareStage3, DynMiddlewareStage4, -// }; -// use rpc_toolkit::serde::{Deserialize, Serialize}; -// use rpc_toolkit::url::Host; -// use rpc_toolkit::yajrc::RpcError; -// use rpc_toolkit::{command, rpc_server, run_cli, Context, Metadata}; +use futures::FutureExt; +use hyper::Request; +use rpc_toolkit::clap::Arg; +use rpc_toolkit::hyper::http::Error as HttpError; +use rpc_toolkit::hyper::Response; +use rpc_toolkit::serde::{Deserialize, Serialize}; +use rpc_toolkit::url::Host; +use rpc_toolkit::yajrc::RpcError; +use rpc_toolkit::{command, Context}; -// #[derive(Debug, Clone)] -// pub struct AppState(Arc); -// impl From for () { -// fn from(_: AppState) -> Self { -// () -// } -// } +#[derive(Debug, Clone)] +pub struct AppState(Arc); +impl From for () { + fn from(_: AppState) -> Self { + () + } +} -// #[derive(Debug)] -// pub struct ConfigSeed { -// host: Host, -// port: u16, -// } +#[derive(Debug)] +pub struct ConfigSeed { + host: Host, + port: u16, +} -// impl Context for AppState { -// type Metadata = (); -// } +impl Context for AppState {} -// fn test_string() -> String { -// "test".to_owned() -// } +#[command( + about = "Does the thing", + subcommands("dothething2::", self(dothething_impl(async))) +)] +async fn dothething( + #[context] _ctx: AppState, + #[arg(short = 'a')] arg1: Option, + #[arg(short = 'b', default)] val: String, + #[arg(short = 'c', help = "I am the flag `c`!", default)] arg3: bool, + #[arg(stdin)] structured: U, +) -> Result<(Option, String, bool, U), RpcError> +where + U: Serialize + for<'a> Deserialize<'a> + FromStr + Clone + 'static, + U::Err: Display, +{ + Ok((arg1, val, arg3, structured)) +} -// #[command( -// about = "Does the thing", -// subcommands("dothething2::", self(dothething_impl(async))) -// )] -// async fn dothething< -// U: Serialize + for<'a> Deserialize<'a> + FromStr + Clone + 'static, -// E: Display, -// >( -// #[context] _ctx: AppState, -// #[arg(short = 'a')] arg1: Option, -// #[arg(short = 'b', default = "test_string")] val: String, -// #[arg(short = 'c', help = "I am the flag `c`!", default)] arg3: bool, -// #[arg(stdin)] structured: U, -// ) -> Result<(Option, String, bool, U), RpcError> { -// Ok((arg1, val, arg3, structured)) -// } +async fn dothething_impl( + ctx: AppState, + parent_data: (Option, String, bool, U), +) -> Result { + Ok(format!( + "{:?}, {:?}, {}, {}, {}", + ctx, + parent_data.0, + parent_data.1, + parent_data.2, + serde_json::to_string_pretty(&parent_data.3)? + )) +} -// async fn dothething_impl( -// ctx: AppState, -// parent_data: (Option, String, bool, U), -// ) -> Result { -// Ok(format!( -// "{:?}, {:?}, {}, {}, {}", -// ctx, -// parent_data.0, -// parent_data.1, -// parent_data.2, -// serde_json::to_string_pretty(&parent_data.3)? -// )) -// } - -// #[command(about = "Does the thing")] -// fn dothething2 Deserialize<'a> + FromStr, E: Display>( -// #[parent_data] parent_data: (Option, String, bool, U), -// #[arg(stdin)] structured2: U, -// ) -> Result { -// Ok(format!( -// "{:?}, {}, {}, {}, {}", -// parent_data.0, -// parent_data.1, -// parent_data.2, -// serde_json::to_string_pretty(&parent_data.3)?, -// serde_json::to_string_pretty(&structured2)?, -// )) -// } - -// async fn cors( -// req: &mut Request, -// _: M, -// ) -> Result>, HttpError> { -// if req.method() == hyper::Method::OPTIONS { -// Ok(Err(Response::builder() -// .header("Access-Control-Allow-Origin", "*") -// .body(Body::empty())?)) -// } else { -// Ok(Ok(Box::new(|_, _| { -// async move { -// let res: DynMiddlewareStage3 = Box::new(|_, _| { -// async move { -// let res: DynMiddlewareStage4 = Box::new(|res| { -// async move { -// res.headers_mut() -// .insert("Access-Control-Allow-Origin", "*".parse()?); -// Ok::<_, HttpError>(()) -// } -// .boxed() -// }); -// Ok::<_, HttpError>(Ok(res)) -// } -// .boxed() -// }); -// Ok::<_, HttpError>(Ok(res)) -// } -// .boxed() -// }))) -// } -// } +#[command(about = "Does the thing")] +fn dothething2( + #[parent_data] parent_data: (Option, String, bool, U), + #[arg(stdin)] structured2: U, +) -> Result +where + U: Serialize + for<'a> Deserialize<'a> + FromStr + Clone + 'static, + U::Err: Display, +{ + Ok(format!( + "{:?}, {}, {}, {}, {}", + parent_data.0, + parent_data.1, + parent_data.2, + serde_json::to_string_pretty(&parent_data.3)?, + serde_json::to_string_pretty(&structured2)?, + )) +} // #[tokio::test] // async fn test_rpc() { diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index 299100a..d478f17 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -1,13 +1,19 @@ +use std::any::TypeId; +use std::ffi::OsString; use std::fmt::Display; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::sync::Arc; use clap::Parser; -use rpc_toolkit::{from_fn, from_fn_async, AnyContext, CliApp, Context, NoParams, ParentHandler}; +use futures::future::ready; +use imbl_value::Value; +use rpc_toolkit::{ + call_remote_socket, from_fn, from_fn_async, AnyContext, CallRemote, CliApp, Context, NoParams, + ParentHandler, Server, +}; use serde::{Deserialize, Serialize}; use tokio::runtime::{Handle, Runtime}; -use tokio::sync::OnceCell; -use url::Url; +use tokio::sync::{Mutex, OnceCell}; use yajrc::RpcError; #[derive(Parser, Deserialize)] @@ -18,7 +24,9 @@ use yajrc::RpcError; about = "This is a test cli application." )] struct CliConfig { - host: Option, + #[arg(long = "host")] + host: Option, + #[arg(short = 'c', long = "config")] config: Option, } impl CliConfig { @@ -40,7 +48,7 @@ impl CliConfig { } struct CliContextSeed { - host: Url, + host: PathBuf, rt: OnceCell, } #[derive(Clone)] @@ -53,6 +61,17 @@ impl Context for CliContext { self.0.rt.get().unwrap().handle().clone() } } +#[async_trait::async_trait] +impl CallRemote for CliContext { + async fn call_remote(&self, method: &str, params: Value) -> Result { + call_remote_socket( + tokio::net::UnixStream::connect(&self.0.host).await.unwrap(), + method, + params, + ) + .await + } +} fn make_cli() -> CliApp { CliApp::new( @@ -61,8 +80,7 @@ fn make_cli() -> CliApp { Ok(CliContext(Arc::new(CliContextSeed { host: config .host - .map(|h| h.parse().unwrap()) - .unwrap_or_else(|| "http://localhost:8080/rpc".parse().unwrap()), + .unwrap_or_else(|| Path::new("./rpc.sock").to_owned()), rt: OnceCell::new(), }))) }, @@ -70,16 +88,30 @@ fn make_cli() -> CliApp { ) } +struct ServerContextSeed { + state: Mutex, +} + +#[derive(Clone)] +struct ServerContext(Arc); +impl Context for ServerContext {} + +fn make_server() -> Server { + let ctx = ServerContext(Arc::new(ServerContextSeed { + state: Mutex::new(Value::Null), + })); + Server::new(move || ready(Ok(ctx.clone())), make_api()) +} + fn make_api() -> ParentHandler { - impl CliContext { - fn host(&self) -> &Url { - &self.0.host - } - } async fn a_hello(_: CliContext) -> Result { Ok::<_, RpcError>("Async Subcommand".to_string()) } #[derive(Debug, Clone, Deserialize, Serialize, Parser)] + struct EchoParams { + next: String, + } + #[derive(Debug, Clone, Deserialize, Serialize, Parser)] struct HelloParams { whom: String, } @@ -88,29 +120,20 @@ fn make_api() -> ParentHandler { donde: String, } ParentHandler::new() - .subcommand( + .subcommand_remote_cli::( "echo", - ParentHandler::::new() - .subcommand_no_cli( - "echo_no_cli", - from_fn(|c: CliContext| { - Ok::<_, RpcError>( - format!("Subcommand No Cli: Host {host}", host = c.host()).to_string(), - ) - }), - ) - .subcommand_no_cli( - "echo_cli", - from_fn(|c: CliContext| { - Ok::<_, RpcError>( - format!("Subcommand Cli: Host {host}", host = c.host()).to_string(), - ) - }), - ), + from_fn_async( + |c: ServerContext, EchoParams { next }: EchoParams| async move { + Ok::<_, RpcError>(std::mem::replace( + &mut *c.0.state.lock().await, + Value::String(Arc::new(next)), + )) + }, + ), ) .subcommand( "hello", - from_fn(|_: CliContext, HelloParams { whom }: HelloParams| { + from_fn(|_: AnyContext, HelloParams { whom }: HelloParams| { Ok::<_, RpcError>(format!("Hello {whom}").to_string()) }), ) @@ -123,7 +146,7 @@ fn make_api() -> ParentHandler { Ok::<_, RpcError>( format!( "Subcommand No Cli: Host {host} Donde = {donde}", - host = c.host() + host = c.0.host.display() ) .to_string(), ) @@ -137,8 +160,8 @@ fn make_api() -> ParentHandler { from_fn(|c: CliContext, _, InheritParams { donde }| { Ok::<_, RpcError>( format!( - "Subcommand No Cli: Host {host} Donde = {donde}", - host = c.host(), + "Root Command: Host {host} Donde = {donde}", + host = c.0.host.display(), ) .to_string(), ) @@ -167,3 +190,63 @@ pub fn internal_error(e: impl Display) -> RpcError { ..yajrc::INTERNAL_ERROR } } + +#[test] +fn test_cli() { + make_cli() + .run( + ["test-cli", "hello", "me"] + .into_iter() + .map(|s| OsString::from(s)), + ) + .unwrap(); + make_cli() + .run( + ["test-cli", "fizz", "buzz"] + .into_iter() + .map(|s| OsString::from(s)), + ) + .unwrap(); +} + +#[tokio::test] +async fn test_server() { + let path = Path::new(env!("CARGO_TARGET_TMPDIR")).join("rpc.sock"); + tokio::fs::remove_file(&path).await.unwrap_or_default(); + let server = make_server(); + let (shutdown, fut) = server + .run_unix(path.clone(), |err| eprintln!("IO Error: {err}")) + .unwrap(); + tokio::join!( + tokio::task::spawn_blocking(move || { + make_cli() + .run( + [ + "test-cli", + &format!("--host={}", path.display()), + "echo", + "foo", + ] + .into_iter() + .map(|s| OsString::from(s)), + ) + .unwrap(); + make_cli() + .run( + [ + "test-cli", + &format!("--host={}", path.display()), + "echo", + "bar", + ] + .into_iter() + .map(|s| OsString::from(s)), + ) + .unwrap(); + shutdown.shutdown() + }), + fut + ) + .0 + .unwrap(); +} From 496491a2f975cf1acede686400aeb0778bdaa89e Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 28 Dec 2023 17:26:46 -0700 Subject: [PATCH 21/79] add middleware and metadata --- rpc-toolkit/src/cli.rs | 5 +- rpc-toolkit/src/handler.rs | 149 ++++++++++++++- rpc-toolkit/src/server/http.rs | 321 ++++++++++++++++++++++++++++----- rpc-toolkit/src/server/mod.rs | 11 +- 4 files changed, 431 insertions(+), 55 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index da2ce96..fdee58a 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -1,4 +1,5 @@ use std::any::TypeId; +use std::collections::VecDeque; use std::ffi::OsString; use std::marker::PhantomData; @@ -63,14 +64,14 @@ impl let (method, params) = root_handler.cli_parse(&matches, ctx_ty)?; let res = root_handler.handle_sync(HandleAnyArgs { context: ctx.clone().upcast(), - parent_method: Vec::new(), + parent_method: VecDeque::new(), method: method.clone(), params: params.clone(), })?; root_handler.cli_display( HandleAnyArgs { context: ctx.upcast(), - parent_method: Vec::new(), + parent_method: VecDeque::new(), method, params, }, diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index 6086342..676ccba 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser}; use futures::Future; +use imbl_value::imbl::OrdMap; use imbl_value::Value; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; @@ -18,7 +19,7 @@ use crate::{CallRemote, CallRemoteHandler}; pub(crate) struct HandleAnyArgs { pub(crate) context: AnyContext, - pub(crate) parent_method: Vec<&'static str>, + pub(crate) parent_method: VecDeque<&'static str>, pub(crate) method: VecDeque<&'static str>, pub(crate) params: Value, } @@ -53,6 +54,11 @@ impl HandleAnyArgs { pub(crate) trait HandleAny: std::fmt::Debug + Send + Sync { fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result; + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value>; fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option>; } #[async_trait::async_trait] @@ -63,6 +69,13 @@ impl HandleAny for Arc { async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { self.deref().handle_async(handle_args).await } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.deref().metadata(method, ctx_ty) + } fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { self.deref().method_from_dots(method, ctx_ty) } @@ -122,6 +135,16 @@ impl HandleAny for DynHandler { DynHandler::WithCli(h) => h.handle_async(handle_args).await, } } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + match self { + DynHandler::WithoutCli(h) => h.metadata(method, ctx_ty), + DynHandler::WithCli(h) => h.metadata(method, ctx_ty), + } + } fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { match self { DynHandler::WithoutCli(h) => h.method_from_dots(method, ctx_ty), @@ -133,7 +156,7 @@ impl HandleAny for DynHandler { #[derive(Debug, Clone)] pub struct HandleArgs { pub context: Context, - pub parent_method: Vec<&'static str>, + pub parent_method: VecDeque<&'static str>, pub method: VecDeque<&'static str>, pub params: H::Params, pub inherited_params: H::InheritedParams, @@ -179,6 +202,14 @@ pub trait Handler: .await .unwrap() } + #[allow(unused_variables)] + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + OrdMap::new() + } fn contexts(&self) -> Option> { Context::type_ids_for(self) } @@ -235,6 +266,13 @@ where ) .map_err(internal_error) } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.handler.metadata(method, ctx_ty) + } fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { self.handler.method_from_dots(method, ctx_ty) } @@ -318,20 +356,27 @@ impl SubcommandMap { pub struct ParentHandler { _phantom: PhantomData<(Params, InheritedParams)>, pub(crate) subcommands: SubcommandMap, + metadata: OrdMap<&'static str, Value>, } impl ParentHandler { pub fn new() -> Self { Self { _phantom: PhantomData, subcommands: SubcommandMap(BTreeMap::new()), + metadata: OrdMap::new(), } } + pub fn with_metadata(mut self, key: &'static str, value: Value) -> Self { + self.metadata.insert(key, value); + self + } } impl Clone for ParentHandler { fn clone(&self) -> Self { Self { _phantom: PhantomData, subcommands: self.subcommands.clone(), + metadata: self.metadata.clone(), } } } @@ -431,6 +476,19 @@ where raw_params, }) } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.handler.metadata(method, ctx_ty) + } + fn contexts(&self) -> Option> { + self.handler.contexts() + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.handler.method_from_dots(method, ctx_ty) + } } impl CliBindings @@ -742,7 +800,7 @@ where ) -> Result { let cmd = method.pop_front(); if let Some(cmd) = cmd { - parent_method.push(cmd); + parent_method.push_back(cmd); } if let Some((_, sub_handler)) = &self.subcommands.get(context.inner_type_id(), cmd) { sub_handler.handle_sync(HandleAnyArgs { @@ -767,7 +825,7 @@ where ) -> Result { let cmd = method.pop_front(); if let Some(cmd) = cmd { - parent_method.push(cmd); + parent_method.push_back(cmd); } if let Some((_, sub_handler)) = self.subcommands.get(context.inner_type_id(), cmd) { sub_handler @@ -782,6 +840,18 @@ where Err(yajrc::METHOD_NOT_FOUND_ERROR) } } + fn metadata( + &self, + mut method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + let mut metadata = self.metadata.clone(); + if let Some((_, handler)) = self.subcommands.get(ctx_ty, method.pop_front()) { + handler.metadata(method, ctx_ty).union(metadata) + } else { + metadata + } + } fn contexts(&self) -> Option> { let mut set = BTreeSet::new(); for ctx_ty in self.subcommands.0.values().flat_map(|c| c.keys()) { @@ -872,7 +942,7 @@ where ) -> Result<(), Self::Err> { let cmd = method.pop_front(); if let Some(cmd) = cmd { - parent_method.push(cmd); + parent_method.push_back(cmd); } if let Some((_, DynHandler::WithCli(sub_handler))) = self.subcommands.get(context.inner_type_id(), cmd) @@ -896,6 +966,13 @@ pub struct FromFn { _phantom: PhantomData<(T, E, Args)>, function: F, blocking: bool, + metadata: OrdMap<&'static str, Value>, +} +impl FromFn { + pub fn with_metadata(mut self, key: &'static str, value: Value) -> Self { + self.metadata.insert(key, value); + self + } } impl Clone for FromFn { fn clone(&self) -> Self { @@ -903,6 +980,7 @@ impl Clone for FromFn { _phantom: PhantomData, function: self.function.clone(), blocking: self.blocking, + metadata: self.metadata.clone(), } } } @@ -929,6 +1007,7 @@ pub fn from_fn(function: F) -> FromFn { function, _phantom: PhantomData, blocking: false, + metadata: OrdMap::new(), } } @@ -937,18 +1016,21 @@ pub fn from_fn_blocking(function: F) -> FromFn { function, _phantom: PhantomData, blocking: true, + metadata: OrdMap::new(), } } pub struct FromFnAsync { _phantom: PhantomData<(Fut, T, E, Args)>, function: F, + metadata: OrdMap<&'static str, Value>, } impl Clone for FromFnAsync { fn clone(&self) -> Self { Self { _phantom: PhantomData, function: self.function.clone(), + metadata: self.metadata.clone(), } } } @@ -972,6 +1054,7 @@ pub fn from_fn_async(function: F) -> FromFnAsync, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } } impl HandlerTypes for FromFnAsync where @@ -1032,6 +1122,13 @@ where async fn handle_async(&self, _: HandleArgs) -> Result { (self.function)().await } + fn metadata( + &self, + mut method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } } impl HandlerTypes for FromFn @@ -1067,6 +1164,13 @@ where self.handle_async_with_sync(handle_args).await } } + fn metadata( + &self, + mut method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } } impl HandlerTypes for FromFnAsync where @@ -1096,6 +1200,13 @@ where ) -> Result { (self.function)(handle_args.context).await } + fn metadata( + &self, + mut method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } } impl HandlerTypes for FromFn @@ -1136,6 +1247,13 @@ where self.handle_async_with_sync(handle_args).await } } + fn metadata( + &self, + mut method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } } impl HandlerTypes for FromFnAsync @@ -1172,6 +1290,13 @@ where } = handle_args; (self.function)(context, params).await } + fn metadata( + &self, + mut method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } } impl HandlerTypes @@ -1219,6 +1344,13 @@ where self.handle_async_with_sync(handle_args).await } } + fn metadata( + &self, + mut method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } } impl HandlerTypes @@ -1261,6 +1393,13 @@ where } = handle_args; (self.function)(context, params, inherited_params).await } + fn metadata( + &self, + mut method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } } impl CliBindings for FromFn diff --git a/rpc-toolkit/src/server/http.rs b/rpc-toolkit/src/server/http.rs index ab40aeb..00f092e 100644 --- a/rpc-toolkit/src/server/http.rs +++ b/rpc-toolkit/src/server/http.rs @@ -1,60 +1,287 @@ +use std::any::TypeId; use std::task::Context; -use futures::future::BoxFuture; +use futures::future::{join_all, BoxFuture}; +use futures::FutureExt; +use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; use http::request::Parts; +use http_body_util::BodyExt; use hyper::body::{Bytes, Incoming}; +use hyper::service::Service; use hyper::{Request, Response}; -use yajrc::{RpcRequest, RpcResponse}; +use imbl_value::Value; +use serde::de::DeserializeOwned; +use serde::Serialize; +use yajrc::{RpcError, RpcMethod}; + +use crate::server::{RpcRequest, RpcResponse, SingleOrBatchRpcRequest}; +use crate::util::{internal_error, parse_error}; +use crate::{handler, HandleAny, Server}; + +const FALLBACK_ERROR: &str = "{\"error\":{\"code\":-32603,\"message\":\"Internal error\",\"data\":\"Failed to serialize rpc response\"}}"; + +pub fn fallback_rpc_error_response() -> Response { + Response::builder() + .header(CONTENT_TYPE, "application/json") + .header(CONTENT_LENGTH, FALLBACK_ERROR.len()) + .body(Bytes::from_static(FALLBACK_ERROR.as_bytes())) + .unwrap() +} + +pub fn json_http_response(t: &T) -> Response { + let body = match serde_json::to_vec(t) { + Ok(a) => a, + Err(_) => return fallback_rpc_error_response(), + }; + Response::builder() + .header(CONTENT_TYPE, "application/json") + .header(CONTENT_LENGTH, body.len()) + .body(Bytes::from(body)) + .unwrap_or_else(|_| fallback_rpc_error_response()) +} type BoxBody = http_body_util::combinators::BoxBody; #[async_trait::async_trait] -pub trait Middleware { - type ProcessHttpRequestResult; +pub trait Middleware: Clone + Send + Sync + 'static { + type Metadata: DeserializeOwned + Send + 'static; + #[allow(unused_variables)] async fn process_http_request( - &self, - req: &mut Request, - ) -> Result>>; - type ProcessRpcRequestResult; + &mut self, + context: &Context, + request: &mut Request, + ) -> Result<(), Response> { + Ok(()) + } + #[allow(unused_variables)] async fn process_rpc_request( - &self, - prev: Self::ProcessHttpRequestResult, - // metadata: &Context::Metadata, - req: &mut RpcRequest, - ) -> Result; - type ProcessRpcResponseResult; - async fn process_rpc_response( - &self, - prev: Self::ProcessRpcRequestResult, - res: &mut RpcResponse, - ) -> Self::ProcessRpcResponseResult; - async fn process_http_response( - &self, - prev: Self::ProcessRpcResponseResult, - res: &mut Response, - ); + &mut self, + metadata: Self::Metadata, + request: &mut RpcRequest, + ) -> Result<(), RpcResponse> { + Ok(()) + } + #[allow(unused_variables)] + async fn process_rpc_response(&mut self, response: &mut RpcResponse) {} + #[allow(unused_variables)] + async fn process_http_response(&mut self, response: &mut Response) {} } -// pub struct DynMiddleware { -// process_http_request: Box< -// dyn for<'a> Fn( -// &'a mut Request, -// ) -> BoxFuture< -// 'a, -// Result, hyper::Result>>, -// > + Send -// + Sync, -// >, -// } -// type DynProcessRpcRequest<'m, Context: crate::Context> = Box< -// dyn for<'a> FnOnce( -// &'a Context::Metadata, -// &'a mut RpcRequest, -// ) -// -> BoxFuture<'a, Result, DynSkipHandler<'m>>> -// + Send -// + Sync -// + 'm, -// >; -// type DynProcessRpcResponse<'m> = -// Box FnOnce(&'a mut RpcResponse) -> BoxFuture<'a, DynProcessHttpResponse<'m>>>; +#[allow(private_bounds)] +trait _Middleware: Send + Sync { + fn dyn_clone(&self) -> DynMiddleware; + fn process_http_request<'a>( + &'a mut self, + context: &'a Context, + request: &'a mut Request, + ) -> BoxFuture<'a, Result<(), Response>>; + fn process_rpc_request<'a>( + &'a mut self, + metadata: Value, + request: &'a mut RpcRequest, + ) -> BoxFuture<'a, Result<(), RpcResponse>>; + fn process_rpc_response<'a>(&'a mut self, response: &'a mut RpcResponse) -> BoxFuture<'a, ()>; + fn process_http_response<'a>( + &'a mut self, + response: &'a mut Response, + ) -> BoxFuture<'a, ()>; +} +impl + Send + Sync> _Middleware for T { + fn dyn_clone(&self) -> DynMiddleware { + DynMiddleware(Box::new(::clone(&self))) + } + fn process_http_request<'a>( + &'a mut self, + context: &'a Context, + request: &'a mut Request, + ) -> BoxFuture<'a, Result<(), Response>> { + >::process_http_request(self, context, request) + } + fn process_rpc_request<'a>( + &'a mut self, + metadata: Value, + request: &'a mut RpcRequest, + ) -> BoxFuture<'a, Result<(), RpcResponse>> { + >::process_rpc_request( + self, + match imbl_value::from_value(metadata) { + Ok(a) => a, + Err(e) => return async { Err(internal_error(e).into()) }.boxed(), + }, + request, + ) + } + fn process_rpc_response<'a>(&'a mut self, response: &'a mut RpcResponse) -> BoxFuture<'a, ()> { + >::process_rpc_response(self, response) + } + fn process_http_response<'a>( + &'a mut self, + response: &'a mut Response, + ) -> BoxFuture<'a, ()> { + >::process_http_response(self, response) + } +} + +struct DynMiddleware(Box>); +impl Clone for DynMiddleware { + fn clone(&self) -> Self { + self.0.dyn_clone() + } +} + +pub struct HttpServer { + inner: Server, + middleware: Vec>, +} +impl Clone for HttpServer { + fn clone(&self) -> Self { + Self { + inner: self.inner.clone(), + middleware: self.middleware.clone(), + } + } +} +impl Server { + pub fn for_http(self) -> HttpServer { + HttpServer { + inner: self, + middleware: Vec::new(), + } + } + pub fn middleware>(self, middleware: T) -> HttpServer { + self.for_http().middleware(middleware) + } +} +impl HttpServer { + pub fn middleware>(mut self, middleware: T) -> Self { + self.middleware.push(DynMiddleware(Box::new(middleware))); + self + } + async fn process_http_request(&self, mut req: Request) -> Response { + let mut mid = self.middleware.clone(); + match async { + let ctx = (self.inner.make_ctx)().await?; + for middleware in mid.iter_mut().rev() { + if let Err(e) = middleware.0.process_http_request(&ctx, &mut req).await { + return Ok::<_, RpcError>(e); + } + } + let (_, body) = req.into_parts(); + match serde_json::from_slice::( + &*body.collect().await.map_err(internal_error)?.to_bytes(), + ) + .map_err(parse_error)? + { + SingleOrBatchRpcRequest::Single(rpc_req) => { + let mut res = + json_http_response(&self.process_rpc_request(&mut mid, rpc_req).await); + for middleware in mid.iter_mut() { + middleware.0.process_http_response(&mut res).await; + } + Ok(res) + } + SingleOrBatchRpcRequest::Batch(rpc_reqs) => { + let (mids, rpc_res): (Vec<_>, Vec<_>) = + join_all(rpc_reqs.into_iter().map(|rpc_req| async { + let mut mid = mid.clone(); + let res = self.process_rpc_request(&mut mid, rpc_req).await; + (mid, res) + })) + .await + .into_iter() + .unzip(); + let mut res = json_http_response(&rpc_res); + for mut mid in mids.into_iter().fold( + vec![Vec::with_capacity(rpc_res.len()); mid.len()], + |mut acc, x| { + for (idx, middleware) in x.into_iter().enumerate() { + acc[idx].push(middleware); + } + acc + }, + ) { + for middleware in mid.iter_mut() { + middleware.0.process_http_response(&mut res).await; + } + } + Ok(res) + } + } + } + .await + { + Ok(a) => a, + Err(e) => json_http_response(&RpcResponse { + id: None, + result: Err(e), + }), + } + } + async fn process_rpc_request( + &self, + mid: &mut Vec>, + mut req: RpcRequest, + ) -> RpcResponse { + let metadata = Value::Object( + self.inner + .root_handler + .metadata( + match self + .inner + .root_handler + .method_from_dots(req.method.as_str(), TypeId::of::()) + { + Some(a) => a, + None => { + return RpcResponse { + id: req.id, + result: Err(yajrc::METHOD_NOT_FOUND_ERROR), + } + } + }, + TypeId::of::(), + ) + .into_iter() + .map(|(key, value)| (key.into(), value)) + .collect(), + ); + let mut res = async { + for middleware in mid.iter_mut().rev() { + if let Err(res) = middleware + .0 + .process_rpc_request(metadata.clone(), &mut req) + .await + { + return res; + } + } + self.inner.handle_single_request(req).await + } + .await; + for middleware in mid.iter_mut() { + middleware.0.process_rpc_response(&mut res).await; + } + res + } + pub fn handle(&self, req: Request) -> BoxFuture<'static, Response> { + let server = self.clone(); + async move { + server + .process_http_request(req.map(|b| BoxBody::new(b))) + .await + } + .boxed() + } +} + +impl Service> for HttpServer { + type Response = Response; + type Error = hyper::Error; + type Future = futures::future::Map< + BoxFuture<'static, Self::Response>, + fn(Self::Response) -> Result, + >; + fn call(&self, req: Request) -> Self::Future { + self.handle(req).map(Ok) + } +} diff --git a/rpc-toolkit/src/server/mod.rs b/rpc-toolkit/src/server/mod.rs index 5920717..a56f6f9 100644 --- a/rpc-toolkit/src/server/mod.rs +++ b/rpc-toolkit/src/server/mod.rs @@ -1,4 +1,5 @@ use std::any::TypeId; +use std::collections::VecDeque; use std::sync::Arc; use futures::future::{join_all, BoxFuture}; @@ -24,6 +25,14 @@ pub struct Server { make_ctx: Arc BoxFuture<'static, Result> + Send + Sync>, root_handler: Arc>, } +impl Clone for Server { + fn clone(&self) -> Self { + Self { + make_ctx: self.make_ctx.clone(), + root_handler: self.root_handler.clone(), + } + } +} impl Server { pub fn new< MakeCtx: Fn() -> Fut + Send + Sync + 'static, @@ -54,7 +63,7 @@ impl Server { root_handler .handle_async(HandleAnyArgs { context: make_ctx().await?.upcast(), - parent_method: Vec::new(), + parent_method: VecDeque::new(), method: method.ok_or_else(|| yajrc::METHOD_NOT_FOUND_ERROR)?, params, }) From 689d55c31d6f1c173ee34893273d0ebdcbf21c28 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 28 Dec 2023 17:36:04 -0700 Subject: [PATCH 22/79] switch to ordmap for better cloning --- rpc-toolkit/src/handler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs index 676ccba..6d85d31 100644 --- a/rpc-toolkit/src/handler.rs +++ b/rpc-toolkit/src/handler.rs @@ -321,7 +321,7 @@ impl<'a> std::borrow::Borrow> for Name { } #[derive(Debug, Clone)] -pub(crate) struct SubcommandMap(pub(crate) BTreeMap, DynHandler>>); +pub(crate) struct SubcommandMap(pub(crate) OrdMap, DynHandler>>); impl SubcommandMap { fn insert( &mut self, @@ -362,7 +362,7 @@ impl ParentHandler { pub fn new() -> Self { Self { _phantom: PhantomData, - subcommands: SubcommandMap(BTreeMap::new()), + subcommands: SubcommandMap(OrdMap::new()), metadata: OrdMap::new(), } } From 4cbbf1a0a7c8bb35ea1de267ed914c2903c95d7c Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 2 Jan 2024 11:56:45 -0700 Subject: [PATCH 23/79] wip --- Cargo.lock | 1 + rpc-toolkit/Cargo.toml | 1 + rpc-toolkit/src/cli.rs | 32 +- rpc-toolkit/src/context.rs | 44 +- rpc-toolkit/src/handler.rs | 1501 --------------------------- rpc-toolkit/src/handler/adapters.rs | 485 +++++++++ rpc-toolkit/src/handler/from_fn.rs | 601 +++++++++++ rpc-toolkit/src/handler/marker.rs | 1 + rpc-toolkit/src/handler/mod.rs | 317 ++++++ rpc-toolkit/src/handler/parent.rs | 520 ++++++++++ rpc-toolkit/src/server/mod.rs | 4 +- rpc-toolkit/tests/handler.rs | 5 +- 12 files changed, 1964 insertions(+), 1548 deletions(-) delete mode 100644 rpc-toolkit/src/handler.rs create mode 100644 rpc-toolkit/src/handler/adapters.rs create mode 100644 rpc-toolkit/src/handler/from_fn.rs create mode 100644 rpc-toolkit/src/handler/marker.rs create mode 100644 rpc-toolkit/src/handler/mod.rs create mode 100644 rpc-toolkit/src/handler/parent.rs diff --git a/Cargo.lock b/Cargo.lock index 5b323fd..17d221d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1033,6 +1033,7 @@ dependencies = [ "http-body-util", "hyper 1.0.1", "imbl-value", + "itertools", "lazy_format", "lazy_static", "openssl", diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml index 146f205..ada859c 100644 --- a/rpc-toolkit/Cargo.toml +++ b/rpc-toolkit/Cargo.toml @@ -23,6 +23,7 @@ futures = "0.3" http = "1" http-body-util = "0.1" hyper = { version = "1", features = ["server", "http1", "http2", "client"] } +itertools = "0.12" imbl-value = "0.1" lazy_format = "2" lazy_static = "1.4" diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index fdee58a..c518842 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -165,11 +165,11 @@ pub async fn call_remote_socket( .result } -pub struct CallRemoteHandler { - _phantom: PhantomData, +pub struct CallRemoteHandler { + _phantom: PhantomData, handler: RemoteHandler, } -impl CallRemoteHandler { +impl CallRemoteHandler { pub fn new(handler: RemoteHandler) -> Self { Self { _phantom: PhantomData, @@ -177,9 +177,7 @@ impl CallRemoteHandler Clone - for CallRemoteHandler -{ +impl Clone for CallRemoteHandler { fn clone(&self) -> Self { Self { _phantom: PhantomData, @@ -187,17 +185,14 @@ impl Clone } } } -impl std::fmt::Debug - for CallRemoteHandler -{ +impl std::fmt::Debug for CallRemoteHandler { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("CallRemoteHandler").finish() } } -impl HandlerTypes for CallRemoteHandler +impl HandlerTypes for CallRemoteHandler where - RemoteContext: IntoContext, RemoteHandler: HandlerTypes, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, @@ -210,16 +205,15 @@ where type Err = RemoteHandler::Err; } #[async_trait::async_trait] -impl Handler - for CallRemoteHandler +impl Handler for CallRemoteHandler where - RemoteContext: IntoContext, - RemoteHandler: Handler, + RemoteHandler: Handler, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, { + type Context = Context; async fn handle_async( &self, handle_args: HandleArgs, @@ -245,17 +239,15 @@ where } } } -// #[async_trait::async_trait] -impl CliBindings - for CallRemoteHandler +impl CliBindings for CallRemoteHandler where - RemoteContext: IntoContext, - RemoteHandler: Handler + CliBindings, + RemoteHandler: Handler + CliBindings, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, { + type Context = Context; fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { self.handler.cli_command(ctx_ty) } diff --git a/rpc-toolkit/src/context.rs b/rpc-toolkit/src/context.rs index 0683fd2..a5a0deb 100644 --- a/rpc-toolkit/src/context.rs +++ b/rpc-toolkit/src/context.rs @@ -1,10 +1,8 @@ use std::any::{Any, TypeId}; -use std::collections::BTreeSet; +use imbl_value::imbl::OrdSet; use tokio::runtime::Handle; -use crate::Handler; - pub trait Context: Any + Send + Sync + 'static { fn inner_type_id(&self) -> TypeId { ::type_id(&self) @@ -17,7 +15,7 @@ pub trait Context: Any + Send + Sync + 'static { #[allow(private_bounds)] pub trait IntoContext: sealed::Sealed + Any + Send + Sync + Sized + 'static { fn runtime(&self) -> Handle; - fn type_ids_for + ?Sized>(handler: &H) -> Option>; + fn type_ids() -> Option>; fn inner_type_id(&self) -> TypeId; fn upcast(self) -> AnyContext; fn downcast(value: AnyContext) -> Result; @@ -27,8 +25,8 @@ impl IntoContext for C { fn runtime(&self) -> Handle { ::runtime(&self) } - fn type_ids_for + ?Sized>(_: &H) -> Option> { - let mut set = BTreeSet::new(); + fn type_ids() -> Option> { + let mut set = OrdSet::new(); set.insert(TypeId::of::()); Some(set) } @@ -51,38 +49,38 @@ pub enum EitherContext { C1(C1), C2(C2), } -impl IntoContext for EitherContext { +impl IntoContext for EitherContext { fn runtime(&self) -> Handle { match self { Self::C1(a) => a.runtime(), Self::C2(a) => a.runtime(), } } - fn type_ids_for + ?Sized>(_: &H) -> Option> { - let mut set = BTreeSet::new(); - set.insert(TypeId::of::()); - set.insert(TypeId::of::()); + fn type_ids() -> Option> { + let mut set = OrdSet::new(); + set.extend(C1::type_ids()?); + set.extend(C2::type_ids()?); Some(set) } fn inner_type_id(&self) -> TypeId { match self { - EitherContext::C1(c) => c.type_id(), - EitherContext::C2(c) => c.type_id(), + EitherContext::C1(c) => c.inner_type_id(), + EitherContext::C2(c) => c.inner_type_id(), } } fn downcast(value: AnyContext) -> Result { - if value.inner_type_id() == TypeId::of::() { - Ok(EitherContext::C1(C1::downcast(value)?)) - } else if value.inner_type_id() == TypeId::of::() { - Ok(EitherContext::C2(C2::downcast(value)?)) - } else { - Err(value) + match C1::downcast(value) { + Ok(a) => Ok(EitherContext::C1(a)), + Err(value) => match C2::downcast(value) { + Ok(a) => Ok(EitherContext::C2(a)), + Err(value) => Err(value), + }, } } fn upcast(self) -> AnyContext { match self { - Self::C1(c) => AnyContext::new(c), - Self::C2(c) => AnyContext::new(c), + Self::C1(c) => c.upcast(), + Self::C2(c) => c.upcast(), } } } @@ -102,7 +100,7 @@ impl IntoContext for AnyContext { fn runtime(&self) -> Handle { self.0.runtime() } - fn type_ids_for + ?Sized>(_: &H) -> Option> { + fn type_ids() -> Option> { None } fn inner_type_id(&self) -> TypeId { @@ -119,6 +117,6 @@ impl IntoContext for AnyContext { mod sealed { pub(crate) trait Sealed {} impl Sealed for C {} - impl Sealed for super::EitherContext {} + impl Sealed for super::EitherContext {} impl Sealed for super::AnyContext {} } diff --git a/rpc-toolkit/src/handler.rs b/rpc-toolkit/src/handler.rs deleted file mode 100644 index 6d85d31..0000000 --- a/rpc-toolkit/src/handler.rs +++ /dev/null @@ -1,1501 +0,0 @@ -use std::any::TypeId; -use std::collections::{BTreeMap, BTreeSet, VecDeque}; -use std::fmt::Display; -use std::marker::PhantomData; -use std::ops::Deref; -use std::sync::Arc; - -use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser}; -use futures::Future; -use imbl_value::imbl::OrdMap; -use imbl_value::Value; -use serde::de::DeserializeOwned; -use serde::{Deserialize, Serialize}; -use yajrc::RpcError; - -use crate::context::{AnyContext, IntoContext}; -use crate::util::{combine, internal_error, invalid_params, Flat}; -use crate::{CallRemote, CallRemoteHandler}; - -pub(crate) struct HandleAnyArgs { - pub(crate) context: AnyContext, - pub(crate) parent_method: VecDeque<&'static str>, - pub(crate) method: VecDeque<&'static str>, - pub(crate) params: Value, -} -impl HandleAnyArgs { - fn downcast(self) -> Result, imbl_value::Error> - where - H: HandlerTypes, - H::Params: DeserializeOwned, - H::InheritedParams: DeserializeOwned, - { - let Self { - context, - parent_method, - method, - params, - } = self; - Ok(HandleArgs { - context: Context::downcast(context).map_err(|_| imbl_value::Error { - kind: imbl_value::ErrorKind::Deserialization, - source: serde::ser::Error::custom("context does not match expected"), - })?, - parent_method, - method, - params: imbl_value::from_value(params.clone())?, - inherited_params: imbl_value::from_value(params.clone())?, - raw_params: params, - }) - } -} - -#[async_trait::async_trait] -pub(crate) trait HandleAny: std::fmt::Debug + Send + Sync { - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; - async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result; - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value>; - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option>; -} -#[async_trait::async_trait] -impl HandleAny for Arc { - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { - self.deref().handle_sync(handle_args) - } - async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { - self.deref().handle_async(handle_args).await - } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.deref().metadata(method, ctx_ty) - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.deref().method_from_dots(method, ctx_ty) - } -} - -pub(crate) trait CliBindingsAny { - fn cli_command(&self, ctx_ty: TypeId) -> Command; - fn cli_parse( - &self, - matches: &ArgMatches, - ctx_ty: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; - fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError>; -} - -pub trait CliBindings: HandlerTypes { - fn cli_command(&self, ctx_ty: TypeId) -> Command; - fn cli_parse( - &self, - matches: &ArgMatches, - ctx_ty: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; - fn cli_display( - &self, - handle_args: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err>; -} - -pub trait PrintCliResult: HandlerTypes { - fn print( - &self, - handle_args: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err>; -} - -pub(crate) trait HandleAnyWithCli: HandleAny + CliBindingsAny {} -impl HandleAnyWithCli for T {} - -#[derive(Debug, Clone)] -pub(crate) enum DynHandler { - WithoutCli(Arc), - WithCli(Arc), -} -#[async_trait::async_trait] -impl HandleAny for DynHandler { - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { - match self { - DynHandler::WithoutCli(h) => h.handle_sync(handle_args), - DynHandler::WithCli(h) => h.handle_sync(handle_args), - } - } - async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { - match self { - DynHandler::WithoutCli(h) => h.handle_async(handle_args).await, - DynHandler::WithCli(h) => h.handle_async(handle_args).await, - } - } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - match self { - DynHandler::WithoutCli(h) => h.metadata(method, ctx_ty), - DynHandler::WithCli(h) => h.metadata(method, ctx_ty), - } - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - match self { - DynHandler::WithoutCli(h) => h.method_from_dots(method, ctx_ty), - DynHandler::WithCli(h) => h.method_from_dots(method, ctx_ty), - } - } -} - -#[derive(Debug, Clone)] -pub struct HandleArgs { - pub context: Context, - pub parent_method: VecDeque<&'static str>, - pub method: VecDeque<&'static str>, - pub params: H::Params, - pub inherited_params: H::InheritedParams, - pub raw_params: Value, -} - -pub trait HandlerTypes { - type Params: Send + Sync; - type InheritedParams: Send + Sync; - type Ok: Send + Sync; - type Err: Send + Sync; -} - -#[async_trait::async_trait] -pub trait Handler: - HandlerTypes + std::fmt::Debug + Clone + Send + Sync + 'static -{ - fn handle_sync(&self, handle_args: HandleArgs) -> Result { - handle_args - .context - .runtime() - .block_on(self.handle_async(handle_args)) - } - async fn handle_async( - &self, - handle_args: HandleArgs, - ) -> Result; - async fn handle_async_with_sync( - &self, - handle_args: HandleArgs, - ) -> Result { - self.handle_sync(handle_args) - } - async fn handle_async_with_sync_blocking( - &self, - handle_args: HandleArgs, - ) -> Result { - let s = self.clone(); - handle_args - .context - .runtime() - .spawn_blocking(move || s.handle_sync(handle_args)) - .await - .unwrap() - } - #[allow(unused_variables)] - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - OrdMap::new() - } - fn contexts(&self) -> Option> { - Context::type_ids_for(self) - } - #[allow(unused_variables)] - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - if method.is_empty() { - Some(VecDeque::new()) - } else { - None - } - } -} - -pub(crate) struct AnyHandler { - _ctx: PhantomData, - handler: H, -} -impl AnyHandler { - pub(crate) fn new(handler: H) -> Self { - Self { - _ctx: PhantomData, - handler, - } - } -} -impl std::fmt::Debug for AnyHandler { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("AnyHandler").field(&self.handler).finish() - } -} - -#[async_trait::async_trait] -impl> HandleAny for AnyHandler -where - H::Params: DeserializeOwned, - H::InheritedParams: DeserializeOwned, - H::Ok: Serialize, - RpcError: From, -{ - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { - imbl_value::to_value( - &self - .handler - .handle_sync(handle_args.downcast().map_err(invalid_params)?)?, - ) - .map_err(internal_error) - } - async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { - imbl_value::to_value( - &self - .handler - .handle_async(handle_args.downcast().map_err(invalid_params)?) - .await?, - ) - .map_err(internal_error) - } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.handler.metadata(method, ctx_ty) - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.handler.method_from_dots(method, ctx_ty) - } -} - -impl> CliBindingsAny for AnyHandler -where - H: CliBindings, - H::Params: DeserializeOwned, - H::InheritedParams: DeserializeOwned, - H::Ok: Serialize + DeserializeOwned, - RpcError: From, -{ - fn cli_command(&self, ctx_ty: TypeId) -> Command { - self.handler.cli_command(ctx_ty) - } - fn cli_parse( - &self, - matches: &ArgMatches, - ctx_ty: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.handler.cli_parse(matches, ctx_ty) - } - fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError> { - self.handler - .cli_display( - handle_args.downcast().map_err(invalid_params)?, - imbl_value::from_value(result).map_err(internal_error)?, - ) - .map_err(RpcError::from) - } -} - -#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] -pub struct NoParams {} - -#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] -pub enum Never {} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) struct Name(pub(crate) Option<&'static str>); -impl<'a> std::borrow::Borrow> for Name { - fn borrow(&self) -> &Option<&'a str> { - &self.0 - } -} - -#[derive(Debug, Clone)] -pub(crate) struct SubcommandMap(pub(crate) OrdMap, DynHandler>>); -impl SubcommandMap { - fn insert( - &mut self, - ctx_tys: Option>, - name: Option<&'static str>, - handler: DynHandler, - ) { - let mut for_name = self.0.remove(&name).unwrap_or_default(); - if let Some(ctx_tys) = ctx_tys { - for ctx_ty in ctx_tys { - for_name.insert(Some(ctx_ty), handler.clone()); - } - } else { - for_name.insert(None, handler); - } - self.0.insert(Name(name), for_name); - } - - fn get<'a>(&'a self, ctx_ty: TypeId, name: Option<&str>) -> Option<(Name, &'a DynHandler)> { - if let Some((name, for_name)) = self.0.get_key_value(&name) { - if let Some(for_ctx) = for_name.get(&Some(ctx_ty)) { - Some((*name, for_ctx)) - } else { - for_name.get(&None).map(|h| (*name, h)) - } - } else { - None - } - } -} - -pub struct ParentHandler { - _phantom: PhantomData<(Params, InheritedParams)>, - pub(crate) subcommands: SubcommandMap, - metadata: OrdMap<&'static str, Value>, -} -impl ParentHandler { - pub fn new() -> Self { - Self { - _phantom: PhantomData, - subcommands: SubcommandMap(OrdMap::new()), - metadata: OrdMap::new(), - } - } - pub fn with_metadata(mut self, key: &'static str, value: Value) -> Self { - self.metadata.insert(key, value); - self - } -} -impl Clone for ParentHandler { - fn clone(&self) -> Self { - Self { - _phantom: PhantomData, - subcommands: self.subcommands.clone(), - metadata: self.metadata.clone(), - } - } -} -impl std::fmt::Debug for ParentHandler { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("ParentHandler") - .field(&self.subcommands) - .finish() - } -} - -struct InheritanceHandler { - _phantom: PhantomData<(Context, Params, InheritedParams)>, - handler: H, - inherit: F, -} -impl Clone - for InheritanceHandler -{ - fn clone(&self) -> Self { - Self { - _phantom: PhantomData, - handler: self.handler.clone(), - inherit: self.inherit.clone(), - } - } -} -impl std::fmt::Debug - for InheritanceHandler -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("InheritanceHandler") - .field(&self.handler) - .finish() - } -} -impl HandlerTypes - for InheritanceHandler -where - Context: IntoContext, - H: HandlerTypes, - Params: Send + Sync, - InheritedParams: Send + Sync, -{ - type Params = H::Params; - type InheritedParams = Flat; - type Ok = H::Ok; - type Err = H::Err; -} -#[async_trait::async_trait] -impl Handler - for InheritanceHandler -where - Context: IntoContext, - Params: Send + Sync + 'static, - InheritedParams: Send + Sync + 'static, - H: Handler, - F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, -{ - fn handle_sync( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - ) -> Result { - self.handler.handle_sync(HandleArgs { - context, - parent_method, - method, - params, - inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), - raw_params, - }) - } - async fn handle_async( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - ) -> Result { - self.handler.handle_sync(HandleArgs { - context, - parent_method, - method, - params, - inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), - raw_params, - }) - } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.handler.metadata(method, ctx_ty) - } - fn contexts(&self) -> Option> { - self.handler.contexts() - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.handler.method_from_dots(method, ctx_ty) - } -} - -impl CliBindings - for InheritanceHandler -where - Context: IntoContext, - Params: Send + Sync + 'static, - InheritedParams: Send + Sync + 'static, - H: CliBindings, - F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, -{ - fn cli_command(&self, ctx_ty: TypeId) -> Command { - self.handler.cli_command(ctx_ty) - } - fn cli_parse( - &self, - matches: &ArgMatches, - ctx_ty: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.handler.cli_parse(matches, ctx_ty) - } - fn cli_display( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.handler.cli_display( - HandleArgs { - context, - parent_method, - method, - params, - inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), - raw_params, - }, - result, - ) - } -} - -impl ParentHandler { - pub fn subcommand(mut self, name: &'static str, handler: H) -> Self - where - Context: IntoContext, - H: HandlerTypes - + Handler - + CliBindings - + 'static, - H::Params: DeserializeOwned, - H::Ok: Serialize + DeserializeOwned, - RpcError: From, - { - self.subcommands.insert( - handler.contexts(), - name.into(), - DynHandler::WithCli(Arc::new(AnyHandler::new(handler))), - ); - self - } - pub fn subcommand_remote_cli( - mut self, - name: &'static str, - handler: H, - ) -> Self - where - ServerContext: IntoContext, - CliContext: IntoContext + CallRemote, - H: HandlerTypes - + Handler - + CliBindings - + 'static, - H::Params: Serialize + DeserializeOwned, - H::Ok: Serialize + DeserializeOwned, - RpcError: From, - H::Err: From, - CallRemoteHandler: Handler, - as HandlerTypes>::Ok: Serialize + DeserializeOwned, - as HandlerTypes>::Params: Serialize + DeserializeOwned, - as HandlerTypes>::InheritedParams: DeserializeOwned, - RpcError: From< as HandlerTypes>::Err>, - { - self.subcommands.insert( - handler.contexts(), - name.into(), - DynHandler::WithoutCli(Arc::new(AnyHandler::new(handler.clone()))), - ); - let call_remote = CallRemoteHandler::::new(handler); - self.subcommands.insert( - call_remote.contexts(), - name.into(), - DynHandler::WithCli(Arc::new(AnyHandler::new(call_remote))), - ); - self - } - pub fn subcommand_no_cli(mut self, name: &'static str, handler: H) -> Self - where - Context: IntoContext, - H: Handler + 'static, - H::Params: DeserializeOwned, - H::Ok: Serialize, - RpcError: From, - { - self.subcommands.insert( - handler.contexts(), - name.into(), - DynHandler::WithoutCli(Arc::new(AnyHandler::new(handler))), - ); - self - } -} -impl ParentHandler -where - Params: DeserializeOwned + 'static, - InheritedParams: DeserializeOwned + 'static, -{ - pub fn subcommand_with_inherited( - mut self, - name: &'static str, - handler: H, - inherit: F, - ) -> Self - where - Context: IntoContext, - H: Handler + CliBindings + 'static, - H::Params: DeserializeOwned, - H::Ok: Serialize + DeserializeOwned, - RpcError: From, - F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, - { - self.subcommands.insert( - handler.contexts(), - name.into(), - DynHandler::WithCli(Arc::new(AnyHandler { - _ctx: PhantomData, - handler: InheritanceHandler:: { - _phantom: PhantomData, - handler, - inherit, - }, - })), - ); - self - } - pub fn subcommand_with_inherited_remote_cli( - mut self, - name: &'static str, - handler: H, - inherit: F, - ) -> Self - where - ServerContext: IntoContext, - CliContext: IntoContext + CallRemote, - H: HandlerTypes + Handler + CliBindings + 'static, - H::Params: Serialize + DeserializeOwned, - H::Ok: Serialize + DeserializeOwned, - RpcError: From, - H::Err: From, - CallRemoteHandler: - Handler, - as HandlerTypes>::Ok: Serialize + DeserializeOwned, - as HandlerTypes>::Params: Serialize + DeserializeOwned, - as HandlerTypes>::InheritedParams: - Serialize + DeserializeOwned, - RpcError: From< as HandlerTypes>::Err>, - F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, - { - self.subcommands.insert( - handler.contexts(), - name.into(), - DynHandler::WithoutCli(Arc::new(AnyHandler::new(InheritanceHandler::< - ServerContext, - Params, - InheritedParams, - H, - F, - > { - _phantom: PhantomData, - handler: handler.clone(), - inherit: inherit.clone(), - }))), - ); - let call_remote = CallRemoteHandler::::new(handler); - self.subcommands.insert( - call_remote.contexts(), - name.into(), - DynHandler::WithCli(Arc::new(AnyHandler::new(InheritanceHandler::< - CliContext, - Params, - InheritedParams, - CallRemoteHandler, - F, - > { - _phantom: PhantomData, - handler: call_remote, - inherit, - }))), - ); - self - } - pub fn subcommand_with_inherited_no_cli( - mut self, - name: &'static str, - handler: H, - inherit: F, - ) -> Self - where - Context: IntoContext, - H: Handler + 'static, - H::Params: DeserializeOwned, - H::Ok: Serialize, - RpcError: From, - F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, - { - self.subcommands.insert( - handler.contexts(), - name.into(), - DynHandler::WithoutCli(Arc::new(AnyHandler { - _ctx: PhantomData, - handler: InheritanceHandler:: { - _phantom: PhantomData, - handler, - inherit, - }, - })), - ); - self - } - pub fn root_handler(mut self, handler: H, inherit: F) -> Self - where - Context: IntoContext, - H: HandlerTypes + Handler + CliBindings + 'static, - H::Params: DeserializeOwned, - H::Ok: Serialize + DeserializeOwned, - RpcError: From, - F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, - { - self.subcommands.insert( - handler.contexts(), - None, - DynHandler::WithCli(Arc::new(AnyHandler { - _ctx: PhantomData, - handler: InheritanceHandler:: { - _phantom: PhantomData, - handler, - inherit, - }, - })), - ); - self - } - pub fn root_handler_no_cli(mut self, handler: H, inherit: F) -> Self - where - Context: IntoContext, - H: Handler + 'static, - H::Params: DeserializeOwned, - H::Ok: Serialize, - RpcError: From, - F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, - { - self.subcommands.insert( - handler.contexts(), - None, - DynHandler::WithoutCli(Arc::new(AnyHandler { - _ctx: PhantomData, - handler: InheritanceHandler:: { - _phantom: PhantomData, - handler, - inherit, - }, - })), - ); - self - } -} - -impl HandlerTypes for ParentHandler -where - Params: Send + Sync, - InheritedParams: Send + Sync, -{ - type Params = Params; - type InheritedParams = InheritedParams; - type Ok = Value; - type Err = RpcError; -} -#[async_trait::async_trait] -impl Handler for ParentHandler -where - Context: IntoContext, - Params: Serialize + Send + Sync + 'static, - InheritedParams: Serialize + Send + Sync + 'static, -{ - fn handle_sync( - &self, - HandleArgs { - context, - mut parent_method, - mut method, - raw_params, - .. - }: HandleArgs, - ) -> Result { - let cmd = method.pop_front(); - if let Some(cmd) = cmd { - parent_method.push_back(cmd); - } - if let Some((_, sub_handler)) = &self.subcommands.get(context.inner_type_id(), cmd) { - sub_handler.handle_sync(HandleAnyArgs { - context: context.upcast(), - parent_method, - method, - params: raw_params, - }) - } else { - Err(yajrc::METHOD_NOT_FOUND_ERROR) - } - } - async fn handle_async( - &self, - HandleArgs { - context, - mut parent_method, - mut method, - raw_params, - .. - }: HandleArgs, - ) -> Result { - let cmd = method.pop_front(); - if let Some(cmd) = cmd { - parent_method.push_back(cmd); - } - if let Some((_, sub_handler)) = self.subcommands.get(context.inner_type_id(), cmd) { - sub_handler - .handle_async(HandleAnyArgs { - context: context.upcast(), - parent_method, - method, - params: raw_params, - }) - .await - } else { - Err(yajrc::METHOD_NOT_FOUND_ERROR) - } - } - fn metadata( - &self, - mut method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - let mut metadata = self.metadata.clone(); - if let Some((_, handler)) = self.subcommands.get(ctx_ty, method.pop_front()) { - handler.metadata(method, ctx_ty).union(metadata) - } else { - metadata - } - } - fn contexts(&self) -> Option> { - let mut set = BTreeSet::new(); - for ctx_ty in self.subcommands.0.values().flat_map(|c| c.keys()) { - set.insert((*ctx_ty)?); - } - Some(set) - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - let (head, tail) = if method.is_empty() { - (None, None) - } else { - method - .split_once(".") - .map(|(head, tail)| (Some(head), Some(tail))) - .unwrap_or((Some(method), None)) - }; - let (Name(name), h) = self.subcommands.get(ctx_ty, head)?; - let mut res = VecDeque::new(); - if let Some(name) = name { - res.push_back(name); - } - if let Some(tail) = tail { - res.append(&mut h.method_from_dots(tail, ctx_ty)?); - } - Some(res) - } -} - -impl CliBindings for ParentHandler -where - Params: FromArgMatches + CommandFactory + Serialize + Send + Sync + 'static, - InheritedParams: Serialize + Send + Sync + 'static, -{ - fn cli_command(&self, ctx_ty: TypeId) -> Command { - let mut base = Params::command(); - for (name, handlers) in &self.subcommands.0 { - if let (Name(Some(name)), Some(DynHandler::WithCli(handler))) = ( - name, - if let Some(handler) = handlers.get(&Some(ctx_ty)) { - Some(handler) - } else if let Some(handler) = handlers.get(&None) { - Some(handler) - } else { - None - }, - ) { - base = base.subcommand(handler.cli_command(ctx_ty).name(name)); - } - } - base - } - fn cli_parse( - &self, - matches: &ArgMatches, - ctx_ty: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?; - let (name, matches) = match matches.subcommand() { - Some((name, matches)) => (Some(name), matches), - None => (None, matches), - }; - if let Some((Name(Some(name)), DynHandler::WithCli(handler))) = - self.subcommands.get(ctx_ty, name) - { - let (mut method, params) = handler.cli_parse(matches, ctx_ty)?; - method.push_front(name); - - Ok(( - method, - combine(root_params, params) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e))?, - )) - } else { - Ok((VecDeque::new(), root_params)) - } - } - fn cli_display( - &self, - HandleArgs { - context, - mut parent_method, - mut method, - raw_params, - .. - }: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err> { - let cmd = method.pop_front(); - if let Some(cmd) = cmd { - parent_method.push_back(cmd); - } - if let Some((_, DynHandler::WithCli(sub_handler))) = - self.subcommands.get(context.inner_type_id(), cmd) - { - sub_handler.cli_display( - HandleAnyArgs { - context, - parent_method, - method, - params: raw_params, - }, - result, - ) - } else { - Err(yajrc::METHOD_NOT_FOUND_ERROR) - } - } -} - -pub struct FromFn { - _phantom: PhantomData<(T, E, Args)>, - function: F, - blocking: bool, - metadata: OrdMap<&'static str, Value>, -} -impl FromFn { - pub fn with_metadata(mut self, key: &'static str, value: Value) -> Self { - self.metadata.insert(key, value); - self - } -} -impl Clone for FromFn { - fn clone(&self) -> Self { - Self { - _phantom: PhantomData, - function: self.function.clone(), - blocking: self.blocking, - metadata: self.metadata.clone(), - } - } -} -impl std::fmt::Debug for FromFn { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("FromFn") - .field("blocking", &self.blocking) - .finish() - } -} -impl PrintCliResult for FromFn -where - Context: IntoContext, - Self: HandlerTypes, - ::Ok: Display, -{ - fn print(&self, _: HandleArgs, result: Self::Ok) -> Result<(), Self::Err> { - Ok(println!("{result}")) - } -} - -pub fn from_fn(function: F) -> FromFn { - FromFn { - function, - _phantom: PhantomData, - blocking: false, - metadata: OrdMap::new(), - } -} - -pub fn from_fn_blocking(function: F) -> FromFn { - FromFn { - function, - _phantom: PhantomData, - blocking: true, - metadata: OrdMap::new(), - } -} - -pub struct FromFnAsync { - _phantom: PhantomData<(Fut, T, E, Args)>, - function: F, - metadata: OrdMap<&'static str, Value>, -} -impl Clone for FromFnAsync { - fn clone(&self) -> Self { - Self { - _phantom: PhantomData, - function: self.function.clone(), - metadata: self.metadata.clone(), - } - } -} -impl std::fmt::Debug for FromFnAsync { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("FromFnAsync").finish() - } -} -impl PrintCliResult for FromFnAsync -where - Context: IntoContext, - Self: HandlerTypes, - ::Ok: Display, -{ - fn print(&self, _: HandleArgs, result: Self::Ok) -> Result<(), Self::Err> { - Ok(println!("{result}")) - } -} - -pub fn from_fn_async(function: F) -> FromFnAsync { - FromFnAsync { - function, - _phantom: PhantomData, - metadata: OrdMap::new(), - } -} - -impl HandlerTypes for FromFn -where - F: Fn() -> Result + Send + Sync + Clone + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - type Params = NoParams; - type InheritedParams = NoParams; - type Ok = T; - type Err = E; -} -#[async_trait::async_trait] -impl Handler for FromFn -where - Context: IntoContext, - F: Fn() -> Result + Send + Sync + Clone + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - fn handle_sync(&self, _: HandleArgs) -> Result { - (self.function)() - } - async fn handle_async( - &self, - handle_args: HandleArgs, - ) -> Result { - if self.blocking { - self.handle_async_with_sync_blocking(handle_args).await - } else { - self.handle_async_with_sync(handle_args).await - } - } - fn metadata( - &self, - mut method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.metadata.clone() - } -} -impl HandlerTypes for FromFnAsync -where - F: Fn() -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - type Params = NoParams; - type InheritedParams = NoParams; - type Ok = T; - type Err = E; -} -#[async_trait::async_trait] -impl Handler for FromFnAsync -where - Context: IntoContext, - F: Fn() -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - async fn handle_async(&self, _: HandleArgs) -> Result { - (self.function)().await - } - fn metadata( - &self, - mut method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.metadata.clone() - } -} - -impl HandlerTypes for FromFn -where - Context: IntoContext, - F: Fn(Context) -> Result + Send + Sync + Clone + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - type Params = NoParams; - type InheritedParams = NoParams; - type Ok = T; - type Err = E; -} -#[async_trait::async_trait] -impl Handler for FromFn -where - Context: IntoContext, - F: Fn(Context) -> Result + Send + Sync + Clone + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - fn handle_sync(&self, handle_args: HandleArgs) -> Result { - (self.function)(handle_args.context) - } - async fn handle_async( - &self, - handle_args: HandleArgs, - ) -> Result { - if self.blocking { - self.handle_async_with_sync_blocking(handle_args).await - } else { - self.handle_async_with_sync(handle_args).await - } - } - fn metadata( - &self, - mut method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.metadata.clone() - } -} -impl HandlerTypes for FromFnAsync -where - Context: IntoContext, - F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - type Params = NoParams; - type InheritedParams = NoParams; - type Ok = T; - type Err = E; -} -#[async_trait::async_trait] -impl Handler for FromFnAsync -where - Context: IntoContext, - F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - async fn handle_async( - &self, - handle_args: HandleArgs, - ) -> Result { - (self.function)(handle_args.context).await - } - fn metadata( - &self, - mut method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.metadata.clone() - } -} - -impl HandlerTypes for FromFn -where - Context: IntoContext, - F: Fn(Context, Params) -> Result + Send + Sync + Clone + 'static, - Params: DeserializeOwned + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - type Params = Params; - type InheritedParams = NoParams; - type Ok = T; - type Err = E; -} -#[async_trait::async_trait] -impl Handler for FromFn -where - Context: IntoContext, - F: Fn(Context, Params) -> Result + Send + Sync + Clone + 'static, - Params: DeserializeOwned + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - fn handle_sync(&self, handle_args: HandleArgs) -> Result { - let HandleArgs { - context, params, .. - } = handle_args; - (self.function)(context, params) - } - async fn handle_async( - &self, - handle_args: HandleArgs, - ) -> Result { - if self.blocking { - self.handle_async_with_sync_blocking(handle_args).await - } else { - self.handle_async_with_sync(handle_args).await - } - } - fn metadata( - &self, - mut method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.metadata.clone() - } -} - -impl HandlerTypes for FromFnAsync -where - Context: IntoContext, - F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, - Params: DeserializeOwned + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - type Params = Params; - type InheritedParams = NoParams; - type Ok = T; - type Err = E; -} -#[async_trait::async_trait] -impl Handler - for FromFnAsync -where - Context: IntoContext, - F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, - Params: DeserializeOwned + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - async fn handle_async( - &self, - handle_args: HandleArgs, - ) -> Result { - let HandleArgs { - context, params, .. - } = handle_args; - (self.function)(context, params).await - } - fn metadata( - &self, - mut method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.metadata.clone() - } -} - -impl HandlerTypes - for FromFn -where - Context: IntoContext, - F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, - Params: DeserializeOwned + Send + Sync + 'static, - InheritedParams: DeserializeOwned + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - type Params = Params; - type InheritedParams = InheritedParams; - type Ok = T; - type Err = E; -} -#[async_trait::async_trait] -impl Handler - for FromFn -where - Context: IntoContext, - F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, - Params: DeserializeOwned + Send + Sync + 'static, - InheritedParams: DeserializeOwned + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - fn handle_sync(&self, handle_args: HandleArgs) -> Result { - let HandleArgs { - context, - params, - inherited_params, - .. - } = handle_args; - (self.function)(context, params, inherited_params) - } - async fn handle_async( - &self, - handle_args: HandleArgs, - ) -> Result { - if self.blocking { - self.handle_async_with_sync_blocking(handle_args).await - } else { - self.handle_async_with_sync(handle_args).await - } - } - fn metadata( - &self, - mut method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.metadata.clone() - } -} - -impl HandlerTypes - for FromFnAsync -where - Context: IntoContext, - F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, - Params: DeserializeOwned + Send + Sync + 'static, - InheritedParams: DeserializeOwned + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - type Params = Params; - type InheritedParams = InheritedParams; - type Ok = T; - type Err = E; -} -#[async_trait::async_trait] -impl Handler - for FromFnAsync -where - Context: IntoContext, - F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, - Params: DeserializeOwned + Send + Sync + 'static, - InheritedParams: DeserializeOwned + Send + Sync + 'static, - T: Send + Sync + 'static, - E: Send + Sync + 'static, -{ - async fn handle_async( - &self, - handle_args: HandleArgs, - ) -> Result { - let HandleArgs { - context, - params, - inherited_params, - .. - } = handle_args; - (self.function)(context, params, inherited_params).await - } - fn metadata( - &self, - mut method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.metadata.clone() - } -} - -impl CliBindings for FromFn -where - Context: IntoContext, - Self: HandlerTypes, - Self::Params: FromArgMatches + CommandFactory + Serialize, - Self: PrintCliResult, -{ - fn cli_command(&self, _: TypeId) -> Command { - Self::Params::command() - } - fn cli_parse( - &self, - matches: &ArgMatches, - _: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - Self::Params::from_arg_matches(matches).and_then(|a| { - Ok(( - VecDeque::new(), - imbl_value::to_value(&a) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, - )) - }) - } - fn cli_display( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.print( - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }, - result, - ) - } -} - -impl CliBindings for FromFnAsync -where - Context: IntoContext, - Self: HandlerTypes, - Self::Params: FromArgMatches + CommandFactory + Serialize, - Self: PrintCliResult, -{ - fn cli_command(&self, _: TypeId) -> Command { - Self::Params::command() - } - fn cli_parse( - &self, - matches: &ArgMatches, - _: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - Self::Params::from_arg_matches(matches).and_then(|a| { - Ok(( - VecDeque::new(), - imbl_value::to_value(&a) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, - )) - }) - } - fn cli_display( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.print( - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }, - result, - ) - } -} diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs new file mode 100644 index 0000000..0eca646 --- /dev/null +++ b/rpc-toolkit/src/handler/adapters.rs @@ -0,0 +1,485 @@ +use std::any::TypeId; +use std::collections::VecDeque; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::sync::Arc; +use std::task::Context; + +use clap::{CommandFactory, FromArgMatches}; +use imbl_value::imbl::{OrdMap, OrdSet}; +use imbl_value::Value; +use serde::de::DeserializeOwned; +use serde::Serialize; +use yajrc::RpcError; + +use crate::util::{internal_error, parse_error, Flat}; +use crate::{ + intersect_type_ids, iter_from_ctx_and_handler, AnyHandler, CallRemote, CliBindings, DynHandler, + HandleArgs, Handler, HandlerTypes, IntoContext, IntoHandlers, NoParams, PrintCliResult, +}; + +pub trait HandlerExt: HandlerTypes + Sized { + fn no_cli(self) -> NoCli; + fn with_custom_display

(self, display: P) -> CustomDisplay + where + P: PrintCliResult< + Params = Self::Params, + InheritedParams = Self::InheritedParams, + Ok = Self::Ok, + Err = Self::Err, + >; + fn with_custom_display_fn( + self, + display: F, + ) -> CustomDisplayFn + where + F: Fn(HandleArgs, Self::Ok) -> Result<(), Self::Err>; +} + +impl HandlerExt for T { + fn no_cli(self) -> NoCli { + NoCli(self) + } + fn with_custom_display

(self, display: P) -> CustomDisplay + where + P: PrintCliResult< + Params = Self::Params, + InheritedParams = Self::InheritedParams, + Ok = Self::Ok, + Err = Self::Err, + >, + { + CustomDisplay { + print: display, + handler: self, + } + } + fn with_custom_display_fn( + self, + display: F, + ) -> CustomDisplayFn + where + F: Fn(HandleArgs, Self::Ok) -> Result<(), Self::Err>, + { + CustomDisplayFn { + _phantom: PhantomData, + print: display, + handler: self, + } + } +} + +#[derive(Debug, Clone)] +pub struct NoCli(pub H); +impl HandlerTypes for NoCli { + type Params = H::Params; + type InheritedParams = H::InheritedParams; + type Ok = H::Ok; + type Err = H::Err; +} + +#[derive(Clone, Debug)] +pub struct CustomDisplay { + print: P, + handler: H, +} +impl HandlerTypes for CustomDisplay +where + H: HandlerTypes, +{ + type Params = H::Params; + type InheritedParams = H::InheritedParams; + type Ok = H::Ok; + type Err = H::Err; +} +#[async_trait::async_trait] +impl Handler for CustomDisplay +where + H: Handler, + P: Send + Sync + Clone + Debug + 'static, +{ + type Context = H::Context; + fn handle_sync( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.handler.handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + } + async fn handle_async( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.handler + .handle_async(HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + .await + } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.handler.metadata(method, ctx_ty) + } + fn contexts(&self) -> Option> { + self.handler.contexts() + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.handler.method_from_dots(method, ctx_ty) + } +} +impl CliBindings for CustomDisplay +where + H: HandlerTypes, + H::Params: FromArgMatches + CommandFactory + Serialize, + P: PrintCliResult< + Params = H::Params, + InheritedParams = H::InheritedParams, + Ok = H::Ok, + Err = H::Err, + > + Send + + Sync + + Clone + + Debug + + 'static, +{ + type Context = P::Context; + fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { + H::Params::command() + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + ctx_ty: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.print.print( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) + } +} +impl IntoHandlers for CustomDisplay +where + Self: HandlerTypes + Handler + CliBindings, + ::Params: DeserializeOwned, + ::InheritedParams: DeserializeOwned, + ::Ok: Serialize + DeserializeOwned, + RpcError: From<::Err>, +{ + fn into_handlers(self) -> impl IntoIterator, crate::DynHandler)> { + iter_from_ctx_and_handler( + intersect_type_ids(self.contexts(), ::Context::type_ids()), + DynHandler::WithCli(Arc::new(AnyHandler::new(self))), + ) + } +} + +pub struct CustomDisplayFn { + _phantom: PhantomData, + print: F, + handler: H, +} +impl Clone for CustomDisplayFn { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + print: self.print.clone(), + handler: self.handler.clone(), + } + } +} +impl Debug for CustomDisplayFn { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CustomDisplayFn") + .field("print", &self.print) + .field("handler", &self.handler) + .finish() + } +} +impl HandlerTypes for CustomDisplayFn +where + H: HandlerTypes, +{ + type Params = H::Params; + type InheritedParams = H::InheritedParams; + type Ok = H::Ok; + type Err = H::Err; +} +#[async_trait::async_trait] +impl Handler for CustomDisplayFn +where + Context: Send + Sync + 'static, + H: Handler, + F: Send + Sync + Clone + Debug + 'static, +{ + type Context = H::Context; + fn handle_sync( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.handler.handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + } + async fn handle_async( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.handler + .handle_async(HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + .await + } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.handler.metadata(method, ctx_ty) + } + fn contexts(&self) -> Option> { + self.handler.contexts() + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.handler.method_from_dots(method, ctx_ty) + } +} +impl CliBindings for CustomDisplayFn +where + Context: IntoContext, + H: CliBindings, + F: Fn(HandleArgs, H::Ok) -> Result<(), H::Err> + + Send + + Sync + + Clone + + Debug + + 'static, +{ + type Context = Context; + fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { + self.handler.cli_command(ctx_ty) + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + ctx_ty: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + self.handler.cli_parse(matches, ctx_ty) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + (self.print)( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) + } +} + +// pub struct RemoteCli { +// _phantom: PhantomData<(CliContext, RemoteContext)>, +// handler: H, +// } +// impl Clone for RemoteCli { +// fn clone(&self) -> Self { +// Self { +// _phantom: PhantomData, +// handler: self.handler.clone(), +// } +// } +// } +// impl Debug for RemoteCli { +// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +// f.debug_tuple("RemoteCli").field(&self.handler).finish() +// } +// } +// impl HandlerTypes for RemoteCli +// where +// H: HandlerTypes, +// { +// type Params = H::Params; +// type InheritedParams = H::InheritedParams; +// type Ok = H::Ok; +// type Err = H::Err; +// } +// #[async_trait::async_trait] +// impl Handler> +// for RemoteCli +// where +// CliContext: CallRemote, +// H: Handler, +// { +// async fn handle_async( +// &self, +// HandleArgs { +// context, +// parent_method, +// method, +// params, +// inherited_params, +// raw_params, +// }: HandleArgs, +// ) -> Result { +// let full_method = parent_method.into_iter().chain(method).collect::>(); +// match context +// .call_remote( +// &full_method.join("."), +// imbl_value::to_value(&Flat(params, inherited_params)).map_err(parse_error)?, +// ) +// .await +// { +// Ok(a) => imbl_value::from_value(a) +// .map_err(internal_error) +// .map_err(Self::Err::from), +// Err(e) => Err(Self::Err::from(e)), +// } +// } +// fn metadata( +// &self, +// method: VecDeque<&'static str>, +// ctx_ty: TypeId, +// ) -> OrdMap<&'static str, Value> { +// self.handler.metadata(method, ctx_ty) +// } +// fn contexts(&self) -> Option> { +// todo!() +// } +// fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { +// self.handler.method_from_dots(method, ctx_ty) +// } +// } +// impl CliBindings for RemoteCli +// where +// Context: crate::Context, +// H: CliBindings, +// { +// fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { +// self.handler.cli_command(ctx_ty) +// } +// fn cli_parse( +// &self, +// matches: &clap::ArgMatches, +// ctx_ty: TypeId, +// ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { +// self.handler.cli_parse(matches, ctx_ty) +// } +// fn cli_display( +// &self, +// HandleArgs { +// context, +// parent_method, +// method, +// params, +// inherited_params, +// raw_params, +// }: HandleArgs, +// result: Self::Ok, +// ) -> Result<(), Self::Err> { +// self.handler.cli_display( +// HandleArgs { +// context, +// parent_method, +// method, +// params, +// inherited_params, +// raw_params, +// }, +// result, +// ) +// } +// } diff --git a/rpc-toolkit/src/handler/from_fn.rs b/rpc-toolkit/src/handler/from_fn.rs new file mode 100644 index 0000000..fa06850 --- /dev/null +++ b/rpc-toolkit/src/handler/from_fn.rs @@ -0,0 +1,601 @@ +use std::any::TypeId; +use std::collections::VecDeque; +use std::fmt::Display; +use std::marker::PhantomData; +use std::sync::Arc; + +use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; +use futures::Future; +use imbl_value::imbl::OrdMap; +use imbl_value::Value; +use serde::de::DeserializeOwned; +use serde::Serialize; +use yajrc::RpcError; + +use crate::marker::LeafHandler; +use crate::{ + intersect_type_ids, iter_from_ctx_and_handler, AnyContext, AnyHandler, CliBindings, DynHandler, + HandleArgs, Handler, HandlerTypes, IntoContext, IntoHandlers, NoCli, NoParams, PrintCliResult, +}; + +pub struct FromFn { + _phantom: PhantomData<(T, E, Args)>, + function: F, + blocking: bool, + metadata: OrdMap<&'static str, Value>, +} +impl FromFn { + pub fn with_metadata(mut self, key: &'static str, value: Value) -> Self { + self.metadata.insert(key, value); + self + } +} +impl Clone for FromFn { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + function: self.function.clone(), + blocking: self.blocking, + metadata: self.metadata.clone(), + } + } +} +impl std::fmt::Debug for FromFn { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FromFn") + .field("blocking", &self.blocking) + .finish() + } +} +impl LeafHandler for FromFn {} +impl PrintCliResult for FromFn +where + Self: HandlerTypes, + ::Ok: Display, +{ + type Context = AnyContext; + fn print(&self, _: HandleArgs, result: Self::Ok) -> Result<(), Self::Err> { + Ok(println!("{result}")) + } +} +impl IntoHandlers for FromFn +where + Self: HandlerTypes + Handler + CliBindings, + ::Params: DeserializeOwned, + ::InheritedParams: DeserializeOwned, + ::Ok: Serialize + DeserializeOwned, + RpcError: From<::Err>, +{ + fn into_handlers(self) -> impl IntoIterator, crate::DynHandler)> { + iter_from_ctx_and_handler( + intersect_type_ids(self.contexts(), ::Context::type_ids()), + DynHandler::WithCli(Arc::new(AnyHandler::new(self))), + ) + } +} +impl IntoHandlers for NoCli> +where + Self: HandlerTypes + Handler, + ::Params: DeserializeOwned, + ::InheritedParams: DeserializeOwned, + ::Ok: Serialize + DeserializeOwned, + RpcError: From<::Err>, +{ + fn into_handlers(self) -> impl IntoIterator, crate::DynHandler)> { + iter_from_ctx_and_handler( + self.contexts(), + DynHandler::WithoutCli(Arc::new(AnyHandler::new(self))), + ) + } +} + +pub fn from_fn(function: F) -> FromFn { + FromFn { + function, + _phantom: PhantomData, + blocking: false, + metadata: OrdMap::new(), + } +} + +pub fn from_fn_blocking(function: F) -> FromFn { + FromFn { + function, + _phantom: PhantomData, + blocking: true, + metadata: OrdMap::new(), + } +} + +pub struct FromFnAsync { + _phantom: PhantomData<(Fut, T, E, Args)>, + function: F, + metadata: OrdMap<&'static str, Value>, +} +impl Clone for FromFnAsync { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + function: self.function.clone(), + metadata: self.metadata.clone(), + } + } +} +impl std::fmt::Debug for FromFnAsync { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FromFnAsync").finish() + } +} +impl PrintCliResult for FromFnAsync +where + Self: HandlerTypes, + ::Ok: Display, +{ + type Context = AnyContext; + fn print(&self, _: HandleArgs, result: Self::Ok) -> Result<(), Self::Err> { + Ok(println!("{result}")) + } +} +impl IntoHandlers for FromFnAsync +where + Self: HandlerTypes + Handler + CliBindings, + ::Params: DeserializeOwned, + ::InheritedParams: DeserializeOwned, + ::Ok: Serialize + DeserializeOwned, + RpcError: From<::Err>, +{ + fn into_handlers(self) -> impl IntoIterator, crate::DynHandler)> { + iter_from_ctx_and_handler( + intersect_type_ids(self.contexts(), ::Context::type_ids()), + DynHandler::WithCli(Arc::new(AnyHandler::new(self))), + ) + } +} +impl IntoHandlers for NoCli> +where + Self: HandlerTypes + Handler, + ::Params: DeserializeOwned, + ::InheritedParams: DeserializeOwned, + ::Ok: Serialize + DeserializeOwned, + RpcError: From<::Err>, +{ + fn into_handlers(self) -> impl IntoIterator, crate::DynHandler)> { + iter_from_ctx_and_handler( + self.contexts(), + DynHandler::WithoutCli(Arc::new(AnyHandler::new(self))), + ) + } +} + +pub fn from_fn_async(function: F) -> FromFnAsync { + FromFnAsync { + function, + _phantom: PhantomData, + metadata: OrdMap::new(), + } +} + +impl HandlerTypes for FromFn +where + F: Fn() -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = NoParams; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFn +where + F: Fn() -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Context = AnyContext; + fn handle_sync(&self, _: HandleArgs) -> Result { + (self.function)() + } + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + if self.blocking { + self.handle_async_with_sync_blocking(handle_args).await + } else { + self.handle_async_with_sync(handle_args).await + } + } + fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} +impl HandlerTypes for FromFnAsync +where + F: Fn() -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = NoParams; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFnAsync +where + F: Fn() -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Context = AnyContext; + async fn handle_async( + &self, + _: HandleArgs, + ) -> Result { + (self.function)().await + } + fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} + +impl HandlerTypes for FromFn +where + Context: IntoContext, + F: Fn(Context) -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = NoParams; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFn +where + Context: IntoContext, + F: Fn(Context) -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Context = Context; + fn handle_sync( + &self, + handle_args: HandleArgs, + ) -> Result { + (self.function)(handle_args.context) + } + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + if self.blocking { + self.handle_async_with_sync_blocking(handle_args).await + } else { + self.handle_async_with_sync(handle_args).await + } + } + fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} +impl HandlerTypes for FromFnAsync +where + Context: IntoContext, + F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = NoParams; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFnAsync +where + Context: IntoContext, + F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Context = Context; + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + (self.function)(handle_args.context).await + } + fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} + +impl HandlerTypes for FromFn +where + Context: IntoContext, + F: Fn(Context, Params) -> Result + Send + Sync + Clone + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Params; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFn +where + Context: IntoContext, + F: Fn(Context, Params) -> Result + Send + Sync + Clone + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Context = Context; + fn handle_sync( + &self, + handle_args: HandleArgs, + ) -> Result { + let HandleArgs { + context, params, .. + } = handle_args; + (self.function)(context, params) + } + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + if self.blocking { + self.handle_async_with_sync_blocking(handle_args).await + } else { + self.handle_async_with_sync(handle_args).await + } + } + fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} +impl HandlerTypes for FromFnAsync +where + Context: IntoContext, + F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Params; + type InheritedParams = NoParams; + type Ok = T; + type Err = E; +} +#[async_trait::async_trait] +impl Handler for FromFnAsync +where + Context: IntoContext, + F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Context = Context; + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + let HandleArgs { + context, params, .. + } = handle_args; + (self.function)(context, params).await + } + fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} + +impl HandlerTypes + for FromFn +where + Context: IntoContext, + F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = T; + type Err = E; +} +#[async_trait::async_trait] +impl Handler + for FromFn +where + Context: IntoContext, + F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Context = Context; + fn handle_sync( + &self, + handle_args: HandleArgs, + ) -> Result { + let HandleArgs { + context, + params, + inherited_params, + .. + } = handle_args; + (self.function)(context, params, inherited_params) + } + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + if self.blocking { + self.handle_async_with_sync_blocking(handle_args).await + } else { + self.handle_async_with_sync(handle_args).await + } + } + fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} +impl HandlerTypes + for FromFnAsync +where + Context: IntoContext, + F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = T; + type Err = E; +} +#[async_trait::async_trait] +impl Handler + for FromFnAsync +where + Context: IntoContext, + F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + Sync + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Context = Context; + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result { + let HandleArgs { + context, + params, + inherited_params, + .. + } = handle_args; + (self.function)(context, params, inherited_params).await + } + fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} + +impl CliBindings for FromFn +where + Self: HandlerTypes, + Self::Params: FromArgMatches + CommandFactory + Serialize, + Self: PrintCliResult, +{ + type Context = ::Context; + fn cli_command(&self, _: TypeId) -> Command { + Self::Params::command() + } + fn cli_parse( + &self, + matches: &ArgMatches, + _: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.print( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) + } +} + +impl CliBindings for FromFnAsync +where + Self: HandlerTypes, + Self::Params: FromArgMatches + CommandFactory + Serialize, + Self: PrintCliResult, +{ + type Context = ::Context; + fn cli_command(&self, _: TypeId) -> Command { + Self::Params::command() + } + fn cli_parse( + &self, + matches: &ArgMatches, + _: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.print( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) + } +} diff --git a/rpc-toolkit/src/handler/marker.rs b/rpc-toolkit/src/handler/marker.rs new file mode 100644 index 0000000..32a0f18 --- /dev/null +++ b/rpc-toolkit/src/handler/marker.rs @@ -0,0 +1 @@ +pub trait LeafHandler {} diff --git a/rpc-toolkit/src/handler/mod.rs b/rpc-toolkit/src/handler/mod.rs new file mode 100644 index 0000000..d859c7c --- /dev/null +++ b/rpc-toolkit/src/handler/mod.rs @@ -0,0 +1,317 @@ +use std::any::TypeId; +use std::collections::VecDeque; +use std::ops::Deref; +use std::sync::Arc; + +use clap::{ArgMatches, Command, Parser}; +use imbl_value::imbl::{OrdMap, OrdSet}; +use imbl_value::Value; +use serde::de::DeserializeOwned; +use serde::{Deserialize, Serialize}; +use yajrc::RpcError; + +use crate::context::{AnyContext, IntoContext}; +use crate::util::{internal_error, invalid_params}; + +pub mod adapters; +pub mod from_fn; +pub mod marker; +pub mod parent; + +pub use adapters::*; +pub use from_fn::*; +pub use parent::*; + +pub(crate) struct HandleAnyArgs { + pub(crate) context: AnyContext, + pub(crate) parent_method: VecDeque<&'static str>, + pub(crate) method: VecDeque<&'static str>, + pub(crate) params: Value, +} +impl HandleAnyArgs { + fn downcast(self) -> Result, imbl_value::Error> + where + H: HandlerTypes, + H::Params: DeserializeOwned, + H::InheritedParams: DeserializeOwned, + { + let Self { + context, + parent_method, + method, + params, + } = self; + Ok(HandleArgs { + context: Context::downcast(context).map_err(|_| imbl_value::Error { + kind: imbl_value::ErrorKind::Deserialization, + source: serde::ser::Error::custom("context does not match expected"), + })?, + parent_method, + method, + params: imbl_value::from_value(params.clone())?, + inherited_params: imbl_value::from_value(params.clone())?, + raw_params: params, + }) + } +} + +#[async_trait::async_trait] +pub(crate) trait HandleAny: std::fmt::Debug + Send + Sync { + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; + async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result; + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value>; + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option>; +} +#[async_trait::async_trait] +impl HandleAny for Arc { + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + self.deref().handle_sync(handle_args) + } + async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { + self.deref().handle_async(handle_args).await + } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.deref().metadata(method, ctx_ty) + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.deref().method_from_dots(method, ctx_ty) + } +} + +pub(crate) trait CliBindingsAny { + fn cli_command(&self, ctx_ty: TypeId) -> Command; + fn cli_parse( + &self, + matches: &ArgMatches, + ctx_ty: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; + fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError>; +} + +pub trait CliBindings: HandlerTypes { + type Context: IntoContext; + fn cli_command(&self, ctx_ty: TypeId) -> Command; + fn cli_parse( + &self, + matches: &ArgMatches, + ctx_ty: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; + fn cli_display( + &self, + handle_args: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err>; +} + +pub trait PrintCliResult: HandlerTypes { + type Context: IntoContext; + fn print( + &self, + handle_args: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err>; +} + +pub(crate) trait HandleAnyWithCli: HandleAny + CliBindingsAny {} +impl HandleAnyWithCli for T {} + +#[derive(Debug, Clone)] +#[allow(private_interfaces)] +pub enum DynHandler { + WithoutCli(Arc), + WithCli(Arc), +} +#[async_trait::async_trait] +impl HandleAny for DynHandler { + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + match self { + DynHandler::WithoutCli(h) => h.handle_sync(handle_args), + DynHandler::WithCli(h) => h.handle_sync(handle_args), + } + } + async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { + match self { + DynHandler::WithoutCli(h) => h.handle_async(handle_args).await, + DynHandler::WithCli(h) => h.handle_async(handle_args).await, + } + } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + match self { + DynHandler::WithoutCli(h) => h.metadata(method, ctx_ty), + DynHandler::WithCli(h) => h.metadata(method, ctx_ty), + } + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + match self { + DynHandler::WithoutCli(h) => h.method_from_dots(method, ctx_ty), + DynHandler::WithCli(h) => h.method_from_dots(method, ctx_ty), + } + } +} + +#[derive(Debug, Clone)] +pub struct HandleArgs { + pub context: Context, + pub parent_method: VecDeque<&'static str>, + pub method: VecDeque<&'static str>, + pub params: H::Params, + pub inherited_params: H::InheritedParams, + pub raw_params: Value, +} + +pub trait HandlerTypes { + type Params: Send + Sync; + type InheritedParams: Send + Sync; + type Ok: Send + Sync; + type Err: Send + Sync; +} + +#[async_trait::async_trait] +pub trait Handler: HandlerTypes + std::fmt::Debug + Clone + Send + Sync + 'static { + type Context: IntoContext; + fn handle_sync( + &self, + handle_args: HandleArgs, + ) -> Result { + handle_args + .context + .runtime() + .block_on(self.handle_async(handle_args)) + } + async fn handle_async( + &self, + handle_args: HandleArgs, + ) -> Result; + async fn handle_async_with_sync( + &self, + handle_args: HandleArgs, + ) -> Result { + self.handle_sync(handle_args) + } + async fn handle_async_with_sync_blocking( + &self, + handle_args: HandleArgs, + ) -> Result { + let s = self.clone(); + handle_args + .context + .runtime() + .spawn_blocking(move || s.handle_sync(handle_args)) + .await + .unwrap() + } + #[allow(unused_variables)] + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + OrdMap::new() + } + fn contexts(&self) -> Option> { + Self::Context::type_ids() + } + #[allow(unused_variables)] + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + if method.is_empty() { + Some(VecDeque::new()) + } else { + None + } + } +} + +pub(crate) struct AnyHandler(H); +impl AnyHandler { + pub(crate) fn new(handler: H) -> Self { + Self(handler) + } +} +impl std::fmt::Debug for AnyHandler { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("AnyHandler").field(&self.0).finish() + } +} + +#[async_trait::async_trait] +impl HandleAny for AnyHandler +where + H::Params: DeserializeOwned, + H::InheritedParams: DeserializeOwned, + H::Ok: Serialize, + RpcError: From, +{ + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + imbl_value::to_value( + &self + .0 + .handle_sync(handle_args.downcast().map_err(invalid_params)?)?, + ) + .map_err(internal_error) + } + async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { + imbl_value::to_value( + &self + .0 + .handle_async(handle_args.downcast().map_err(invalid_params)?) + .await?, + ) + .map_err(internal_error) + } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.0.metadata(method, ctx_ty) + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.0.method_from_dots(method, ctx_ty) + } +} + +impl CliBindingsAny for AnyHandler +where + H: CliBindings, + H::Params: DeserializeOwned, + H::InheritedParams: DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, +{ + fn cli_command(&self, ctx_ty: TypeId) -> Command { + self.0.cli_command(ctx_ty) + } + fn cli_parse( + &self, + matches: &ArgMatches, + ctx_ty: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + self.0.cli_parse(matches, ctx_ty) + } + fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError> { + self.0 + .cli_display( + handle_args.downcast().map_err(invalid_params)?, + imbl_value::from_value(result).map_err(internal_error)?, + ) + .map_err(RpcError::from) + } +} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] +pub struct NoParams {} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] +pub enum Never {} diff --git a/rpc-toolkit/src/handler/parent.rs b/rpc-toolkit/src/handler/parent.rs new file mode 100644 index 0000000..998f6d6 --- /dev/null +++ b/rpc-toolkit/src/handler/parent.rs @@ -0,0 +1,520 @@ +use std::any::TypeId; +use std::collections::VecDeque; +use std::marker::PhantomData; +use std::sync::Arc; + +use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; +use imbl_value::imbl::{OrdMap, OrdSet}; +use imbl_value::Value; +use serde::de::DeserializeOwned; +use serde::Serialize; +use yajrc::RpcError; + +use crate::util::{combine, Flat}; +use crate::{ + AnyContext, AnyHandler, CliBindings, DynHandler, HandleAny, HandleAnyArgs, HandleArgs, Handler, + HandlerTypes, IntoContext, NoCli, NoParams, +}; + +pub trait IntoHandlers: HandlerTypes { + fn into_handlers(self) -> impl IntoIterator, DynHandler)>; +} + +impl IntoHandlers for H { + fn into_handlers(self) -> impl IntoIterator, DynHandler)> { + iter_from_ctx_and_handler( + intersect_type_ids(self.contexts(), ::Context::type_ids()), + DynHandler::WithCli(Arc::new(AnyHandler::new(self))), + ) + } +} + +pub(crate) fn iter_from_ctx_and_handler( + ctx: Option>, + handler: DynHandler, +) -> impl IntoIterator, DynHandler)> { + if let Some(ctx) = ctx { + itertools::Either::Left(ctx.into_iter().map(Some)) + } else { + itertools::Either::Right(std::iter::once(None)) + } + .map(move |ctx| (ctx, handler.clone())) +} + +pub(crate) fn intersect_type_ids( + a: Option>, + b: Option>, +) -> Option> { + match (a, b) { + (None, None) => None, + (Some(a), None) => Some(a), + (None, Some(b)) => Some(b), + (Some(a), Some(b)) => Some(a.intersection(b)), + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) struct Name(pub(crate) Option<&'static str>); +impl<'a> std::borrow::Borrow> for Name { + fn borrow(&self) -> &Option<&'a str> { + &self.0 + } +} + +#[derive(Debug, Clone)] +pub(crate) struct SubcommandMap(pub(crate) OrdMap, DynHandler>>); +impl SubcommandMap { + fn insert( + &mut self, + name: Option<&'static str>, + handlers: impl IntoIterator, DynHandler)>, + ) { + let mut for_name = self.0.remove(&name).unwrap_or_default(); + for (ctx_ty, handler) in handlers { + for_name.insert(ctx_ty, handler); + } + self.0.insert(Name(name), for_name); + } + + fn get<'a>(&'a self, ctx_ty: TypeId, name: Option<&str>) -> Option<(Name, &'a DynHandler)> { + if let Some((name, for_name)) = self.0.get_key_value(&name) { + if let Some(for_ctx) = for_name.get(&Some(ctx_ty)) { + Some((*name, for_ctx)) + } else { + for_name.get(&None).map(|h| (*name, h)) + } + } else { + None + } + } +} + +pub struct ParentHandler { + _phantom: PhantomData<(Params, InheritedParams)>, + pub(crate) subcommands: SubcommandMap, + metadata: OrdMap<&'static str, Value>, +} +impl ParentHandler { + pub fn new() -> Self { + Self { + _phantom: PhantomData, + subcommands: SubcommandMap(OrdMap::new()), + metadata: OrdMap::new(), + } + } + pub fn with_metadata(mut self, key: &'static str, value: Value) -> Self { + self.metadata.insert(key, value); + self + } +} +impl Clone for ParentHandler { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + subcommands: self.subcommands.clone(), + metadata: self.metadata.clone(), + } + } +} +impl std::fmt::Debug for ParentHandler { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("ParentHandler") + .field(&self.subcommands) + .finish() + } +} + +struct InheritanceHandler { + _phantom: PhantomData<(Params, InheritedParams)>, + handler: H, + inherit: F, +} +impl Clone + for InheritanceHandler +{ + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + handler: self.handler.clone(), + inherit: self.inherit.clone(), + } + } +} +impl std::fmt::Debug + for InheritanceHandler +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("InheritanceHandler") + .field(&self.handler) + .finish() + } +} +impl HandlerTypes + for InheritanceHandler +where + H: HandlerTypes, + Params: Send + Sync, + InheritedParams: Send + Sync, +{ + type Params = H::Params; + type InheritedParams = Flat; + type Ok = H::Ok; + type Err = H::Err; +} +#[async_trait::async_trait] +impl Handler for InheritanceHandler +where + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + H: Handler, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, +{ + type Context = H::Context; + fn handle_sync( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.handler.handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, + }) + } + async fn handle_async( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.handler.handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, + }) + } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.handler.metadata(method, ctx_ty) + } + fn contexts(&self) -> Option> { + self.handler.contexts() + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.handler.method_from_dots(method, ctx_ty) + } +} + +impl CliBindings + for InheritanceHandler +where + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + H: CliBindings, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, +{ + type Context = H::Context; + fn cli_command(&self, ctx_ty: TypeId) -> Command { + self.handler.cli_command(ctx_ty) + } + fn cli_parse( + &self, + matches: &ArgMatches, + ctx_ty: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + self.handler.cli_parse(matches, ctx_ty) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.handler.cli_display( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, + }, + result, + ) + } +} + +impl ParentHandler { + fn get_contexts(&self) -> Option> { + let mut set = OrdSet::new(); + for ctx_ty in self.subcommands.0.values().flat_map(|c| c.keys()) { + set.insert((*ctx_ty)?); + } + Some(set) + } + pub fn subcommand(mut self, name: &'static str, handler: H) -> Self + where + H: IntoHandlers>, + { + self.subcommands + .insert(name.into(), handler.into_handlers()); + self + } + pub fn root_handler(mut self, handler: H) -> Self + where + H: IntoHandlers>, + { + self.subcommands.insert(None, handler.into_handlers()); + self + } +} + +impl HandlerTypes for ParentHandler +where + Params: Send + Sync, + InheritedParams: Send + Sync, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = Value; + type Err = RpcError; +} +#[async_trait::async_trait] +impl Handler for ParentHandler +where + Params: Serialize + Send + Sync + 'static, + InheritedParams: Serialize + Send + Sync + 'static, +{ + type Context = AnyContext; + fn handle_sync( + &self, + HandleArgs { + context, + mut parent_method, + mut method, + raw_params, + .. + }: HandleArgs, + ) -> Result { + let cmd = method.pop_front(); + if let Some(cmd) = cmd { + parent_method.push_back(cmd); + } + if let Some((_, sub_handler)) = &self.subcommands.get(context.inner_type_id(), cmd) { + sub_handler.handle_sync(HandleAnyArgs { + context, + parent_method, + method, + params: raw_params, + }) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } + async fn handle_async( + &self, + HandleArgs { + context, + mut parent_method, + mut method, + raw_params, + .. + }: HandleArgs, + ) -> Result { + let cmd = method.pop_front(); + if let Some(cmd) = cmd { + parent_method.push_back(cmd); + } + if let Some((_, sub_handler)) = self.subcommands.get(context.inner_type_id(), cmd) { + sub_handler + .handle_async(HandleAnyArgs { + context, + parent_method, + method, + params: raw_params, + }) + .await + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } + fn metadata( + &self, + mut method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + let metadata = self.metadata.clone(); + if let Some((_, handler)) = self.subcommands.get(ctx_ty, method.pop_front()) { + handler.metadata(method, ctx_ty).union(metadata) + } else { + metadata + } + } + fn contexts(&self) -> Option> { + self.get_contexts() + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + let (head, tail) = if method.is_empty() { + (None, None) + } else { + method + .split_once(".") + .map(|(head, tail)| (Some(head), Some(tail))) + .unwrap_or((Some(method), None)) + }; + let (Name(name), h) = self.subcommands.get(ctx_ty, head)?; + let mut res = VecDeque::new(); + if let Some(name) = name { + res.push_back(name); + } + if let Some(tail) = tail { + res.append(&mut h.method_from_dots(tail, ctx_ty)?); + } + Some(res) + } +} + +impl CliBindings for ParentHandler +where + Params: FromArgMatches + CommandFactory + Serialize + Send + Sync + 'static, + InheritedParams: Serialize + Send + Sync + 'static, +{ + type Context = AnyContext; + fn cli_command(&self, ctx_ty: TypeId) -> Command { + let mut base = Params::command().subcommand_required(true); + for (name, handlers) in &self.subcommands.0 { + match ( + name, + if let Some(handler) = handlers.get(&Some(ctx_ty)) { + Some(handler) + } else if let Some(handler) = handlers.get(&None) { + Some(handler) + } else { + None + }, + ) { + (Name(Some(name)), Some(DynHandler::WithCli(handler))) => { + base = base.subcommand(handler.cli_command(ctx_ty).name(name)); + } + (Name(None), Some(DynHandler::WithCli(_))) => { + base = base.subcommand_required(false); + } + _ => (), + } + } + base + } + fn cli_parse( + &self, + matches: &ArgMatches, + ctx_ty: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?; + let (name, matches) = match matches.subcommand() { + Some((name, matches)) => (Some(name), matches), + None => (None, matches), + }; + if let Some((Name(Some(name)), DynHandler::WithCli(handler))) = + self.subcommands.get(ctx_ty, name) + { + let (mut method, params) = handler.cli_parse(matches, ctx_ty)?; + method.push_front(name); + + Ok(( + method, + combine(root_params, params) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e))?, + )) + } else { + Ok((VecDeque::new(), root_params)) + } + } + fn cli_display( + &self, + HandleArgs { + context, + mut parent_method, + mut method, + raw_params, + .. + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + let cmd = method.pop_front(); + if let Some(cmd) = cmd { + parent_method.push_back(cmd); + } + if let Some((_, DynHandler::WithCli(sub_handler))) = + self.subcommands.get(context.inner_type_id(), cmd) + { + sub_handler.cli_display( + HandleAnyArgs { + context, + parent_method, + method, + params: raw_params, + }, + result, + ) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } +} +impl IntoHandlers for ParentHandler +where + Self: HandlerTypes + Handler + CliBindings, + ::Params: DeserializeOwned, + ::InheritedParams: DeserializeOwned, + ::Ok: Serialize + DeserializeOwned, + RpcError: From<::Err>, +{ + fn into_handlers(self) -> impl IntoIterator, DynHandler)> { + iter_from_ctx_and_handler( + self.contexts(), + DynHandler::WithoutCli(Arc::new(AnyHandler::new(self))), + ) + } +} +impl IntoHandlers for NoCli> +where + ParentHandler: HandlerTypes + Handler, + as HandlerTypes>::Params: DeserializeOwned, + as HandlerTypes>::InheritedParams: DeserializeOwned, + as HandlerTypes>::Ok: Serialize + DeserializeOwned, + RpcError: From< as HandlerTypes>::Err>, +{ + fn into_handlers(self) -> impl IntoIterator, DynHandler)> { + iter_from_ctx_and_handler( + self.0.contexts(), + DynHandler::WithoutCli(Arc::new(AnyHandler::new(self.0))), + ) + } +} diff --git a/rpc-toolkit/src/server/mod.rs b/rpc-toolkit/src/server/mod.rs index a56f6f9..52f13f6 100644 --- a/rpc-toolkit/src/server/mod.rs +++ b/rpc-toolkit/src/server/mod.rs @@ -8,7 +8,7 @@ use imbl_value::Value; use yajrc::{RpcError, RpcMethod}; use crate::util::{invalid_request, JobRunner}; -use crate::{AnyHandler, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; +use crate::{AnyContext, AnyHandler, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; type GenericRpcMethod = yajrc::GenericRpcMethod; type RpcRequest = yajrc::RpcRequest; @@ -23,7 +23,7 @@ pub use socket::*; pub struct Server { make_ctx: Arc BoxFuture<'static, Result> + Send + Sync>, - root_handler: Arc>, + root_handler: Arc>, } impl Clone for Server { fn clone(&self) -> Self { diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index d478f17..1415599 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -120,7 +120,7 @@ fn make_api() -> ParentHandler { donde: String, } ParentHandler::new() - .subcommand_remote_cli::( + .subcommand( "echo", from_fn_async( |c: ServerContext, EchoParams { next }: EchoParams| async move { @@ -129,7 +129,8 @@ fn make_api() -> ParentHandler { Value::String(Arc::new(next)), )) }, - ), + ) + .with_remote_cli::(), ) .subcommand( "hello", From bb29367e9407fd2333cfbe0f0657e686010b5714 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 2 Jan 2024 12:45:13 -0700 Subject: [PATCH 24/79] builder api --- .../src/command/build.rs | 2 +- rpc-toolkit/src/handler/adapters.rs | 456 +++++++++++++----- rpc-toolkit/src/handler/from_fn.rs | 85 +--- rpc-toolkit/src/handler/mod.rs | 8 +- rpc-toolkit/src/handler/parent.rs | 199 +------- rpc-toolkit/src/server/http.rs | 4 +- rpc-toolkit/src/server/mod.rs | 2 +- rpc-toolkit/tests/handler.rs | 22 +- 8 files changed, 373 insertions(+), 405 deletions(-) diff --git a/rpc-toolkit-macro-internals/src/command/build.rs b/rpc-toolkit-macro-internals/src/command/build.rs index 15d45dd..8b3190f 100644 --- a/rpc-toolkit-macro-internals/src/command/build.rs +++ b/rpc-toolkit-macro-internals/src/command/build.rs @@ -1362,7 +1362,7 @@ fn build_inherited(parent_data: Option, generics: &Generics) -> (TokenStre ) } else { ( - quote! { type InheritedParams = ::rpc_toolkit::NoParams; }, + quote! { type InheritedParams = ::rpc_toolkit::Empty; }, inherited_generics, ) } diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index 0eca646..5b1c5af 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -3,7 +3,6 @@ use std::collections::VecDeque; use std::fmt::Debug; use std::marker::PhantomData; use std::sync::Arc; -use std::task::Context; use clap::{CommandFactory, FromArgMatches}; use imbl_value::imbl::{OrdMap, OrdSet}; @@ -14,8 +13,8 @@ use yajrc::RpcError; use crate::util::{internal_error, parse_error, Flat}; use crate::{ - intersect_type_ids, iter_from_ctx_and_handler, AnyHandler, CallRemote, CliBindings, DynHandler, - HandleArgs, Handler, HandlerTypes, IntoContext, IntoHandlers, NoParams, PrintCliResult, + iter_from_ctx_and_handler, AnyHandler, CallRemote, CliBindings, DynHandler, EitherContext, + HandleArgs, Handler, HandlerTypes, IntoContext, IntoHandlers, PrintCliResult, }; pub trait HandlerExt: HandlerTypes + Sized { @@ -34,6 +33,13 @@ pub trait HandlerExt: HandlerTypes + Sized { ) -> CustomDisplayFn where F: Fn(HandleArgs, Self::Ok) -> Result<(), Self::Err>; + fn with_inherited( + self, + f: F, + ) -> InheritanceHandler + where + F: Fn(Params, InheritedParams) -> Self::InheritedParams; + fn with_remote_cli(self) -> RemoteCli; } impl HandlerExt for T { @@ -67,6 +73,25 @@ impl HandlerExt for T { handler: self, } } + fn with_inherited( + self, + f: F, + ) -> InheritanceHandler + where + F: Fn(Params, InheritedParams) -> Self::InheritedParams, + { + InheritanceHandler { + _phantom: PhantomData, + handler: self, + inherit: f, + } + } + fn with_remote_cli(self) -> RemoteCli { + RemoteCli { + _phantom: PhantomData, + handler: self, + } + } } #[derive(Debug, Clone)] @@ -77,6 +102,21 @@ impl HandlerTypes for NoCli { type Ok = H::Ok; type Err = H::Err; } +impl IntoHandlers for NoCli +where + H: Handler, + H::Params: DeserializeOwned, + H::InheritedParams: DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, +{ + fn into_handlers(self) -> impl IntoIterator, DynHandler)> { + iter_from_ctx_and_handler( + self.0.contexts(), + DynHandler::WithoutCli(Arc::new(AnyHandler::new(self.0))), + ) + } +} #[derive(Clone, Debug)] pub struct CustomDisplay { @@ -171,13 +211,13 @@ where + 'static, { type Context = P::Context; - fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { + fn cli_command(&self, _: TypeId) -> clap::Command { H::Params::command() } fn cli_parse( &self, matches: &clap::ArgMatches, - ctx_ty: TypeId, + _: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { Self::Params::from_arg_matches(matches).and_then(|a| { Ok(( @@ -212,21 +252,6 @@ where ) } } -impl IntoHandlers for CustomDisplay -where - Self: HandlerTypes + Handler + CliBindings, - ::Params: DeserializeOwned, - ::InheritedParams: DeserializeOwned, - ::Ok: Serialize + DeserializeOwned, - RpcError: From<::Err>, -{ - fn into_handlers(self) -> impl IntoIterator, crate::DynHandler)> { - iter_from_ctx_and_handler( - intersect_type_ids(self.contexts(), ::Context::type_ids()), - DynHandler::WithCli(Arc::new(AnyHandler::new(self))), - ) - } -} pub struct CustomDisplayFn { _phantom: PhantomData, @@ -371,115 +396,282 @@ where } } -// pub struct RemoteCli { -// _phantom: PhantomData<(CliContext, RemoteContext)>, -// handler: H, -// } -// impl Clone for RemoteCli { -// fn clone(&self) -> Self { -// Self { -// _phantom: PhantomData, -// handler: self.handler.clone(), -// } -// } -// } -// impl Debug for RemoteCli { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// f.debug_tuple("RemoteCli").field(&self.handler).finish() -// } -// } -// impl HandlerTypes for RemoteCli -// where -// H: HandlerTypes, -// { -// type Params = H::Params; -// type InheritedParams = H::InheritedParams; -// type Ok = H::Ok; -// type Err = H::Err; -// } -// #[async_trait::async_trait] -// impl Handler> -// for RemoteCli -// where -// CliContext: CallRemote, -// H: Handler, -// { -// async fn handle_async( -// &self, -// HandleArgs { -// context, -// parent_method, -// method, -// params, -// inherited_params, -// raw_params, -// }: HandleArgs, -// ) -> Result { -// let full_method = parent_method.into_iter().chain(method).collect::>(); -// match context -// .call_remote( -// &full_method.join("."), -// imbl_value::to_value(&Flat(params, inherited_params)).map_err(parse_error)?, -// ) -// .await -// { -// Ok(a) => imbl_value::from_value(a) -// .map_err(internal_error) -// .map_err(Self::Err::from), -// Err(e) => Err(Self::Err::from(e)), -// } -// } -// fn metadata( -// &self, -// method: VecDeque<&'static str>, -// ctx_ty: TypeId, -// ) -> OrdMap<&'static str, Value> { -// self.handler.metadata(method, ctx_ty) -// } -// fn contexts(&self) -> Option> { -// todo!() -// } -// fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { -// self.handler.method_from_dots(method, ctx_ty) -// } -// } -// impl CliBindings for RemoteCli -// where -// Context: crate::Context, -// H: CliBindings, -// { -// fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { -// self.handler.cli_command(ctx_ty) -// } -// fn cli_parse( -// &self, -// matches: &clap::ArgMatches, -// ctx_ty: TypeId, -// ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { -// self.handler.cli_parse(matches, ctx_ty) -// } -// fn cli_display( -// &self, -// HandleArgs { -// context, -// parent_method, -// method, -// params, -// inherited_params, -// raw_params, -// }: HandleArgs, -// result: Self::Ok, -// ) -> Result<(), Self::Err> { -// self.handler.cli_display( -// HandleArgs { -// context, -// parent_method, -// method, -// params, -// inherited_params, -// raw_params, -// }, -// result, -// ) -// } -// } +pub struct RemoteCli { + _phantom: PhantomData, + handler: H, +} +impl Clone for RemoteCli { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + handler: self.handler.clone(), + } + } +} +impl Debug for RemoteCli { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("RemoteCli").field(&self.handler).finish() + } +} +impl HandlerTypes for RemoteCli +where + H: HandlerTypes, +{ + type Params = H::Params; + type InheritedParams = H::InheritedParams; + type Ok = H::Ok; + type Err = H::Err; +} +#[async_trait::async_trait] +impl Handler for RemoteCli +where + Context: CallRemote, + H: Handler, + H::Params: Serialize, + H::InheritedParams: Serialize, + H::Ok: DeserializeOwned, + H::Err: From, +{ + type Context = EitherContext; + async fn handle_async( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + match context { + EitherContext::C1(context) => { + let full_method = parent_method.into_iter().chain(method).collect::>(); + match context + .call_remote( + &full_method.join("."), + imbl_value::to_value(&Flat(params, inherited_params)) + .map_err(parse_error)?, + ) + .await + { + Ok(a) => imbl_value::from_value(a) + .map_err(internal_error) + .map_err(Self::Err::from), + Err(e) => Err(Self::Err::from(e)), + } + } + EitherContext::C2(context) => { + self.handler + .handle_async(HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + .await + } + } + } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.handler.metadata(method, ctx_ty) + } + fn contexts(&self) -> Option> { + todo!() + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.handler.method_from_dots(method, ctx_ty) + } +} +impl CliBindings for RemoteCli +where + H: CliBindings, +{ + type Context = H::Context; + fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { + self.handler.cli_command(ctx_ty) + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + ctx_ty: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + self.handler.cli_parse(matches, ctx_ty) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.handler.cli_display( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) + } +} + +pub struct InheritanceHandler { + _phantom: PhantomData<(Params, InheritedParams)>, + handler: H, + inherit: F, +} +impl Clone + for InheritanceHandler +{ + fn clone(&self) -> Self { + Self { + _phantom: PhantomData, + handler: self.handler.clone(), + inherit: self.inherit.clone(), + } + } +} +impl std::fmt::Debug + for InheritanceHandler +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("InheritanceHandler") + .field(&self.handler) + .finish() + } +} +impl HandlerTypes + for InheritanceHandler +where + H: HandlerTypes, + Params: Send + Sync, + InheritedParams: Send + Sync, +{ + type Params = H::Params; + type InheritedParams = Flat; + type Ok = H::Ok; + type Err = H::Err; +} +#[async_trait::async_trait] +impl Handler for InheritanceHandler +where + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + H: Handler, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, +{ + type Context = H::Context; + fn handle_sync( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.handler.handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, + }) + } + async fn handle_async( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.handler.handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, + }) + } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.handler.metadata(method, ctx_ty) + } + fn contexts(&self) -> Option> { + self.handler.contexts() + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.handler.method_from_dots(method, ctx_ty) + } +} + +impl CliBindings + for InheritanceHandler +where + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + H: CliBindings, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, +{ + type Context = H::Context; + fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { + self.handler.cli_command(ctx_ty) + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + ctx_ty: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + self.handler.cli_parse(matches, ctx_ty) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.handler.cli_display( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, + }, + result, + ) + } +} diff --git a/rpc-toolkit/src/handler/from_fn.rs b/rpc-toolkit/src/handler/from_fn.rs index fa06850..e3bec14 100644 --- a/rpc-toolkit/src/handler/from_fn.rs +++ b/rpc-toolkit/src/handler/from_fn.rs @@ -2,7 +2,6 @@ use std::any::TypeId; use std::collections::VecDeque; use std::fmt::Display; use std::marker::PhantomData; -use std::sync::Arc; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; use futures::Future; @@ -10,12 +9,10 @@ use imbl_value::imbl::OrdMap; use imbl_value::Value; use serde::de::DeserializeOwned; use serde::Serialize; -use yajrc::RpcError; use crate::marker::LeafHandler; use crate::{ - intersect_type_ids, iter_from_ctx_and_handler, AnyContext, AnyHandler, CliBindings, DynHandler, - HandleArgs, Handler, HandlerTypes, IntoContext, IntoHandlers, NoCli, NoParams, PrintCliResult, + AnyContext, CliBindings, Empty, HandleArgs, Handler, HandlerTypes, IntoContext, PrintCliResult, }; pub struct FromFn { @@ -58,36 +55,6 @@ where Ok(println!("{result}")) } } -impl IntoHandlers for FromFn -where - Self: HandlerTypes + Handler + CliBindings, - ::Params: DeserializeOwned, - ::InheritedParams: DeserializeOwned, - ::Ok: Serialize + DeserializeOwned, - RpcError: From<::Err>, -{ - fn into_handlers(self) -> impl IntoIterator, crate::DynHandler)> { - iter_from_ctx_and_handler( - intersect_type_ids(self.contexts(), ::Context::type_ids()), - DynHandler::WithCli(Arc::new(AnyHandler::new(self))), - ) - } -} -impl IntoHandlers for NoCli> -where - Self: HandlerTypes + Handler, - ::Params: DeserializeOwned, - ::InheritedParams: DeserializeOwned, - ::Ok: Serialize + DeserializeOwned, - RpcError: From<::Err>, -{ - fn into_handlers(self) -> impl IntoIterator, crate::DynHandler)> { - iter_from_ctx_and_handler( - self.contexts(), - DynHandler::WithoutCli(Arc::new(AnyHandler::new(self))), - ) - } -} pub fn from_fn(function: F) -> FromFn { FromFn { @@ -136,36 +103,6 @@ where Ok(println!("{result}")) } } -impl IntoHandlers for FromFnAsync -where - Self: HandlerTypes + Handler + CliBindings, - ::Params: DeserializeOwned, - ::InheritedParams: DeserializeOwned, - ::Ok: Serialize + DeserializeOwned, - RpcError: From<::Err>, -{ - fn into_handlers(self) -> impl IntoIterator, crate::DynHandler)> { - iter_from_ctx_and_handler( - intersect_type_ids(self.contexts(), ::Context::type_ids()), - DynHandler::WithCli(Arc::new(AnyHandler::new(self))), - ) - } -} -impl IntoHandlers for NoCli> -where - Self: HandlerTypes + Handler, - ::Params: DeserializeOwned, - ::InheritedParams: DeserializeOwned, - ::Ok: Serialize + DeserializeOwned, - RpcError: From<::Err>, -{ - fn into_handlers(self) -> impl IntoIterator, crate::DynHandler)> { - iter_from_ctx_and_handler( - self.contexts(), - DynHandler::WithoutCli(Arc::new(AnyHandler::new(self))), - ) - } -} pub fn from_fn_async(function: F) -> FromFnAsync { FromFnAsync { @@ -181,8 +118,8 @@ where T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Params = NoParams; - type InheritedParams = NoParams; + type Params = Empty; + type InheritedParams = Empty; type Ok = T; type Err = E; } @@ -218,8 +155,8 @@ where T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Params = NoParams; - type InheritedParams = NoParams; + type Params = Empty; + type InheritedParams = Empty; type Ok = T; type Err = E; } @@ -250,8 +187,8 @@ where T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Params = NoParams; - type InheritedParams = NoParams; + type Params = Empty; + type InheritedParams = Empty; type Ok = T; type Err = E; } @@ -292,8 +229,8 @@ where T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Params = NoParams; - type InheritedParams = NoParams; + type Params = Empty; + type InheritedParams = Empty; type Ok = T; type Err = E; } @@ -327,7 +264,7 @@ where E: Send + Sync + 'static, { type Params = Params; - type InheritedParams = NoParams; + type InheritedParams = Empty; type Ok = T; type Err = E; } @@ -374,7 +311,7 @@ where E: Send + Sync + 'static, { type Params = Params; - type InheritedParams = NoParams; + type InheritedParams = Empty; type Ok = T; type Err = E; } diff --git a/rpc-toolkit/src/handler/mod.rs b/rpc-toolkit/src/handler/mod.rs index d859c7c..f531a5b 100644 --- a/rpc-toolkit/src/handler/mod.rs +++ b/rpc-toolkit/src/handler/mod.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use yajrc::RpcError; use crate::context::{AnyContext, IntoContext}; -use crate::util::{internal_error, invalid_params}; +use crate::util::{internal_error, invalid_params, Flat}; pub mod adapters; pub mod from_fn; @@ -311,7 +311,11 @@ where } #[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] -pub struct NoParams {} +pub struct Empty {} + +pub(crate) trait OrEmpty {} +impl OrEmpty for T {} +impl OrEmpty> for Empty {} #[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] pub enum Never {} diff --git a/rpc-toolkit/src/handler/parent.rs b/rpc-toolkit/src/handler/parent.rs index 998f6d6..f742d12 100644 --- a/rpc-toolkit/src/handler/parent.rs +++ b/rpc-toolkit/src/handler/parent.rs @@ -12,15 +12,22 @@ use yajrc::RpcError; use crate::util::{combine, Flat}; use crate::{ - AnyContext, AnyHandler, CliBindings, DynHandler, HandleAny, HandleAnyArgs, HandleArgs, Handler, - HandlerTypes, IntoContext, NoCli, NoParams, + AnyContext, AnyHandler, CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, HandleArgs, + Handler, HandlerTypes, IntoContext, OrEmpty, }; pub trait IntoHandlers: HandlerTypes { fn into_handlers(self) -> impl IntoIterator, DynHandler)>; } -impl IntoHandlers for H { +impl IntoHandlers for H +where + H: Handler + CliBindings, + H::Params: DeserializeOwned, + H::InheritedParams: DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, +{ fn into_handlers(self) -> impl IntoIterator, DynHandler)> { iter_from_ctx_and_handler( intersect_type_ids(self.contexts(), ::Context::type_ids()), @@ -89,7 +96,7 @@ impl SubcommandMap { } } -pub struct ParentHandler { +pub struct ParentHandler { _phantom: PhantomData<(Params, InheritedParams)>, pub(crate) subcommands: SubcommandMap, metadata: OrdMap<&'static str, Value>, @@ -124,152 +131,6 @@ impl std::fmt::Debug for ParentHandler { - _phantom: PhantomData<(Params, InheritedParams)>, - handler: H, - inherit: F, -} -impl Clone - for InheritanceHandler -{ - fn clone(&self) -> Self { - Self { - _phantom: PhantomData, - handler: self.handler.clone(), - inherit: self.inherit.clone(), - } - } -} -impl std::fmt::Debug - for InheritanceHandler -{ - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("InheritanceHandler") - .field(&self.handler) - .finish() - } -} -impl HandlerTypes - for InheritanceHandler -where - H: HandlerTypes, - Params: Send + Sync, - InheritedParams: Send + Sync, -{ - type Params = H::Params; - type InheritedParams = Flat; - type Ok = H::Ok; - type Err = H::Err; -} -#[async_trait::async_trait] -impl Handler for InheritanceHandler -where - Params: Send + Sync + 'static, - InheritedParams: Send + Sync + 'static, - H: Handler, - F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, -{ - type Context = H::Context; - fn handle_sync( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - ) -> Result { - self.handler.handle_sync(HandleArgs { - context, - parent_method, - method, - params, - inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), - raw_params, - }) - } - async fn handle_async( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - ) -> Result { - self.handler.handle_sync(HandleArgs { - context, - parent_method, - method, - params, - inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), - raw_params, - }) - } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.handler.metadata(method, ctx_ty) - } - fn contexts(&self) -> Option> { - self.handler.contexts() - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.handler.method_from_dots(method, ctx_ty) - } -} - -impl CliBindings - for InheritanceHandler -where - Params: Send + Sync + 'static, - InheritedParams: Send + Sync + 'static, - H: CliBindings, - F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, -{ - type Context = H::Context; - fn cli_command(&self, ctx_ty: TypeId) -> Command { - self.handler.cli_command(ctx_ty) - } - fn cli_parse( - &self, - matches: &ArgMatches, - ctx_ty: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.handler.cli_parse(matches, ctx_ty) - } - fn cli_display( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.handler.cli_display( - HandleArgs { - context, - parent_method, - method, - params, - inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), - raw_params, - }, - result, - ) - } -} - impl ParentHandler { fn get_contexts(&self) -> Option> { let mut set = OrdSet::new(); @@ -278,17 +139,21 @@ impl ParentHandler { } Some(set) } + #[allow(private_bounds)] pub fn subcommand(mut self, name: &'static str, handler: H) -> Self where - H: IntoHandlers>, + H: IntoHandlers, + H::InheritedParams: OrEmpty>, { self.subcommands .insert(name.into(), handler.into_handlers()); self } + #[allow(private_bounds)] pub fn root_handler(mut self, handler: H) -> Self where - H: IntoHandlers>, + H: IntoHandlers, + H::InheritedParams: OrEmpty>, { self.subcommands.insert(None, handler.into_handlers()); self @@ -488,33 +353,3 @@ where } } } -impl IntoHandlers for ParentHandler -where - Self: HandlerTypes + Handler + CliBindings, - ::Params: DeserializeOwned, - ::InheritedParams: DeserializeOwned, - ::Ok: Serialize + DeserializeOwned, - RpcError: From<::Err>, -{ - fn into_handlers(self) -> impl IntoIterator, DynHandler)> { - iter_from_ctx_and_handler( - self.contexts(), - DynHandler::WithoutCli(Arc::new(AnyHandler::new(self))), - ) - } -} -impl IntoHandlers for NoCli> -where - ParentHandler: HandlerTypes + Handler, - as HandlerTypes>::Params: DeserializeOwned, - as HandlerTypes>::InheritedParams: DeserializeOwned, - as HandlerTypes>::Ok: Serialize + DeserializeOwned, - RpcError: From< as HandlerTypes>::Err>, -{ - fn into_handlers(self) -> impl IntoIterator, DynHandler)> { - iter_from_ctx_and_handler( - self.0.contexts(), - DynHandler::WithoutCli(Arc::new(AnyHandler::new(self.0))), - ) - } -} diff --git a/rpc-toolkit/src/server/http.rs b/rpc-toolkit/src/server/http.rs index 00f092e..8eba483 100644 --- a/rpc-toolkit/src/server/http.rs +++ b/rpc-toolkit/src/server/http.rs @@ -1,10 +1,8 @@ use std::any::TypeId; -use std::task::Context; use futures::future::{join_all, BoxFuture}; use futures::FutureExt; use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; -use http::request::Parts; use http_body_util::BodyExt; use hyper::body::{Bytes, Incoming}; use hyper::service::Service; @@ -16,7 +14,7 @@ use yajrc::{RpcError, RpcMethod}; use crate::server::{RpcRequest, RpcResponse, SingleOrBatchRpcRequest}; use crate::util::{internal_error, parse_error}; -use crate::{handler, HandleAny, Server}; +use crate::{HandleAny, Server}; const FALLBACK_ERROR: &str = "{\"error\":{\"code\":-32603,\"message\":\"Internal error\",\"data\":\"Failed to serialize rpc response\"}}"; diff --git a/rpc-toolkit/src/server/mod.rs b/rpc-toolkit/src/server/mod.rs index 52f13f6..fca5ccb 100644 --- a/rpc-toolkit/src/server/mod.rs +++ b/rpc-toolkit/src/server/mod.rs @@ -8,7 +8,7 @@ use imbl_value::Value; use yajrc::{RpcError, RpcMethod}; use crate::util::{invalid_request, JobRunner}; -use crate::{AnyContext, AnyHandler, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; +use crate::{AnyHandler, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; type GenericRpcMethod = yajrc::GenericRpcMethod; type RpcRequest = yajrc::RpcRequest; diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index 1415599..cc6e203 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -8,8 +8,8 @@ use clap::Parser; use futures::future::ready; use imbl_value::Value; use rpc_toolkit::{ - call_remote_socket, from_fn, from_fn_async, AnyContext, CallRemote, CliApp, Context, NoParams, - ParentHandler, Server, + call_remote_socket, from_fn, from_fn_async, AnyContext, CallRemote, CliApp, Context, Empty, + HandlerExt, ParentHandler, Server, }; use serde::{Deserialize, Serialize}; use tokio::runtime::{Handle, Runtime}; @@ -141,7 +141,7 @@ fn make_api() -> ParentHandler { .subcommand("a_hello", from_fn_async(a_hello)) .subcommand( "dondes", - ParentHandler::::new().subcommand_with_inherited_no_cli( + ParentHandler::::new().subcommand( "donde", from_fn(|c: CliContext, _: (), donde| { Ok::<_, RpcError>( @@ -151,8 +151,9 @@ fn make_api() -> ParentHandler { ) .to_string(), ) - }), - |InheritParams { donde }, _| donde, + }) + .with_inherited(|InheritParams { donde }, _| donde) + .no_cli(), ), ) .subcommand( @@ -166,21 +167,22 @@ fn make_api() -> ParentHandler { ) .to_string(), ) - }), - |id, _| id, + }) + .with_inherited(|a, _| a), ), ) .subcommand( "error", - ParentHandler::::new().root_handler_no_cli( + ParentHandler::::new().root_handler( from_fn(|c: CliContext, _, InheritParams { donde }| { Err::(RpcError { code: 1, message: "This is an example message".into(), data: None, }) - }), - |id, _| id, + }) + .with_inherited(|a, _| a) + .no_cli(), ), ) } From 3748c4bf898d9187ea131de56c668b9802ba2d68 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 2 Jan 2024 12:48:41 -0700 Subject: [PATCH 25/79] remove todo --- rpc-toolkit/src/handler/adapters.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index 5b1c5af..edd50fc 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -483,7 +483,7 @@ where self.handler.metadata(method, ctx_ty) } fn contexts(&self) -> Option> { - todo!() + Self::Context::type_ids() } fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { self.handler.method_from_dots(method, ctx_ty) From 4bcaf67a0376fb8ab80a3fa287fbd0a040902528 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 2 Jan 2024 13:14:32 -0700 Subject: [PATCH 26/79] add no_display adapter --- rpc-toolkit/src/handler/adapters.rs | 113 +++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 4 deletions(-) diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index edd50fc..d92e4ad 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -13,12 +13,13 @@ use yajrc::RpcError; use crate::util::{internal_error, parse_error, Flat}; use crate::{ - iter_from_ctx_and_handler, AnyHandler, CallRemote, CliBindings, DynHandler, EitherContext, - HandleArgs, Handler, HandlerTypes, IntoContext, IntoHandlers, PrintCliResult, + iter_from_ctx_and_handler, AnyContext, AnyHandler, CallRemote, CliBindings, DynHandler, + EitherContext, HandleArgs, Handler, HandlerTypes, IntoContext, IntoHandlers, PrintCliResult, }; -pub trait HandlerExt: HandlerTypes + Sized { +pub trait HandlerExt: Handler + Sized { fn no_cli(self) -> NoCli; + fn no_display(self) -> NoDisplay; fn with_custom_display

(self, display: P) -> CustomDisplay where P: PrintCliResult< @@ -42,10 +43,13 @@ pub trait HandlerExt: HandlerTypes + Sized { fn with_remote_cli(self) -> RemoteCli; } -impl HandlerExt for T { +impl HandlerExt for T { fn no_cli(self) -> NoCli { NoCli(self) } + fn no_display(self) -> NoDisplay { + NoDisplay(self) + } fn with_custom_display

(self, display: P) -> CustomDisplay where P: PrintCliResult< @@ -118,6 +122,107 @@ where } } +#[derive(Debug, Clone)] +pub struct NoDisplay(pub H); +impl HandlerTypes for NoDisplay { + type Params = H::Params; + type InheritedParams = H::InheritedParams; + type Ok = H::Ok; + type Err = H::Err; +} +#[async_trait::async_trait] +impl Handler for NoDisplay +where + H: Handler, +{ + type Context = H::Context; + fn handle_sync( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.0.handle_sync(HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + } + async fn handle_async( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + ) -> Result { + self.0 + .handle_async(HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + .await + } + fn metadata( + &self, + method: VecDeque<&'static str>, + ctx_ty: TypeId, + ) -> OrdMap<&'static str, Value> { + self.0.metadata(method, ctx_ty) + } + fn contexts(&self) -> Option> { + self.0.contexts() + } + fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + self.0.method_from_dots(method, ctx_ty) + } +} +impl CliBindings for NoDisplay +where + H: HandlerTypes, + H::Params: FromArgMatches + CommandFactory + Serialize, +{ + type Context = AnyContext; + fn cli_command(&self, _: TypeId) -> clap::Command { + H::Params::command() + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + _: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + _: HandleArgs, + _: Self::Ok, + ) -> Result<(), Self::Err> { + Ok(()) + } +} + #[derive(Clone, Debug)] pub struct CustomDisplay { print: P, From 408c3f683ad49d1eb36e2f5a85fd944613b9db8c Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 3 Jan 2024 13:24:10 -0700 Subject: [PATCH 27/79] provide context to middleware in all stages --- rpc-toolkit/src/server/http.rs | 43 ++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/rpc-toolkit/src/server/http.rs b/rpc-toolkit/src/server/http.rs index 8eba483..c5da3f1 100644 --- a/rpc-toolkit/src/server/http.rs +++ b/rpc-toolkit/src/server/http.rs @@ -54,15 +54,16 @@ pub trait Middleware: Clone + Send + Sync + 'static { #[allow(unused_variables)] async fn process_rpc_request( &mut self, + context: &Context, metadata: Self::Metadata, request: &mut RpcRequest, ) -> Result<(), RpcResponse> { Ok(()) } #[allow(unused_variables)] - async fn process_rpc_response(&mut self, response: &mut RpcResponse) {} + async fn process_rpc_response(&mut self, context: &Context, response: &mut RpcResponse) {} #[allow(unused_variables)] - async fn process_http_response(&mut self, response: &mut Response) {} + async fn process_http_response(&mut self, context: &Context, response: &mut Response) {} } #[allow(private_bounds)] @@ -75,12 +76,19 @@ trait _Middleware: Send + Sync { ) -> BoxFuture<'a, Result<(), Response>>; fn process_rpc_request<'a>( &'a mut self, + context: &'a Context, metadata: Value, request: &'a mut RpcRequest, ) -> BoxFuture<'a, Result<(), RpcResponse>>; - fn process_rpc_response<'a>(&'a mut self, response: &'a mut RpcResponse) -> BoxFuture<'a, ()>; + fn process_rpc_response<'a>( + &'a mut self, + + context: &'a Context, + response: &'a mut RpcResponse, + ) -> BoxFuture<'a, ()>; fn process_http_response<'a>( &'a mut self, + context: &'a Context, response: &'a mut Response, ) -> BoxFuture<'a, ()>; } @@ -97,11 +105,13 @@ impl + Send + Sync> _Middleware< } fn process_rpc_request<'a>( &'a mut self, + context: &'a Context, metadata: Value, request: &'a mut RpcRequest, ) -> BoxFuture<'a, Result<(), RpcResponse>> { >::process_rpc_request( self, + context, match imbl_value::from_value(metadata) { Ok(a) => a, Err(e) => return async { Err(internal_error(e).into()) }.boxed(), @@ -109,14 +119,19 @@ impl + Send + Sync> _Middleware< request, ) } - fn process_rpc_response<'a>(&'a mut self, response: &'a mut RpcResponse) -> BoxFuture<'a, ()> { - >::process_rpc_response(self, response) + fn process_rpc_response<'a>( + &'a mut self, + context: &'a Context, + response: &'a mut RpcResponse, + ) -> BoxFuture<'a, ()> { + >::process_rpc_response(self, context, response) } fn process_http_response<'a>( &'a mut self, + context: &'a Context, response: &'a mut Response, ) -> BoxFuture<'a, ()> { - >::process_http_response(self, response) + >::process_http_response(self, context, response) } } @@ -171,10 +186,11 @@ impl HttpServer { .map_err(parse_error)? { SingleOrBatchRpcRequest::Single(rpc_req) => { - let mut res = - json_http_response(&self.process_rpc_request(&mut mid, rpc_req).await); + let mut res = json_http_response( + &self.process_rpc_request(&ctx, &mut mid, rpc_req).await, + ); for middleware in mid.iter_mut() { - middleware.0.process_http_response(&mut res).await; + middleware.0.process_http_response(&ctx, &mut res).await; } Ok(res) } @@ -182,7 +198,7 @@ impl HttpServer { let (mids, rpc_res): (Vec<_>, Vec<_>) = join_all(rpc_reqs.into_iter().map(|rpc_req| async { let mut mid = mid.clone(); - let res = self.process_rpc_request(&mut mid, rpc_req).await; + let res = self.process_rpc_request(&ctx, &mut mid, rpc_req).await; (mid, res) })) .await @@ -199,7 +215,7 @@ impl HttpServer { }, ) { for middleware in mid.iter_mut() { - middleware.0.process_http_response(&mut res).await; + middleware.0.process_http_response(&ctx, &mut res).await; } } Ok(res) @@ -217,6 +233,7 @@ impl HttpServer { } async fn process_rpc_request( &self, + ctx: &Context, mid: &mut Vec>, mut req: RpcRequest, ) -> RpcResponse { @@ -247,7 +264,7 @@ impl HttpServer { for middleware in mid.iter_mut().rev() { if let Err(res) = middleware .0 - .process_rpc_request(metadata.clone(), &mut req) + .process_rpc_request(ctx, metadata.clone(), &mut req) .await { return res; @@ -257,7 +274,7 @@ impl HttpServer { } .await; for middleware in mid.iter_mut() { - middleware.0.process_rpc_response(&mut res).await; + middleware.0.process_rpc_response(ctx, &mut res).await; } res } From db64cc6134aca424793faf84d4984fd063153368 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 3 Jan 2024 13:27:09 -0700 Subject: [PATCH 28/79] publish boxbody type --- rpc-toolkit/src/server/http.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc-toolkit/src/server/http.rs b/rpc-toolkit/src/server/http.rs index c5da3f1..2bb4171 100644 --- a/rpc-toolkit/src/server/http.rs +++ b/rpc-toolkit/src/server/http.rs @@ -38,7 +38,7 @@ pub fn json_http_response(t: &T) -> Response { .unwrap_or_else(|_| fallback_rpc_error_response()) } -type BoxBody = http_body_util::combinators::BoxBody; +pub type BoxBody = http_body_util::combinators::BoxBody; #[async_trait::async_trait] pub trait Middleware: Clone + Send + Sync + 'static { From 711d0a729a87d3d9831990a0f988e81ff77b748d Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 3 Jan 2024 13:31:04 -0700 Subject: [PATCH 29/79] publish rpc types --- rpc-toolkit/src/server/mod.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rpc-toolkit/src/server/mod.rs b/rpc-toolkit/src/server/mod.rs index fca5ccb..cc9a87a 100644 --- a/rpc-toolkit/src/server/mod.rs +++ b/rpc-toolkit/src/server/mod.rs @@ -10,10 +10,10 @@ use yajrc::{RpcError, RpcMethod}; use crate::util::{invalid_request, JobRunner}; use crate::{AnyHandler, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; -type GenericRpcMethod = yajrc::GenericRpcMethod; -type RpcRequest = yajrc::RpcRequest; -type RpcResponse = yajrc::RpcResponse; -type SingleOrBatchRpcRequest = yajrc::SingleOrBatchRpcRequest; +pub type GenericRpcMethod = yajrc::GenericRpcMethod; +pub type RpcRequest = yajrc::RpcRequest; +pub type RpcResponse = yajrc::RpcResponse; +pub type SingleOrBatchRpcRequest = yajrc::SingleOrBatchRpcRequest; mod http; mod socket; From 1024f0feccff3c367ab1032cb15b784118d1b693 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 3 Jan 2024 16:42:30 -0700 Subject: [PATCH 30/79] update cargo lock --- Cargo.lock | 210 +++++++++++++++++++++++------------------------------ 1 file changed, 89 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 17d221d..f83480c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", "getrandom", @@ -80,9 +80,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.75" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" +checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" [[package]] name = "async-stream" @@ -103,18 +103,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", ] [[package]] name = "async-trait" -version = "0.1.74" +version = "0.1.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" +checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", ] [[package]] @@ -191,9 +191,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.11" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfaff671f6b22ca62406885ece523383b9b64022e341e53e009a62ebc47a45f2" +checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" dependencies = [ "clap_builder", "clap_derive", @@ -201,9 +201,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.11" +version = "4.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a216b506622bb1d316cd51328dce24e07bdff4a6128a47c7e7fad11878d5adbb" +checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" dependencies = [ "anstream", "anstyle", @@ -220,7 +220,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", ] [[package]] @@ -320,9 +320,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" +checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", @@ -335,9 +335,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" +checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" dependencies = [ "futures-core", "futures-sink", @@ -345,15 +345,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" +checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" [[package]] name = "futures-executor" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" +checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" dependencies = [ "futures-core", "futures-task", @@ -362,38 +362,38 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" +checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" [[package]] name = "futures-macro" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" +checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", ] [[package]] name = "futures-sink" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" +checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" [[package]] name = "futures-task" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" +checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" [[package]] name = "futures-util" -version = "0.3.29" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" +checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ "futures-channel", "futures-core", @@ -565,9 +565,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.27" +version = "0.14.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" +checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" dependencies = [ "bytes", "futures-channel", @@ -580,7 +580,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.4.10", + "socket2", "tokio", "tower-service", "tracing", @@ -589,9 +589,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f9214f3e703236b221f1a9cd88ec8b4adfa5296de01ab96216361f4692f56" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" dependencies = [ "bytes", "futures-channel", @@ -614,7 +614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper 0.14.27", + "hyper 0.14.28", "native-tls", "tokio", "tokio-native-tls", @@ -747,9 +747,9 @@ checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "memchr" -version = "2.6.4" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" [[package]] name = "mime" @@ -807,9 +807,9 @@ dependencies = [ [[package]] name = "object" -version = "0.32.1" +version = "0.32.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" dependencies = [ "memchr", ] @@ -822,9 +822,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.61" +version = "0.10.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45" +checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" dependencies = [ "bitflags 2.4.1", "cfg-if", @@ -843,7 +843,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", ] [[package]] @@ -863,9 +863,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.97" +version = "0.9.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b" +checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" dependencies = [ "cc", "libc", @@ -920,7 +920,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", ] [[package]] @@ -937,24 +937,24 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" [[package]] name = "proc-macro2" -version = "1.0.70" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -985,9 +985,9 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.22" +version = "0.11.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" dependencies = [ "base64", "bytes", @@ -997,7 +997,7 @@ dependencies = [ "h2 0.3.22", "http 0.2.11", "http-body 0.4.6", - "hyper 0.14.27", + "hyper 0.14.28", "hyper-tls", "ipnet", "js-sys", @@ -1031,7 +1031,7 @@ dependencies = [ "futures", "http 1.0.0", "http-body-util", - "hyper 1.0.1", + "hyper 1.1.0", "imbl-value", "itertools", "lazy_format", @@ -1096,11 +1096,11 @@ checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" [[package]] name = "schannel" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1134,9 +1134,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.193" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" dependencies = [ "serde_derive", ] @@ -1153,20 +1153,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.194" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" dependencies = [ "itoa", "ryu", @@ -1209,16 +1209,6 @@ version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.5" @@ -1248,9 +1238,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.41" +version = "2.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c8b28c477cc3bf0e7966561e3460130e1255f7a1cf71931075f1c5e7a7e269" +checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" dependencies = [ "proc-macro2", "quote", @@ -1280,35 +1270,35 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.8.1" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" +checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" dependencies = [ "cfg-if", "fastrand", "redox_syscall", "rustix", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", ] [[package]] @@ -1328,9 +1318,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.0" +version = "1.35.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c" +checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" dependencies = [ "backtrace", "bytes", @@ -1340,7 +1330,7 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.5", + "socket2", "tokio-macros", "windows-sys 0.48.0", ] @@ -1353,7 +1343,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", ] [[package]] @@ -1508,7 +1498,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", "wasm-bindgen-shared", ] @@ -1542,7 +1532,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1563,28 +1553,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "windows-sys" version = "0.48.0" @@ -1753,20 +1721,20 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.30" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "306dca4455518f1f31635ec308b6b3e4eb1b11758cefafc782827d0aa7acb5c7" +checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.30" +version = "0.7.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba" +checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.41", + "syn 2.0.47", ] From e13808674ad24717376ded64a35106a7481dfc4d Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 3 Jan 2024 16:59:34 -0700 Subject: [PATCH 31/79] enforce trait bounds at call site --- rpc-toolkit/src/handler/from_fn.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/rpc-toolkit/src/handler/from_fn.rs b/rpc-toolkit/src/handler/from_fn.rs index e3bec14..e9cf099 100644 --- a/rpc-toolkit/src/handler/from_fn.rs +++ b/rpc-toolkit/src/handler/from_fn.rs @@ -56,7 +56,10 @@ where } } -pub fn from_fn(function: F) -> FromFn { +pub fn from_fn(function: F) -> FromFn +where + FromFn: HandlerTypes, +{ FromFn { function, _phantom: PhantomData, @@ -65,7 +68,10 @@ pub fn from_fn(function: F) -> FromFn { } } -pub fn from_fn_blocking(function: F) -> FromFn { +pub fn from_fn_blocking(function: F) -> FromFn +where + FromFn: HandlerTypes, +{ FromFn { function, _phantom: PhantomData, @@ -104,7 +110,10 @@ where } } -pub fn from_fn_async(function: F) -> FromFnAsync { +pub fn from_fn_async(function: F) -> FromFnAsync +where + FromFnAsync: HandlerTypes, +{ FromFnAsync { function, _phantom: PhantomData, From ba431972bffb46e2b080f3b14c54dac781b5158b Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 3 Jan 2024 17:13:12 -0700 Subject: [PATCH 32/79] make phantom Send/Sync --- rpc-toolkit/src/cli.rs | 9 +++---- rpc-toolkit/src/command.rs | 3 +-- rpc-toolkit/src/handler/adapters.rs | 15 +++++------ rpc-toolkit/src/handler/from_fn.rs | 42 ++++++++++++++++++----------- rpc-toolkit/src/handler/marker.rs | 1 - rpc-toolkit/src/handler/mod.rs | 1 - rpc-toolkit/src/handler/parent.rs | 7 +++-- rpc-toolkit/src/util.rs | 33 +++++++++++++---------- 8 files changed, 60 insertions(+), 51 deletions(-) delete mode 100644 rpc-toolkit/src/handler/marker.rs diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index c518842..bb84a25 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -1,7 +1,6 @@ use std::any::TypeId; use std::collections::VecDeque; use std::ffi::OsString; -use std::marker::PhantomData; use clap::{CommandFactory, FromArgMatches}; use imbl_value::Value; @@ -12,7 +11,7 @@ use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader use url::Url; use yajrc::{Id, RpcError}; -use crate::util::{internal_error, parse_error, Flat}; +use crate::util::{internal_error, parse_error, Flat, PhantomData}; use crate::{ AnyHandler, CliBindings, CliBindingsAny, DynHandler, HandleAny, HandleAnyArgs, HandleArgs, Handler, HandlerTypes, IntoContext, Name, ParentHandler, @@ -35,7 +34,7 @@ impl root_handler: ParentHandler, ) -> Self { Self { - _phantom: PhantomData, + _phantom: PhantomData::new(), make_ctx: Box::new(make_ctx), root_handler, } @@ -172,7 +171,7 @@ pub struct CallRemoteHandler { impl CallRemoteHandler { pub fn new(handler: RemoteHandler) -> Self { Self { - _phantom: PhantomData, + _phantom: PhantomData::new(), handler: handler, } } @@ -180,7 +179,7 @@ impl CallRemoteHandler { impl Clone for CallRemoteHandler { fn clone(&self) -> Self { Self { - _phantom: PhantomData, + _phantom: PhantomData::new(), handler: self.handler.clone(), } } diff --git a/rpc-toolkit/src/command.rs b/rpc-toolkit/src/command.rs index 3dabecf..8ad8645 100644 --- a/rpc-toolkit/src/command.rs +++ b/rpc-toolkit/src/command.rs @@ -1,4 +1,3 @@ -use std::marker::PhantomData; use std::sync::Arc; use clap::{ArgMatches, CommandFactory, FromArgMatches}; @@ -9,7 +8,7 @@ use serde::de::DeserializeOwned; use serde::ser::Serialize; use yajrc::RpcError; -use crate::util::{extract, Flat}; +use crate::util::{extract, Flat, PhantomData}; /// Stores a command's implementation for a given context /// Can be created from anything that implements ParentCommand, AsyncCommand, or SyncCommand diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index d92e4ad..1b08329 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -1,7 +1,6 @@ use std::any::TypeId; use std::collections::VecDeque; use std::fmt::Debug; -use std::marker::PhantomData; use std::sync::Arc; use clap::{CommandFactory, FromArgMatches}; @@ -11,7 +10,7 @@ use serde::de::DeserializeOwned; use serde::Serialize; use yajrc::RpcError; -use crate::util::{internal_error, parse_error, Flat}; +use crate::util::{internal_error, parse_error, Flat, PhantomData}; use crate::{ iter_from_ctx_and_handler, AnyContext, AnyHandler, CallRemote, CliBindings, DynHandler, EitherContext, HandleArgs, Handler, HandlerTypes, IntoContext, IntoHandlers, PrintCliResult, @@ -72,7 +71,7 @@ impl HandlerExt for T { F: Fn(HandleArgs, Self::Ok) -> Result<(), Self::Err>, { CustomDisplayFn { - _phantom: PhantomData, + _phantom: PhantomData::new(), print: display, handler: self, } @@ -85,14 +84,14 @@ impl HandlerExt for T { F: Fn(Params, InheritedParams) -> Self::InheritedParams, { InheritanceHandler { - _phantom: PhantomData, + _phantom: PhantomData::new(), handler: self, inherit: f, } } fn with_remote_cli(self) -> RemoteCli { RemoteCli { - _phantom: PhantomData, + _phantom: PhantomData::new(), handler: self, } } @@ -366,7 +365,7 @@ pub struct CustomDisplayFn { impl Clone for CustomDisplayFn { fn clone(&self) -> Self { Self { - _phantom: PhantomData, + _phantom: PhantomData::new(), print: self.print.clone(), handler: self.handler.clone(), } @@ -508,7 +507,7 @@ pub struct RemoteCli { impl Clone for RemoteCli { fn clone(&self) -> Self { Self { - _phantom: PhantomData, + _phantom: PhantomData::new(), handler: self.handler.clone(), } } @@ -645,7 +644,7 @@ impl Clone { fn clone(&self) -> Self { Self { - _phantom: PhantomData, + _phantom: PhantomData::new(), handler: self.handler.clone(), inherit: self.inherit.clone(), } diff --git a/rpc-toolkit/src/handler/from_fn.rs b/rpc-toolkit/src/handler/from_fn.rs index e9cf099..7c151a3 100644 --- a/rpc-toolkit/src/handler/from_fn.rs +++ b/rpc-toolkit/src/handler/from_fn.rs @@ -1,7 +1,6 @@ use std::any::TypeId; use std::collections::VecDeque; use std::fmt::Display; -use std::marker::PhantomData; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; use futures::Future; @@ -10,7 +9,7 @@ use imbl_value::Value; use serde::de::DeserializeOwned; use serde::Serialize; -use crate::marker::LeafHandler; +use crate::util::PhantomData; use crate::{ AnyContext, CliBindings, Empty, HandleArgs, Handler, HandlerTypes, IntoContext, PrintCliResult, }; @@ -30,7 +29,7 @@ impl FromFn { impl Clone for FromFn { fn clone(&self) -> Self { Self { - _phantom: PhantomData, + _phantom: PhantomData::new(), function: self.function.clone(), blocking: self.blocking, metadata: self.metadata.clone(), @@ -44,7 +43,6 @@ impl std::fmt::Debug for FromFn { .finish() } } -impl LeafHandler for FromFn {} impl PrintCliResult for FromFn where Self: HandlerTypes, @@ -62,7 +60,7 @@ where { FromFn { function, - _phantom: PhantomData, + _phantom: PhantomData::new(), blocking: false, metadata: OrdMap::new(), } @@ -74,7 +72,7 @@ where { FromFn { function, - _phantom: PhantomData, + _phantom: PhantomData::new(), blocking: true, metadata: OrdMap::new(), } @@ -85,10 +83,22 @@ pub struct FromFnAsync { function: F, metadata: OrdMap<&'static str, Value>, } +unsafe impl Send for FromFnAsync +where + F: Send, + OrdMap<&'static str, Value>: Send, +{ +} +unsafe impl Sync for FromFnAsync +where + F: Sync, + OrdMap<&'static str, Value>: Sync, +{ +} impl Clone for FromFnAsync { fn clone(&self) -> Self { Self { - _phantom: PhantomData, + _phantom: PhantomData::new(), function: self.function.clone(), metadata: self.metadata.clone(), } @@ -116,7 +126,7 @@ where { FromFnAsync { function, - _phantom: PhantomData, + _phantom: PhantomData::new(), metadata: OrdMap::new(), } } @@ -160,7 +170,7 @@ where impl HandlerTypes for FromFnAsync where F: Fn() -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, + Fut: Future> + Send + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { @@ -173,7 +183,7 @@ where impl Handler for FromFnAsync where F: Fn() -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, + Fut: Future> + Send + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { @@ -234,7 +244,7 @@ impl HandlerTypes for FromFnAsync Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, + Fut: Future> + Send + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { @@ -248,7 +258,7 @@ impl Handler for FromFnAsync where Context: IntoContext, F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, + Fut: Future> + Send + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { @@ -314,7 +324,7 @@ impl HandlerTypes for FromFnAsync Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, + Fut: Future> + Send + 'static, Params: DeserializeOwned + Send + Sync + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, @@ -329,7 +339,7 @@ impl Handler for FromFnAsync Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, + Fut: Future> + Send + 'static, Params: DeserializeOwned + Send + Sync + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, @@ -407,7 +417,7 @@ impl HandlerTypes where Context: IntoContext, F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, + Fut: Future> + Send + 'static, Params: DeserializeOwned + Send + Sync + 'static, InheritedParams: DeserializeOwned + Send + Sync + 'static, T: Send + Sync + 'static, @@ -424,7 +434,7 @@ impl Handler where Context: IntoContext, F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, - Fut: Future> + Send + Sync + 'static, + Fut: Future> + Send + 'static, Params: DeserializeOwned + Send + Sync + 'static, InheritedParams: DeserializeOwned + Send + Sync + 'static, T: Send + Sync + 'static, diff --git a/rpc-toolkit/src/handler/marker.rs b/rpc-toolkit/src/handler/marker.rs deleted file mode 100644 index 32a0f18..0000000 --- a/rpc-toolkit/src/handler/marker.rs +++ /dev/null @@ -1 +0,0 @@ -pub trait LeafHandler {} diff --git a/rpc-toolkit/src/handler/mod.rs b/rpc-toolkit/src/handler/mod.rs index f531a5b..36a898e 100644 --- a/rpc-toolkit/src/handler/mod.rs +++ b/rpc-toolkit/src/handler/mod.rs @@ -15,7 +15,6 @@ use crate::util::{internal_error, invalid_params, Flat}; pub mod adapters; pub mod from_fn; -pub mod marker; pub mod parent; pub use adapters::*; diff --git a/rpc-toolkit/src/handler/parent.rs b/rpc-toolkit/src/handler/parent.rs index f742d12..d137a94 100644 --- a/rpc-toolkit/src/handler/parent.rs +++ b/rpc-toolkit/src/handler/parent.rs @@ -1,6 +1,5 @@ use std::any::TypeId; use std::collections::VecDeque; -use std::marker::PhantomData; use std::sync::Arc; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; @@ -10,7 +9,7 @@ use serde::de::DeserializeOwned; use serde::Serialize; use yajrc::RpcError; -use crate::util::{combine, Flat}; +use crate::util::{combine, Flat, PhantomData}; use crate::{ AnyContext, AnyHandler, CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, HandleArgs, Handler, HandlerTypes, IntoContext, OrEmpty, @@ -104,7 +103,7 @@ pub struct ParentHandler { impl ParentHandler { pub fn new() -> Self { Self { - _phantom: PhantomData, + _phantom: PhantomData::new(), subcommands: SubcommandMap(OrdMap::new()), metadata: OrdMap::new(), } @@ -117,7 +116,7 @@ impl ParentHandler { impl Clone for ParentHandler { fn clone(&self) -> Self { Self { - _phantom: PhantomData, + _phantom: PhantomData::new(), subcommands: self.subcommands.clone(), metadata: self.metadata.clone(), } diff --git a/rpc-toolkit/src/util.rs b/rpc-toolkit/src/util.rs index 7667930..b457e58 100644 --- a/rpc-toolkit/src/util.rs +++ b/rpc-toolkit/src/util.rs @@ -1,4 +1,4 @@ -use std::fmt::Display; +use std::fmt::{Debug, Display}; use futures::future::{BoxFuture, FusedFuture}; use futures::stream::FusedStream; @@ -222,16 +222,21 @@ where } } -// #[derive(Debug)] -// pub enum Infallible {} -// impl From for T { -// fn from(value: Infallible) -> Self { -// match value {} -// } -// } -// impl std::fmt::Display for Infallible { -// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -// match *self {} -// } -// } -// impl std::error::Error for Infallible {} +pub struct PhantomData(std::marker::PhantomData); +impl PhantomData { + pub fn new() -> Self { + PhantomData(std::marker::PhantomData) + } +} +impl Clone for PhantomData { + fn clone(&self) -> Self { + PhantomData::new() + } +} +impl Debug for PhantomData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} +unsafe impl Send for PhantomData {} +unsafe impl Sync for PhantomData {} From 8dcecad033e9924c40418eecf80ee466d10319ef Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 4 Jan 2024 11:39:46 -0700 Subject: [PATCH 33/79] use imbl-value from GH --- Cargo.lock | 3 +-- rpc-toolkit/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f83480c..4f565aa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -656,8 +656,7 @@ dependencies = [ [[package]] name = "imbl-value" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6d3d8cdfd1ac46aab6195692baf9c65dd34dcc7e7ddfb426a2c014736ba90c" +source = "git+https://github.com/Start9Labs/imbl-value.git#48dc39a762a3b4f9300d3b9f850cbd394e777ae0" dependencies = [ "imbl", "serde", diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml index ada859c..7ab144d 100644 --- a/rpc-toolkit/Cargo.toml +++ b/rpc-toolkit/Cargo.toml @@ -24,7 +24,7 @@ http = "1" http-body-util = "0.1" hyper = { version = "1", features = ["server", "http1", "http2", "client"] } itertools = "0.12" -imbl-value = "0.1" +imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } lazy_format = "2" lazy_static = "1.4" openssl = { version = "0.10", features = ["vendored"] } From b873ef37d2190d77d9490ac6f59b3278460aa758 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 4 Jan 2024 12:02:13 -0700 Subject: [PATCH 34/79] publish modules --- rpc-toolkit/src/server/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rpc-toolkit/src/server/mod.rs b/rpc-toolkit/src/server/mod.rs index cc9a87a..91c7fcb 100644 --- a/rpc-toolkit/src/server/mod.rs +++ b/rpc-toolkit/src/server/mod.rs @@ -15,8 +15,8 @@ pub type RpcRequest = yajrc::RpcRequest; pub type RpcResponse = yajrc::RpcResponse; pub type SingleOrBatchRpcRequest = yajrc::SingleOrBatchRpcRequest; -mod http; -mod socket; +pub mod http; +pub mod socket; pub use http::*; pub use socket::*; From b2bb37685366f18d2f49e6b62bf9e5f35b6e8972 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 4 Jan 2024 13:12:08 -0700 Subject: [PATCH 35/79] support axum instead of hyper --- Cargo.lock | 127 ++++++++++++++++++++++++++++++++- rpc-toolkit/Cargo.toml | 3 +- rpc-toolkit/src/lib.rs | 2 +- rpc-toolkit/src/server/http.rs | 69 ++++++++---------- 4 files changed, 158 insertions(+), 43 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f565aa..ab70355 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -123,6 +123,61 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "axum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d09dbe0e490df5da9d69b36dca48a76635288a82f92eca90024883a56202026d" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.1.0", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e87c8503f93e6d144ee5690907ba22db7ba79ab001a932ab99034f0fe836b3df" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -604,7 +659,6 @@ dependencies = [ "itoa", "pin-project-lite", "tokio", - "want", ] [[package]] @@ -620,6 +674,24 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyper-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", + "pin-project-lite", + "socket2", + "tokio", + "tracing", +] + [[package]] name = "idna" version = "0.5.0" @@ -744,6 +816,12 @@ version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +[[package]] +name = "matchit" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" + [[package]] name = "memchr" version = "2.7.1" @@ -1026,11 +1104,11 @@ version = "0.2.3" dependencies = [ "async-stream", "async-trait", + "axum", "clap", "futures", "http 1.0.0", "http-body-util", - "hyper 1.1.0", "imbl-value", "itertools", "lazy_format", @@ -1087,6 +1165,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + [[package]] name = "ryu" version = "1.0.16" @@ -1172,6 +1256,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1246,6 +1340,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "sync_wrapper" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + [[package]] name = "system-configuration" version = "0.5.1" @@ -1380,6 +1480,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -1392,6 +1514,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-core", ] diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml index 7ab144d..5604917 100644 --- a/rpc-toolkit/Cargo.toml +++ b/rpc-toolkit/Cargo.toml @@ -16,13 +16,14 @@ cbor = ["serde_cbor"] default = ["cbor"] [dependencies] +axum = "0.7.3" async-stream = "0.3" async-trait = "0.1" clap = { version = "4", features = ["derive"] } futures = "0.3" http = "1" http-body-util = "0.1" -hyper = { version = "1", features = ["server", "http1", "http2", "client"] } +# hyper = { version = "1", features = ["server", "http1", "http2", "client"] } itertools = "0.12" imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } lazy_format = "2" diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs index d1a60c2..ef404ef 100644 --- a/rpc-toolkit/src/lib.rs +++ b/rpc-toolkit/src/lib.rs @@ -25,7 +25,7 @@ pub use handler::*; /// See also: [arg](rpc_toolkit_macro::arg), [context](rpc_toolkit_macro::context) pub use rpc_toolkit_macro::command; pub use server::*; -pub use {clap, futures, hyper, reqwest, serde, serde_json, tokio, url, yajrc}; +pub use {clap, futures, reqwest, serde, serde_json, tokio, url, yajrc}; mod cli; pub mod command_helpers; diff --git a/rpc-toolkit/src/server/http.rs b/rpc-toolkit/src/server/http.rs index 2bb4171..f433825 100644 --- a/rpc-toolkit/src/server/http.rs +++ b/rpc-toolkit/src/server/http.rs @@ -1,12 +1,14 @@ use std::any::TypeId; +use axum::body::Body; +use axum::extract::Request; +use axum::handler::Handler; +use axum::response::Response; use futures::future::{join_all, BoxFuture}; use futures::FutureExt; use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; use http_body_util::BodyExt; -use hyper::body::{Bytes, Incoming}; -use hyper::service::Service; -use hyper::{Request, Response}; +use imbl_value::imbl::Vector; use imbl_value::Value; use serde::de::DeserializeOwned; use serde::Serialize; @@ -18,15 +20,15 @@ use crate::{HandleAny, Server}; const FALLBACK_ERROR: &str = "{\"error\":{\"code\":-32603,\"message\":\"Internal error\",\"data\":\"Failed to serialize rpc response\"}}"; -pub fn fallback_rpc_error_response() -> Response { +pub fn fallback_rpc_error_response() -> Response { Response::builder() .header(CONTENT_TYPE, "application/json") .header(CONTENT_LENGTH, FALLBACK_ERROR.len()) - .body(Bytes::from_static(FALLBACK_ERROR.as_bytes())) + .body(Body::from(FALLBACK_ERROR.as_bytes())) .unwrap() } -pub fn json_http_response(t: &T) -> Response { +pub fn json_http_response(t: &T) -> Response { let body = match serde_json::to_vec(t) { Ok(a) => a, Err(_) => return fallback_rpc_error_response(), @@ -34,12 +36,10 @@ pub fn json_http_response(t: &T) -> Response { Response::builder() .header(CONTENT_TYPE, "application/json") .header(CONTENT_LENGTH, body.len()) - .body(Bytes::from(body)) + .body(Body::from(body)) .unwrap_or_else(|_| fallback_rpc_error_response()) } -pub type BoxBody = http_body_util::combinators::BoxBody; - #[async_trait::async_trait] pub trait Middleware: Clone + Send + Sync + 'static { type Metadata: DeserializeOwned + Send + 'static; @@ -47,8 +47,8 @@ pub trait Middleware: Clone + Send + Sync + 'static { async fn process_http_request( &mut self, context: &Context, - request: &mut Request, - ) -> Result<(), Response> { + request: &mut Request, + ) -> Result<(), Response> { Ok(()) } #[allow(unused_variables)] @@ -63,7 +63,7 @@ pub trait Middleware: Clone + Send + Sync + 'static { #[allow(unused_variables)] async fn process_rpc_response(&mut self, context: &Context, response: &mut RpcResponse) {} #[allow(unused_variables)] - async fn process_http_response(&mut self, context: &Context, response: &mut Response) {} + async fn process_http_response(&mut self, context: &Context, response: &mut Response) {} } #[allow(private_bounds)] @@ -72,8 +72,8 @@ trait _Middleware: Send + Sync { fn process_http_request<'a>( &'a mut self, context: &'a Context, - request: &'a mut Request, - ) -> BoxFuture<'a, Result<(), Response>>; + request: &'a mut Request, + ) -> BoxFuture<'a, Result<(), Response>>; fn process_rpc_request<'a>( &'a mut self, context: &'a Context, @@ -89,7 +89,7 @@ trait _Middleware: Send + Sync { fn process_http_response<'a>( &'a mut self, context: &'a Context, - response: &'a mut Response, + response: &'a mut Response, ) -> BoxFuture<'a, ()>; } impl + Send + Sync> _Middleware for T { @@ -99,8 +99,8 @@ impl + Send + Sync> _Middleware< fn process_http_request<'a>( &'a mut self, context: &'a Context, - request: &'a mut Request, - ) -> BoxFuture<'a, Result<(), Response>> { + request: &'a mut Request, + ) -> BoxFuture<'a, Result<(), Response>> { >::process_http_request(self, context, request) } fn process_rpc_request<'a>( @@ -129,7 +129,7 @@ impl + Send + Sync> _Middleware< fn process_http_response<'a>( &'a mut self, context: &'a Context, - response: &'a mut Response, + response: &'a mut Response, ) -> BoxFuture<'a, ()> { >::process_http_response(self, context, response) } @@ -144,7 +144,7 @@ impl Clone for DynMiddleware { pub struct HttpServer { inner: Server, - middleware: Vec>, + middleware: Vector>, } impl Clone for HttpServer { fn clone(&self) -> Self { @@ -158,7 +158,7 @@ impl Server { pub fn for_http(self) -> HttpServer { HttpServer { inner: self, - middleware: Vec::new(), + middleware: Vector::new(), } } pub fn middleware>(self, middleware: T) -> HttpServer { @@ -167,10 +167,11 @@ impl Server { } impl HttpServer { pub fn middleware>(mut self, middleware: T) -> Self { - self.middleware.push(DynMiddleware(Box::new(middleware))); + self.middleware + .push_back(DynMiddleware(Box::new(middleware))); self } - async fn process_http_request(&self, mut req: Request) -> Response { + async fn process_http_request(&self, mut req: Request) -> Response { let mut mid = self.middleware.clone(); match async { let ctx = (self.inner.make_ctx)().await?; @@ -234,7 +235,7 @@ impl HttpServer { async fn process_rpc_request( &self, ctx: &Context, - mid: &mut Vec>, + mid: &mut Vector>, mut req: RpcRequest, ) -> RpcResponse { let metadata = Value::Object( @@ -278,25 +279,15 @@ impl HttpServer { } res } - pub fn handle(&self, req: Request) -> BoxFuture<'static, Response> { + pub fn handle(&self, req: Request) -> BoxFuture<'static, Response> { let server = self.clone(); - async move { - server - .process_http_request(req.map(|b| BoxBody::new(b))) - .await - } - .boxed() + async move { server.process_http_request(req).await }.boxed() } } -impl Service> for HttpServer { - type Response = Response; - type Error = hyper::Error; - type Future = futures::future::Map< - BoxFuture<'static, Self::Response>, - fn(Self::Response) -> Result, - >; - fn call(&self, req: Request) -> Self::Future { - self.handle(req).map(Ok) +impl Handler<(), ()> for HttpServer { + type Future = BoxFuture<'static, Response>; + fn call(self, req: Request, _: ()) -> Self::Future { + self.handle(req) } } From 3df960f8be4b3c6307e48c4176b7c1e5d686b829 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 4 Jan 2024 14:52:09 -0700 Subject: [PATCH 36/79] remove debug requirement --- rpc-toolkit/src/handler/adapters.rs | 12 +++--------- rpc-toolkit/src/handler/mod.rs | 6 +++--- rpc-toolkit/src/handler/parent.rs | 4 ++-- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index 1b08329..b4a8924 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -240,7 +240,7 @@ where impl Handler for CustomDisplay where H: Handler, - P: Send + Sync + Clone + Debug + 'static, + P: Send + Sync + Clone + 'static, { type Context = H::Context; fn handle_sync( @@ -311,7 +311,6 @@ where > + Send + Sync + Clone - + Debug + 'static, { type Context = P::Context; @@ -393,7 +392,7 @@ impl Handler for CustomDisplayFn where Context: Send + Sync + 'static, H: Handler, - F: Send + Sync + Clone + Debug + 'static, + F: Send + Sync + Clone + 'static, { type Context = H::Context; fn handle_sync( @@ -456,12 +455,7 @@ impl CliBindings for CustomDisplayFn where Context: IntoContext, H: CliBindings, - F: Fn(HandleArgs, H::Ok) -> Result<(), H::Err> - + Send - + Sync - + Clone - + Debug - + 'static, + F: Fn(HandleArgs, H::Ok) -> Result<(), H::Err> + Send + Sync + Clone + 'static, { type Context = Context; fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { diff --git a/rpc-toolkit/src/handler/mod.rs b/rpc-toolkit/src/handler/mod.rs index 36a898e..d4e58ed 100644 --- a/rpc-toolkit/src/handler/mod.rs +++ b/rpc-toolkit/src/handler/mod.rs @@ -55,7 +55,7 @@ impl HandleAnyArgs { } #[async_trait::async_trait] -pub(crate) trait HandleAny: std::fmt::Debug + Send + Sync { +pub(crate) trait HandleAny: Send + Sync { fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result; fn metadata( @@ -122,7 +122,7 @@ pub trait PrintCliResult: HandlerTypes { pub(crate) trait HandleAnyWithCli: HandleAny + CliBindingsAny {} impl HandleAnyWithCli for T {} -#[derive(Debug, Clone)] +#[derive(Clone)] #[allow(private_interfaces)] pub enum DynHandler { WithoutCli(Arc), @@ -178,7 +178,7 @@ pub trait HandlerTypes { } #[async_trait::async_trait] -pub trait Handler: HandlerTypes + std::fmt::Debug + Clone + Send + Sync + 'static { +pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { type Context: IntoContext; fn handle_sync( &self, diff --git a/rpc-toolkit/src/handler/parent.rs b/rpc-toolkit/src/handler/parent.rs index d137a94..e5b8bbb 100644 --- a/rpc-toolkit/src/handler/parent.rs +++ b/rpc-toolkit/src/handler/parent.rs @@ -67,7 +67,7 @@ impl<'a> std::borrow::Borrow> for Name { } } -#[derive(Debug, Clone)] +#[derive(Clone)] pub(crate) struct SubcommandMap(pub(crate) OrdMap, DynHandler>>); impl SubcommandMap { fn insert( @@ -125,7 +125,7 @@ impl Clone for ParentHandler { impl std::fmt::Debug for ParentHandler { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("ParentHandler") - .field(&self.subcommands) + // .field(&self.subcommands) .finish() } } From 6e1046222f8d794a8a4457d92a9976a1741d955e Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 4 Jan 2024 14:58:51 -0700 Subject: [PATCH 37/79] fix CustomDisplayFn --- rpc-toolkit/src/handler/adapters.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index b4a8924..a5659c5 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -454,19 +454,26 @@ where impl CliBindings for CustomDisplayFn where Context: IntoContext, - H: CliBindings, + H: HandlerTypes, + H::Params: FromArgMatches + CommandFactory + Serialize, F: Fn(HandleArgs, H::Ok) -> Result<(), H::Err> + Send + Sync + Clone + 'static, { type Context = Context; - fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { - self.handler.cli_command(ctx_ty) + fn cli_command(&self, _: TypeId) -> clap::Command { + H::Params::command() } fn cli_parse( &self, matches: &clap::ArgMatches, - ctx_ty: TypeId, + _: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.handler.cli_parse(matches, ctx_ty) + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) } fn cli_display( &self, From 9f7e731fd1ad307c3e67248fb3292ac1e521d236 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 4 Jan 2024 15:10:28 -0700 Subject: [PATCH 38/79] add metadata to fn async --- rpc-toolkit/src/handler/from_fn.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/rpc-toolkit/src/handler/from_fn.rs b/rpc-toolkit/src/handler/from_fn.rs index 7c151a3..b648dcf 100644 --- a/rpc-toolkit/src/handler/from_fn.rs +++ b/rpc-toolkit/src/handler/from_fn.rs @@ -83,17 +83,11 @@ pub struct FromFnAsync { function: F, metadata: OrdMap<&'static str, Value>, } -unsafe impl Send for FromFnAsync -where - F: Send, - OrdMap<&'static str, Value>: Send, -{ -} -unsafe impl Sync for FromFnAsync -where - F: Sync, - OrdMap<&'static str, Value>: Sync, -{ +impl FromFnAsync { + pub fn with_metadata(mut self, key: &'static str, value: Value) -> Self { + self.metadata.insert(key, value); + self + } } impl Clone for FromFnAsync { fn clone(&self) -> Self { From 543ce4f2c0f75dd9aff2f468f2da6ce99a43405e Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 5 Jan 2024 14:27:15 -0700 Subject: [PATCH 39/79] separate CLI traits --- rpc-toolkit/src/cli.rs | 23 +++---- rpc-toolkit/src/handler/adapters.rs | 85 ++++--------------------- rpc-toolkit/src/handler/from_fn.rs | 98 ----------------------------- rpc-toolkit/src/handler/mod.rs | 51 ++++++++++++++- 4 files changed, 68 insertions(+), 189 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index bb84a25..9067edb 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -14,7 +14,7 @@ use yajrc::{Id, RpcError}; use crate::util::{internal_error, parse_error, Flat, PhantomData}; use crate::{ AnyHandler, CliBindings, CliBindingsAny, DynHandler, HandleAny, HandleAnyArgs, HandleArgs, - Handler, HandlerTypes, IntoContext, Name, ParentHandler, + Handler, HandlerTypes, IntoContext, Name, ParentHandler, PrintCliResult, }; type GenericRpcMethod<'a> = yajrc::GenericRpcMethod<&'a str, Value, Value>; @@ -238,26 +238,17 @@ where } } } -impl CliBindings for CallRemoteHandler +impl PrintCliResult + for CallRemoteHandler where - RemoteHandler: Handler + CliBindings, + RemoteHandler: PrintCliResult, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, { type Context = Context; - fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { - self.handler.cli_command(ctx_ty) - } - fn cli_parse( - &self, - matches: &clap::ArgMatches, - ctx_ty: TypeId, - ) -> Result<(std::collections::VecDeque<&'static str>, Value), clap::Error> { - self.handler.cli_parse(matches, ctx_ty) - } - fn cli_display( + fn print( &self, HandleArgs { context, @@ -266,10 +257,10 @@ where params, inherited_params, raw_params, - }: HandleArgs, + }: HandleArgs, result: Self::Ok, ) -> Result<(), Self::Err> { - self.handler.cli_display( + self.handler.print( HandleArgs { context, parent_method, diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index a5659c5..6d711a1 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -191,33 +191,13 @@ where self.0.method_from_dots(method, ctx_ty) } } -impl CliBindings for NoDisplay +impl PrintCliResult for NoDisplay where H: HandlerTypes, H::Params: FromArgMatches + CommandFactory + Serialize, { type Context = AnyContext; - fn cli_command(&self, _: TypeId) -> clap::Command { - H::Params::command() - } - fn cli_parse( - &self, - matches: &clap::ArgMatches, - _: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - Self::Params::from_arg_matches(matches).and_then(|a| { - Ok(( - VecDeque::new(), - imbl_value::to_value(&a) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, - )) - }) - } - fn cli_display( - &self, - _: HandleArgs, - _: Self::Ok, - ) -> Result<(), Self::Err> { + fn print(&self, _: HandleArgs, _: Self::Ok) -> Result<(), Self::Err> { Ok(()) } } @@ -299,10 +279,9 @@ where self.handler.method_from_dots(method, ctx_ty) } } -impl CliBindings for CustomDisplay +impl PrintCliResult for CustomDisplay where H: HandlerTypes, - H::Params: FromArgMatches + CommandFactory + Serialize, P: PrintCliResult< Params = H::Params, InheritedParams = H::InheritedParams, @@ -314,23 +293,7 @@ where + 'static, { type Context = P::Context; - fn cli_command(&self, _: TypeId) -> clap::Command { - H::Params::command() - } - fn cli_parse( - &self, - matches: &clap::ArgMatches, - _: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - Self::Params::from_arg_matches(matches).and_then(|a| { - Ok(( - VecDeque::new(), - imbl_value::to_value(&a) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, - )) - }) - } - fn cli_display( + fn print( &self, HandleArgs { context, @@ -451,31 +414,14 @@ where self.handler.method_from_dots(method, ctx_ty) } } -impl CliBindings for CustomDisplayFn +impl PrintCliResult for CustomDisplayFn where Context: IntoContext, H: HandlerTypes, - H::Params: FromArgMatches + CommandFactory + Serialize, F: Fn(HandleArgs, H::Ok) -> Result<(), H::Err> + Send + Sync + Clone + 'static, { type Context = Context; - fn cli_command(&self, _: TypeId) -> clap::Command { - H::Params::command() - } - fn cli_parse( - &self, - matches: &clap::ArgMatches, - _: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - Self::Params::from_arg_matches(matches).and_then(|a| { - Ok(( - VecDeque::new(), - imbl_value::to_value(&a) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, - )) - }) - } - fn cli_display( + fn print( &self, HandleArgs { context, @@ -594,22 +540,13 @@ where self.handler.method_from_dots(method, ctx_ty) } } -impl CliBindings for RemoteCli +impl PrintCliResult for RemoteCli where - H: CliBindings, + Context: IntoContext, + H: PrintCliResult, { type Context = H::Context; - fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { - self.handler.cli_command(ctx_ty) - } - fn cli_parse( - &self, - matches: &clap::ArgMatches, - ctx_ty: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.handler.cli_parse(matches, ctx_ty) - } - fn cli_display( + fn print( &self, HandleArgs { context, @@ -621,7 +558,7 @@ where }: HandleArgs, result: Self::Ok, ) -> Result<(), Self::Err> { - self.handler.cli_display( + self.handler.print( HandleArgs { context, parent_method, diff --git a/rpc-toolkit/src/handler/from_fn.rs b/rpc-toolkit/src/handler/from_fn.rs index b648dcf..fc3d5c5 100644 --- a/rpc-toolkit/src/handler/from_fn.rs +++ b/rpc-toolkit/src/handler/from_fn.rs @@ -451,101 +451,3 @@ where self.metadata.clone() } } - -impl CliBindings for FromFn -where - Self: HandlerTypes, - Self::Params: FromArgMatches + CommandFactory + Serialize, - Self: PrintCliResult, -{ - type Context = ::Context; - fn cli_command(&self, _: TypeId) -> Command { - Self::Params::command() - } - fn cli_parse( - &self, - matches: &ArgMatches, - _: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - Self::Params::from_arg_matches(matches).and_then(|a| { - Ok(( - VecDeque::new(), - imbl_value::to_value(&a) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, - )) - }) - } - fn cli_display( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.print( - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }, - result, - ) - } -} - -impl CliBindings for FromFnAsync -where - Self: HandlerTypes, - Self::Params: FromArgMatches + CommandFactory + Serialize, - Self: PrintCliResult, -{ - type Context = ::Context; - fn cli_command(&self, _: TypeId) -> Command { - Self::Params::command() - } - fn cli_parse( - &self, - matches: &ArgMatches, - _: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - Self::Params::from_arg_matches(matches).and_then(|a| { - Ok(( - VecDeque::new(), - imbl_value::to_value(&a) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, - )) - }) - } - fn cli_display( - &self, - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandleArgs, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.print( - HandleArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }, - result, - ) - } -} diff --git a/rpc-toolkit/src/handler/mod.rs b/rpc-toolkit/src/handler/mod.rs index d4e58ed..6ea8c73 100644 --- a/rpc-toolkit/src/handler/mod.rs +++ b/rpc-toolkit/src/handler/mod.rs @@ -3,7 +3,7 @@ use std::collections::VecDeque; use std::ops::Deref; use std::sync::Arc; -use clap::{ArgMatches, Command, Parser}; +use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser}; use imbl_value::imbl::{OrdMap, OrdSet}; use imbl_value::Value; use serde::de::DeserializeOwned; @@ -119,6 +119,55 @@ pub trait PrintCliResult: HandlerTypes { ) -> Result<(), Self::Err>; } +impl CliBindings for T +where + T: HandlerTypes, + T::Params: CommandFactory + FromArgMatches + Serialize, + T: PrintCliResult, +{ + type Context = T::Context; + fn cli_command(&self, _: TypeId) -> clap::Command { + Self::Params::command() + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + _: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandleArgs, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.print( + HandleArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) + } +} + pub(crate) trait HandleAnyWithCli: HandleAny + CliBindingsAny {} impl HandleAnyWithCli for T {} From cf133727f1b4357a4bda775223f286ac1cc74212 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 5 Jan 2024 16:13:17 -0700 Subject: [PATCH 40/79] default context for CustomDisplayFn --- rpc-toolkit/src/handler/adapters.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index 6d711a1..0c8beab 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -30,7 +30,7 @@ pub trait HandlerExt: Handler + Sized { fn with_custom_display_fn( self, display: F, - ) -> CustomDisplayFn + ) -> CustomDisplayFn where F: Fn(HandleArgs, Self::Ok) -> Result<(), Self::Err>; fn with_inherited( @@ -66,7 +66,7 @@ impl HandlerExt for T { fn with_custom_display_fn( self, display: F, - ) -> CustomDisplayFn + ) -> CustomDisplayFn where F: Fn(HandleArgs, Self::Ok) -> Result<(), Self::Err>, { @@ -319,12 +319,12 @@ where } } -pub struct CustomDisplayFn { +pub struct CustomDisplayFn { _phantom: PhantomData, print: F, handler: H, } -impl Clone for CustomDisplayFn { +impl Clone for CustomDisplayFn { fn clone(&self) -> Self { Self { _phantom: PhantomData::new(), @@ -333,7 +333,7 @@ impl Clone for CustomDisplayFn { } } } -impl Debug for CustomDisplayFn { +impl Debug for CustomDisplayFn { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CustomDisplayFn") .field("print", &self.print) @@ -341,7 +341,7 @@ impl Debug for CustomDisplayFn { .finish() } } -impl HandlerTypes for CustomDisplayFn +impl HandlerTypes for CustomDisplayFn where H: HandlerTypes, { @@ -351,7 +351,7 @@ where type Err = H::Err; } #[async_trait::async_trait] -impl Handler for CustomDisplayFn +impl Handler for CustomDisplayFn where Context: Send + Sync + 'static, H: Handler, @@ -414,7 +414,7 @@ where self.handler.method_from_dots(method, ctx_ty) } } -impl PrintCliResult for CustomDisplayFn +impl PrintCliResult for CustomDisplayFn where Context: IntoContext, H: HandlerTypes, From 6894fb5b0781ca724e6d546a412ebb340ba5d483 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 24 Jan 2024 11:37:06 -0700 Subject: [PATCH 41/79] bump versions --- Cargo.lock | 170 ++++++++++++++++++++++++++--------------------------- 1 file changed, 85 insertions(+), 85 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab70355..66c0f6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -32,9 +32,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -103,7 +103,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", ] [[package]] @@ -114,7 +114,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", ] [[package]] @@ -125,9 +125,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "axum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d09dbe0e490df5da9d69b36dca48a76635288a82f92eca90024883a56202026d" +checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" dependencies = [ "async-trait", "axum-core", @@ -159,9 +159,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e87c8503f93e6d144ee5690907ba22db7ba79ab001a932ab99034f0fe836b3df" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" dependencies = [ "async-trait", "bytes", @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "bitflags" @@ -207,9 +207,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "bitmaps" @@ -246,9 +246,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.12" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfab8ba68f3668e89f6ff60f5b205cea56aa7b769451a59f34b8682f51c056d" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", "clap_derive", @@ -256,9 +256,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.12" +version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb7fb5e4e979aec3be7791562fcba452f94ad85e954da024396433e0e25a79e9" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstream", "anstyle", @@ -275,7 +275,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", ] [[package]] @@ -429,7 +429,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", ] [[package]] @@ -464,9 +464,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if", "libc", @@ -481,9 +481,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" [[package]] name = "h2" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -500,9 +500,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d308f63daf4181410c242d34c11f928dcb3aa105852019e043c9d1f4e4368a" +checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" dependencies = [ "bytes", "fnv", @@ -546,9 +546,9 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" [[package]] name = "http" @@ -628,7 +628,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2 0.3.22", + "h2 0.3.24", "http 0.2.11", "http-body 0.4.6", "httparse", @@ -651,7 +651,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.0", + "h2 0.4.2", "http 1.0.0", "http-body 1.0.0", "httparse", @@ -718,9 +718,9 @@ dependencies = [ [[package]] name = "imbl-sized-chunks" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6957ea0b2541c5ca561d3ef4538044af79f8a05a1eb3a3b148936aaceaa1076" +checksum = "144006fb58ed787dcae3f54575ff4349755b00ccc99f4b4873860b654be1ed63" dependencies = [ "bitmaps", ] @@ -769,9 +769,9 @@ checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" [[package]] name = "js-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cee9c64da59eae3b50095c18d3e74f8b73c0b86d2792824ff01bbce68ba229ca" +checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" dependencies = [ "wasm-bindgen", ] @@ -790,15 +790,15 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" [[package]] name = "linux-raw-sys" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" +checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "lock_api" @@ -899,11 +899,11 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.62" +version = "0.10.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671" +checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "cfg-if", "foreign-types", "libc", @@ -920,7 +920,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", ] [[package]] @@ -940,9 +940,9 @@ dependencies = [ [[package]] name = "openssl-sys" -version = "0.9.98" +version = "0.9.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7" +checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" dependencies = [ "cc", "libc", @@ -997,7 +997,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", ] [[package]] @@ -1014,15 +1014,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.28" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" [[package]] name = "proc-macro2" -version = "1.0.75" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] @@ -1071,7 +1071,7 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.3.22", + "h2 0.3.24", "http 0.2.11", "http-body 0.4.6", "hyper 0.14.28", @@ -1154,11 +1154,11 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" dependencies = [ - "bitflags 2.4.1", + "bitflags 2.4.2", "errno", "libc", "linux-raw-sys", @@ -1217,9 +1217,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.194" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773" +checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" dependencies = [ "serde_derive", ] @@ -1236,20 +1236,20 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.194" +version = "1.0.195" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0" +checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", ] [[package]] name = "serde_json" -version = "1.0.110" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fbd975230bada99c8bb618e0c365c2eefa219158d5c6c29610fd09ff1833257" +checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" dependencies = [ "itoa", "ryu", @@ -1298,9 +1298,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.2" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" [[package]] name = "socket2" @@ -1331,9 +1331,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.47" +version = "2.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1726efe18f42ae774cc644f330953a5e7b3c3003d3edcecf18850fe9d4dd9afb" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" dependencies = [ "proc-macro2", "quote", @@ -1397,7 +1397,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", ] [[package]] @@ -1442,7 +1442,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", ] [[package]] @@ -1536,9 +1536,9 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "unicode-bidi" -version = "0.3.14" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f2528f27a9eb2b21e69c95319b30bd0efd85d09c379741b0f78ea1d86be2416" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" [[package]] name = "unicode-ident" @@ -1601,9 +1601,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ed0d4f68a3015cc185aff4db9506a015f4b96f95303897bfa23f846db54064e" +checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" dependencies = [ "cfg-if", "wasm-bindgen-macro", @@ -1611,24 +1611,24 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b56f625e64f3a1084ded111c4d5f477df9f8c92df113852fa5a374dbda78826" +checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12" +checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" dependencies = [ "cfg-if", "js-sys", @@ -1638,9 +1638,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0162dbf37223cd2afce98f3d0785506dcb8d266223983e4b5b525859e6e182b2" +checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1648,28 +1648,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" +checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.89" +version = "0.2.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" +checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "web-sys" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" dependencies = [ "js-sys", "wasm-bindgen", @@ -1819,9 +1819,9 @@ dependencies = [ [[package]] name = "yajrc" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47cb33cb21fb6923a0dd074fd20dfd98fc3758103b7e2607db1354b4a86ef37c" +checksum = "ce7af47ad983c2f8357333ef87d859e66deb7eef4bf6f9e1ae7b5e99044a48bf" dependencies = [ "anyhow", "serde", @@ -1858,5 +1858,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.47", + "syn 2.0.48", ] From 779b02d9c36bd3fb51d83c64811f0ce9cdca33f2 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 24 Jan 2024 12:26:32 -0700 Subject: [PATCH 42/79] allow full args for handler fn --- rpc-toolkit/src/cli.rs | 12 +-- rpc-toolkit/src/handler/adapters.rs | 89 ++++++++--------- rpc-toolkit/src/handler/from_fn.rs | 144 ++++++++++++++++++++++++---- rpc-toolkit/src/handler/mod.rs | 39 ++++---- rpc-toolkit/src/handler/parent.rs | 16 ++-- 5 files changed, 204 insertions(+), 96 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 9067edb..fe59b82 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -13,8 +13,8 @@ use yajrc::{Id, RpcError}; use crate::util::{internal_error, parse_error, Flat, PhantomData}; use crate::{ - AnyHandler, CliBindings, CliBindingsAny, DynHandler, HandleAny, HandleAnyArgs, HandleArgs, - Handler, HandlerTypes, IntoContext, Name, ParentHandler, PrintCliResult, + AnyHandler, CliBindingsAny, DynHandler, HandleAny, HandleAnyArgs, Handler, HandlerArgs, + HandlerArgsFor, HandlerTypes, IntoContext, Name, ParentHandler, PrintCliResult, }; type GenericRpcMethod<'a> = yajrc::GenericRpcMethod<&'a str, Value, Value>; @@ -215,7 +215,7 @@ where type Context = Context; async fn handle_async( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { let full_method = handle_args .parent_method @@ -250,18 +250,18 @@ where type Context = Context; fn print( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.handler.print( - HandleArgs { + HandlerArgs { context, parent_method, method, diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index 0c8beab..9fe71ed 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -13,7 +13,8 @@ use yajrc::RpcError; use crate::util::{internal_error, parse_error, Flat, PhantomData}; use crate::{ iter_from_ctx_and_handler, AnyContext, AnyHandler, CallRemote, CliBindings, DynHandler, - EitherContext, HandleArgs, Handler, HandlerTypes, IntoContext, IntoHandlers, PrintCliResult, + EitherContext, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, IntoHandlers, + PrintCliResult, }; pub trait HandlerExt: Handler + Sized { @@ -32,7 +33,7 @@ pub trait HandlerExt: Handler + Sized { display: F, ) -> CustomDisplayFn where - F: Fn(HandleArgs, Self::Ok) -> Result<(), Self::Err>; + F: Fn(HandlerArgsFor, Self::Ok) -> Result<(), Self::Err>; fn with_inherited( self, f: F, @@ -68,7 +69,7 @@ impl HandlerExt for T { display: F, ) -> CustomDisplayFn where - F: Fn(HandleArgs, Self::Ok) -> Result<(), Self::Err>, + F: Fn(HandlerArgsFor, Self::Ok) -> Result<(), Self::Err>, { CustomDisplayFn { _phantom: PhantomData::new(), @@ -137,16 +138,16 @@ where type Context = H::Context; fn handle_sync( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { - self.0.handle_sync(HandleArgs { + self.0.handle_sync(HandlerArgs { context, parent_method, method, @@ -157,17 +158,17 @@ where } async fn handle_async( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { self.0 - .handle_async(HandleArgs { + .handle_async(HandlerArgs { context, parent_method, method, @@ -197,7 +198,7 @@ where H::Params: FromArgMatches + CommandFactory + Serialize, { type Context = AnyContext; - fn print(&self, _: HandleArgs, _: Self::Ok) -> Result<(), Self::Err> { + fn print(&self, _: HandlerArgsFor, _: Self::Ok) -> Result<(), Self::Err> { Ok(()) } } @@ -225,16 +226,16 @@ where type Context = H::Context; fn handle_sync( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { - self.handler.handle_sync(HandleArgs { + self.handler.handle_sync(HandlerArgs { context, parent_method, method, @@ -245,17 +246,17 @@ where } async fn handle_async( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { self.handler - .handle_async(HandleArgs { + .handle_async(HandlerArgs { context, parent_method, method, @@ -295,18 +296,18 @@ where type Context = P::Context; fn print( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.print.print( - HandleArgs { + HandlerArgs { context, parent_method, method, @@ -360,16 +361,16 @@ where type Context = H::Context; fn handle_sync( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { - self.handler.handle_sync(HandleArgs { + self.handler.handle_sync(HandlerArgs { context, parent_method, method, @@ -380,17 +381,17 @@ where } async fn handle_async( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { self.handler - .handle_async(HandleArgs { + .handle_async(HandlerArgs { context, parent_method, method, @@ -418,23 +419,23 @@ impl PrintCliResult for CustomDisplayFn where Context: IntoContext, H: HandlerTypes, - F: Fn(HandleArgs, H::Ok) -> Result<(), H::Err> + Send + Sync + Clone + 'static, + F: Fn(HandlerArgsFor, H::Ok) -> Result<(), H::Err> + Send + Sync + Clone + 'static, { type Context = Context; fn print( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { (self.print)( - HandleArgs { + HandlerArgs { context, parent_method, method, @@ -486,14 +487,14 @@ where type Context = EitherContext; async fn handle_async( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { match context { EitherContext::C1(context) => { @@ -514,7 +515,7 @@ where } EitherContext::C2(context) => { self.handler - .handle_async(HandleArgs { + .handle_async(HandlerArgs { context, parent_method, method, @@ -548,18 +549,18 @@ where type Context = H::Context; fn print( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.handler.print( - HandleArgs { + HandlerArgs { context, parent_method, method, @@ -620,16 +621,16 @@ where type Context = H::Context; fn handle_sync( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { - self.handler.handle_sync(HandleArgs { + self.handler.handle_sync(HandlerArgs { context, parent_method, method, @@ -640,16 +641,16 @@ where } async fn handle_async( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { - self.handler.handle_sync(HandleArgs { + self.handler.handle_sync(HandlerArgs { context, parent_method, method, @@ -694,18 +695,18 @@ where } fn cli_display( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.handler.cli_display( - HandleArgs { + HandlerArgs { context, parent_method, method, diff --git a/rpc-toolkit/src/handler/from_fn.rs b/rpc-toolkit/src/handler/from_fn.rs index fc3d5c5..a8b6777 100644 --- a/rpc-toolkit/src/handler/from_fn.rs +++ b/rpc-toolkit/src/handler/from_fn.rs @@ -2,16 +2,15 @@ use std::any::TypeId; use std::collections::VecDeque; use std::fmt::Display; -use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; use futures::Future; use imbl_value::imbl::OrdMap; use imbl_value::Value; use serde::de::DeserializeOwned; -use serde::Serialize; use crate::util::PhantomData; use crate::{ - AnyContext, CliBindings, Empty, HandleArgs, Handler, HandlerTypes, IntoContext, PrintCliResult, + AnyContext, Empty, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, + PrintCliResult, }; pub struct FromFn { @@ -49,7 +48,11 @@ where ::Ok: Display, { type Context = AnyContext; - fn print(&self, _: HandleArgs, result: Self::Ok) -> Result<(), Self::Err> { + fn print( + &self, + _: HandlerArgsFor, + result: Self::Ok, + ) -> Result<(), Self::Err> { Ok(println!("{result}")) } } @@ -109,7 +112,11 @@ where ::Ok: Display, { type Context = AnyContext; - fn print(&self, _: HandleArgs, result: Self::Ok) -> Result<(), Self::Err> { + fn print( + &self, + _: HandlerArgsFor, + result: Self::Ok, + ) -> Result<(), Self::Err> { Ok(println!("{result}")) } } @@ -125,6 +132,101 @@ where } } +impl HandlerTypes + for FromFn> +where + F: Fn(HandlerArgs) -> Result + + Send + + Sync + + Clone + + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, + Context: IntoContext, + Params: Send + Sync, + InheritedParams: Send + Sync, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = T; + type Err = E; +} +#[async_trait::async_trait] +impl Handler + for FromFn> +where + F: Fn(HandlerArgs) -> Result + + Send + + Sync + + Clone + + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, + Context: IntoContext, + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, +{ + type Context = Context; + fn handle_sync( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + (self.function)(handle_args) + } + async fn handle_async( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + if self.blocking { + self.handle_async_with_sync_blocking(handle_args).await + } else { + self.handle_async_with_sync(handle_args).await + } + } + fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} +impl HandlerTypes + for FromFnAsync> +where + F: Fn(HandlerArgs) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, + Context: IntoContext, + Params: Send + Sync, + InheritedParams: Send + Sync, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = T; + type Err = E; +} +#[async_trait::async_trait] +impl Handler + for FromFnAsync> +where + F: Fn(HandlerArgs) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, + Context: IntoContext, + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, +{ + type Context = Context; + async fn handle_async( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + (self.function)(handle_args).await + } + fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} + impl HandlerTypes for FromFn where F: Fn() -> Result + Send + Sync + Clone + 'static, @@ -144,12 +246,12 @@ where E: Send + Sync + 'static, { type Context = AnyContext; - fn handle_sync(&self, _: HandleArgs) -> Result { + fn handle_sync(&self, _: HandlerArgsFor) -> Result { (self.function)() } async fn handle_async( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { if self.blocking { self.handle_async_with_sync_blocking(handle_args).await @@ -184,7 +286,7 @@ where type Context = AnyContext; async fn handle_async( &self, - _: HandleArgs, + _: HandlerArgsFor, ) -> Result { (self.function)().await } @@ -216,13 +318,13 @@ where type Context = Context; fn handle_sync( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { (self.function)(handle_args.context) } async fn handle_async( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { if self.blocking { self.handle_async_with_sync_blocking(handle_args).await @@ -259,7 +361,7 @@ where type Context = Context; async fn handle_async( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { (self.function)(handle_args.context).await } @@ -293,16 +395,16 @@ where type Context = Context; fn handle_sync( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { - let HandleArgs { + let HandlerArgs { context, params, .. } = handle_args; (self.function)(context, params) } async fn handle_async( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { if self.blocking { self.handle_async_with_sync_blocking(handle_args).await @@ -341,9 +443,9 @@ where type Context = Context; async fn handle_async( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { - let HandleArgs { + let HandlerArgs { context, params, .. } = handle_args; (self.function)(context, params).await @@ -382,9 +484,9 @@ where type Context = Context; fn handle_sync( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { - let HandleArgs { + let HandlerArgs { context, params, inherited_params, @@ -394,7 +496,7 @@ where } async fn handle_async( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { if self.blocking { self.handle_async_with_sync_blocking(handle_args).await @@ -437,9 +539,9 @@ where type Context = Context; async fn handle_async( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { - let HandleArgs { + let HandlerArgs { context, params, inherited_params, diff --git a/rpc-toolkit/src/handler/mod.rs b/rpc-toolkit/src/handler/mod.rs index 6ea8c73..741621f 100644 --- a/rpc-toolkit/src/handler/mod.rs +++ b/rpc-toolkit/src/handler/mod.rs @@ -28,7 +28,9 @@ pub(crate) struct HandleAnyArgs { pub(crate) params: Value, } impl HandleAnyArgs { - fn downcast(self) -> Result, imbl_value::Error> + fn downcast( + self, + ) -> Result, imbl_value::Error> where H: HandlerTypes, H::Params: DeserializeOwned, @@ -40,7 +42,7 @@ impl HandleAnyArgs { method, params, } = self; - Ok(HandleArgs { + Ok(HandlerArgs { context: Context::downcast(context).map_err(|_| imbl_value::Error { kind: imbl_value::ErrorKind::Deserialization, source: serde::ser::Error::custom("context does not match expected"), @@ -105,7 +107,7 @@ pub trait CliBindings: HandlerTypes { ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; fn cli_display( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err>; } @@ -114,7 +116,7 @@ pub trait PrintCliResult: HandlerTypes { type Context: IntoContext; fn print( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err>; } @@ -144,18 +146,18 @@ where } fn cli_display( &self, - HandleArgs { + HandlerArgs { context, parent_method, method, params, inherited_params, raw_params, - }: HandleArgs, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.print( - HandleArgs { + HandlerArgs { context, parent_method, method, @@ -209,13 +211,16 @@ impl HandleAny for DynHandler { } } +pub type HandlerArgsFor = + HandlerArgs; + #[derive(Debug, Clone)] -pub struct HandleArgs { +pub struct HandlerArgs { pub context: Context, pub parent_method: VecDeque<&'static str>, pub method: VecDeque<&'static str>, - pub params: H::Params, - pub inherited_params: H::InheritedParams, + pub params: Params, + pub inherited_params: InheritedParams, pub raw_params: Value, } @@ -231,7 +236,7 @@ pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { type Context: IntoContext; fn handle_sync( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { handle_args .context @@ -240,17 +245,17 @@ pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { } async fn handle_async( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result; async fn handle_async_with_sync( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { self.handle_sync(handle_args) } async fn handle_async_with_sync_blocking( &self, - handle_args: HandleArgs, + handle_args: HandlerArgsFor, ) -> Result { let s = self.clone(); handle_args @@ -305,7 +310,7 @@ where imbl_value::to_value( &self .0 - .handle_sync(handle_args.downcast().map_err(invalid_params)?)?, + .handle_sync(handle_args.downcast::<_, H>().map_err(invalid_params)?)?, ) .map_err(internal_error) } @@ -313,7 +318,7 @@ where imbl_value::to_value( &self .0 - .handle_async(handle_args.downcast().map_err(invalid_params)?) + .handle_async(handle_args.downcast::<_, H>().map_err(invalid_params)?) .await?, ) .map_err(internal_error) @@ -351,7 +356,7 @@ where fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError> { self.0 .cli_display( - handle_args.downcast().map_err(invalid_params)?, + handle_args.downcast::<_, H>().map_err(invalid_params)?, imbl_value::from_value(result).map_err(internal_error)?, ) .map_err(RpcError::from) diff --git a/rpc-toolkit/src/handler/parent.rs b/rpc-toolkit/src/handler/parent.rs index e5b8bbb..381ca2d 100644 --- a/rpc-toolkit/src/handler/parent.rs +++ b/rpc-toolkit/src/handler/parent.rs @@ -11,8 +11,8 @@ use yajrc::RpcError; use crate::util::{combine, Flat, PhantomData}; use crate::{ - AnyContext, AnyHandler, CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, HandleArgs, - Handler, HandlerTypes, IntoContext, OrEmpty, + AnyContext, AnyHandler, CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, + HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, OrEmpty, }; pub trait IntoHandlers: HandlerTypes { @@ -178,13 +178,13 @@ where type Context = AnyContext; fn handle_sync( &self, - HandleArgs { + HandlerArgs { context, mut parent_method, mut method, raw_params, .. - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { let cmd = method.pop_front(); if let Some(cmd) = cmd { @@ -203,13 +203,13 @@ where } async fn handle_async( &self, - HandleArgs { + HandlerArgs { context, mut parent_method, mut method, raw_params, .. - }: HandleArgs, + }: HandlerArgsFor, ) -> Result { let cmd = method.pop_front(); if let Some(cmd) = cmd { @@ -322,13 +322,13 @@ where } fn cli_display( &self, - HandleArgs { + HandlerArgs { context, mut parent_method, mut method, raw_params, .. - }: HandleArgs, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { let cmd = method.pop_front(); From 4faf4b4e9595ca6f0946700bdd95c8cbf19febd7 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 24 Jan 2024 12:31:20 -0700 Subject: [PATCH 43/79] default args for HandlerArgs --- rpc-toolkit/src/handler/mod.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rpc-toolkit/src/handler/mod.rs b/rpc-toolkit/src/handler/mod.rs index 741621f..9e3ebb2 100644 --- a/rpc-toolkit/src/handler/mod.rs +++ b/rpc-toolkit/src/handler/mod.rs @@ -211,11 +211,16 @@ impl HandleAny for DynHandler { } } +#[allow(type_alias_bounds)] pub type HandlerArgsFor = HandlerArgs; #[derive(Debug, Clone)] -pub struct HandlerArgs { +pub struct HandlerArgs< + Context: IntoContext, + Params: Send + Sync = Empty, + InheritedParams: Send + Sync = Empty, +> { pub context: Context, pub parent_method: VecDeque<&'static str>, pub method: VecDeque<&'static str>, From 479a3c4f4d2bd69558ce2480760b26bf9888214d Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 24 Jan 2024 14:41:34 -0700 Subject: [PATCH 44/79] add debug info --- rpc-toolkit/src/cli.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index fe59b82..d7652a4 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -110,11 +110,13 @@ pub async fn call_remote_http( req = req.header("accept", "application/json"); body = serde_json::to_vec(&req)?; } + dbg!(); let res = req .header("content-length", body.len()) .body(body) .send() .await?; + dbg!(); match res .headers() From 4fcafa7b50a7e3a02448f3ba690b8580b91c3191 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 24 Jan 2024 14:49:25 -0700 Subject: [PATCH 45/79] more debug info --- rpc-toolkit/src/cli.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index d7652a4..8025244 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -1,6 +1,7 @@ use std::any::TypeId; use std::collections::VecDeque; use std::ffi::OsString; +use std::time::Duration; use clap::{CommandFactory, FromArgMatches}; use imbl_value::Value; @@ -110,8 +111,8 @@ pub async fn call_remote_http( req = req.header("accept", "application/json"); body = serde_json::to_vec(&req)?; } - dbg!(); - let res = req + let res = dbg!(req) + .timeout(Duration::from_secs(30)) .header("content-length", body.len()) .body(body) .send() From 5f324fb7fcdb8bf84552b0336600d06b0279a0e0 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 24 Jan 2024 14:56:54 -0700 Subject: [PATCH 46/79] disable cbor by default --- rpc-toolkit/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml index 5604917..839835f 100644 --- a/rpc-toolkit/Cargo.toml +++ b/rpc-toolkit/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/Start9Labs/rpc-toolkit" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] cbor = ["serde_cbor"] -default = ["cbor"] +default = [] [dependencies] axum = "0.7.3" From 49c69264dbbf3983e18d315e345c5f64adccc543 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 24 Jan 2024 14:59:26 -0700 Subject: [PATCH 47/79] fix --- rpc-toolkit/src/cli.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 8025244..9948240 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -109,7 +109,7 @@ pub async fn call_remote_http( { req = req.header("content-type", "application/json"); req = req.header("accept", "application/json"); - body = serde_json::to_vec(&req)?; + body = serde_json::to_vec(&rpc_req)?; } let res = dbg!(req) .timeout(Duration::from_secs(30)) From 6362a431003299d3c760f9909ca72316adcb6866 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 24 Jan 2024 15:37:49 -0700 Subject: [PATCH 48/79] remove debugging --- rpc-toolkit/src/cli.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 9948240..4b3f2b1 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -111,13 +111,12 @@ pub async fn call_remote_http( req = req.header("accept", "application/json"); body = serde_json::to_vec(&rpc_req)?; } - let res = dbg!(req) + let res = req .timeout(Duration::from_secs(30)) .header("content-length", body.len()) .body(body) .send() .await?; - dbg!(); match res .headers() From 9e989e23adb440bc72faa585b28e5aa2667a0a0d Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Wed, 24 Jan 2024 15:42:40 -0700 Subject: [PATCH 49/79] remove timeout --- rpc-toolkit/src/cli.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 4b3f2b1..cdf62d7 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -112,7 +112,6 @@ pub async fn call_remote_http( body = serde_json::to_vec(&rpc_req)?; } let res = req - .timeout(Duration::from_secs(30)) .header("content-length", body.len()) .body(body) .send() From 8d714d09a327249f16f77a8f5a160a2b7cfbf380 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 26 Jan 2024 17:22:25 -0700 Subject: [PATCH 50/79] fix sync/async mismatch --- rpc-toolkit/src/cli.rs | 13 +++++++------ rpc-toolkit/src/handler/adapters.rs | 18 ++++++++++-------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index cdf62d7..cf050df 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -5,6 +5,7 @@ use std::time::Duration; use clap::{CommandFactory, FromArgMatches}; use imbl_value::Value; +use reqwest::header::{ACCEPT, CONTENT_LENGTH, CONTENT_TYPE}; use reqwest::{Client, Method}; use serde::de::DeserializeOwned; use serde::Serialize; @@ -101,25 +102,25 @@ pub async fn call_remote_http( let body; #[cfg(feature = "cbor")] { - req = req.header("content-type", "application/cbor"); - req = req.header("accept", "application/cbor, application/json"); + req = req.header(CONTENT_TYPE, "application/cbor"); + req = req.header(ACCEPT, "application/cbor, application/json"); body = serde_cbor::to_vec(&rpc_req)?; } #[cfg(not(feature = "cbor"))] { - req = req.header("content-type", "application/json"); - req = req.header("accept", "application/json"); + req = req.header(CONTENT_TYPE, "application/json"); + req = req.header(ACCEPT, "application/json"); body = serde_json::to_vec(&rpc_req)?; } let res = req - .header("content-length", body.len()) + .header(CONTENT_LENGTH, body.len()) .body(body) .send() .await?; match res .headers() - .get("content-type") + .get(CONTENT_TYPE) .and_then(|v| v.to_str().ok()) { Some("application/json") => { diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index 9fe71ed..7cf1fb1 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -650,14 +650,16 @@ where raw_params, }: HandlerArgsFor, ) -> Result { - self.handler.handle_sync(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), - raw_params, - }) + self.handler + .handle_async(HandlerArgs { + context, + parent_method, + method, + params, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, + }) + .await } fn metadata( &self, From 40d9f406d03500cc3308d2e1d45abe46db8e62bb Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Sat, 17 Feb 2024 16:14:23 -0700 Subject: [PATCH 51/79] pass through raw params --- rpc-toolkit/src/command.rs | 382 ---------------------------- rpc-toolkit/src/handler/adapters.rs | 3 +- 2 files changed, 1 insertion(+), 384 deletions(-) delete mode 100644 rpc-toolkit/src/command.rs diff --git a/rpc-toolkit/src/command.rs b/rpc-toolkit/src/command.rs deleted file mode 100644 index 8ad8645..0000000 --- a/rpc-toolkit/src/command.rs +++ /dev/null @@ -1,382 +0,0 @@ -use std::sync::Arc; - -use clap::{ArgMatches, CommandFactory, FromArgMatches}; -use futures::future::BoxFuture; -use futures::FutureExt; -use imbl_value::Value; -use serde::de::DeserializeOwned; -use serde::ser::Serialize; -use yajrc::RpcError; - -use crate::util::{extract, Flat, PhantomData}; - -/// Stores a command's implementation for a given context -/// Can be created from anything that implements ParentCommand, AsyncCommand, or SyncCommand -pub struct DynCommand { - pub(crate) name: &'static str, - pub(crate) metadata: Context::Metadata, - pub(crate) implementation: Option>, - pub(crate) cli: Option>, - pub(crate) subcommands: Vec, -} - -pub(crate) struct Implementation { - pub(crate) async_impl: Arc< - dyn Fn(Context, Vec<&'static str>, Value) -> BoxFuture<'static, Result> - + Send - + Sync, - >, - pub(crate) sync_impl: - Box, Value) -> Result + Send + Sync>, -} - -pub(crate) struct CliBindings { - pub(crate) cmd: clap::Command, - pub(crate) parser: Box Fn(&'a ArgMatches) -> Result + Send + Sync>, - pub(crate) display: Option< - Box< - dyn Fn(Context, Vec<&'static str>, Value, Value) -> Result<(), imbl_value::Error> - + Send - + Sync, - >, - >, -} -impl CliBindings { - pub(crate) fn from_parent() -> Self { - Self { - cmd: Cmd::command(), - parser: Box::new(|matches| { - imbl_value::to_value(&Cmd::from_arg_matches(matches).map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INVALID_PARAMS_ERROR - })?) - .map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INVALID_PARAMS_ERROR - }) - }), - display: None, - } - } - fn from_leaf>() -> Self - { - Self { - display: Some(Box::new(|ctx, parent_method, params, res| { - let parent_params = imbl_value::from_value(params.clone())?; - Ok(imbl_value::from_value::(params)?.display( - ctx, - ParentInfo { - method: parent_method, - params: parent_params, - }, - imbl_value::from_value(res)?, - )) - })), - ..Self::from_parent::() - } - } -} - -/// Must be implemented for all commands -/// Use `Parent = NoParent` if the implementation requires no arguments from the parent command -pub trait Command: DeserializeOwned + Sized + Send { - const NAME: &'static str; - type Parent: Command; -} - -/// Includes the parent method, and the arguments requested from the parent -/// Arguments are flattened out in the params object, so ensure that there are no collisions between the names of the arguments for your method and its parents -pub struct ParentInfo { - pub method: Vec<&'static str>, - pub params: T, -} - -/// This is automatically generated from a command based on its Parents. -/// It can be used to generate a proof that one of the parents contains the necessary arguments that a subcommand requires. -pub struct ParentChain(PhantomData); -pub struct Contains(PhantomData); -impl Contains { - pub fn none() -> Self { - Self(PhantomData) - } -} -impl From<(Contains, Contains)> for Contains> { - fn from(_: (Contains, Contains)) -> Self { - Self(PhantomData) - } -} - -/// Use this as a Parent if your command does not require any arguments from its parents -#[derive(serde::Deserialize, serde::Serialize)] -pub struct NoParent {} -impl Command for NoParent { - const NAME: &'static str = ""; - type Parent = NoParent; -} -impl ParentChain -where - Cmd: Command, -{ - pub fn child(&self) -> Contains { - Contains(PhantomData) - } - pub fn parent(&self) -> ParentChain { - ParentChain(PhantomData) - } -} - -/// Implement this for a command that has no implementation, but simply exists to organize subcommands -pub trait ParentCommand: Command { - fn metadata() -> Context::Metadata { - Context::Metadata::default() - } - fn subcommands(chain: ParentChain) -> Vec>; -} -impl DynCommand { - pub fn from_parent< - Cmd: ParentCommand + FromArgMatches + CommandFactory + Serialize, - >( - contains: Contains, - ) -> Self { - drop(contains); - Self { - name: Cmd::NAME, - metadata: Cmd::metadata(), - implementation: None, - cli: Some(CliBindings::from_parent::()), - subcommands: Cmd::subcommands(ParentChain::(PhantomData)), - } - } - pub fn from_parent_no_cli>( - contains: Contains, - ) -> Self { - drop(contains); - Self { - name: Cmd::NAME, - metadata: Cmd::metadata(), - implementation: None, - cli: None, - subcommands: Cmd::subcommands(ParentChain::(PhantomData)), - } - } -} - -/// Implement this for any command with an implementation -pub trait LeafCommand: Command { - type Ok: DeserializeOwned + Serialize + Send; - type Err: From + Into + Send; - fn metadata() -> Context::Metadata { - Context::Metadata::default() - } - fn display(self, ctx: Context, parent: ParentInfo, res: Self::Ok); - fn subcommands(chain: ParentChain) -> Vec> { - drop(chain); - Vec::new() - } -} - -/// Implement this if your Command's implementation is async -#[async_trait::async_trait] -pub trait AsyncCommand: LeafCommand { - async fn implementation( - self, - ctx: Context, - parent: ParentInfo, - ) -> Result; -} -impl Implementation { - fn for_async>(contains: Contains) -> Self { - drop(contains); - Self { - async_impl: Arc::new(|ctx, parent_method, params| { - async move { - let parent_params = extract::(¶ms)?; - imbl_value::to_value( - &extract::(¶ms)? - .implementation( - ctx, - ParentInfo { - method: parent_method, - params: parent_params, - }, - ) - .await - .map_err(|e| e.into())?, - ) - .map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::PARSE_ERROR - }) - } - .boxed() - }), - sync_impl: Box::new(|ctx, parent_method, params| { - let parent_params = extract::(¶ms)?; - imbl_value::to_value( - &ctx.runtime() - .block_on( - extract::(¶ms) - .map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INVALID_PARAMS_ERROR - })? - .implementation( - ctx, - ParentInfo { - method: parent_method, - params: parent_params, - }, - ), - ) - .map_err(|e| e.into())?, - ) - .map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::PARSE_ERROR - }) - }), - } - } -} -impl DynCommand { - pub fn from_async + FromArgMatches + CommandFactory + Serialize>( - contains: Contains, - ) -> Self { - Self { - name: Cmd::NAME, - metadata: Cmd::metadata(), - implementation: Some(Implementation::for_async::(contains)), - cli: Some(CliBindings::from_leaf::()), - subcommands: Cmd::subcommands(ParentChain::(PhantomData)), - } - } - pub fn from_async_no_cli>(contains: Contains) -> Self { - Self { - name: Cmd::NAME, - metadata: Cmd::metadata(), - implementation: Some(Implementation::for_async::(contains)), - cli: None, - subcommands: Cmd::subcommands(ParentChain::(PhantomData)), - } - } -} - -/// Implement this if your Command's implementation is not async -pub trait SyncCommand: LeafCommand { - const BLOCKING: bool; - fn implementation( - self, - ctx: Context, - parent: ParentInfo, - ) -> Result; -} -impl Implementation { - fn for_sync>(contains: Contains) -> Self { - drop(contains); - Self { - async_impl: if Cmd::BLOCKING { - Arc::new(|ctx, parent_method, params| { - tokio::task::spawn_blocking(move || { - let parent_params = extract::(¶ms)?; - imbl_value::to_value( - &extract::(¶ms) - .map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INVALID_PARAMS_ERROR - })? - .implementation( - ctx, - ParentInfo { - method: parent_method, - params: parent_params, - }, - ) - .map_err(|e| e.into())?, - ) - .map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::PARSE_ERROR - }) - }) - .map(|f| { - f.map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INTERNAL_ERROR - })? - }) - .boxed() - }) - } else { - Arc::new(|ctx, parent_method, params| { - async move { - let parent_params = extract::(¶ms)?; - imbl_value::to_value( - &extract::(¶ms) - .map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INVALID_PARAMS_ERROR - })? - .implementation( - ctx, - ParentInfo { - method: parent_method, - params: parent_params, - }, - ) - .map_err(|e| e.into())?, - ) - .map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::PARSE_ERROR - }) - } - .boxed() - }) - }, - sync_impl: Box::new(|ctx, method, params| { - let parent = extract::(¶ms)?; - imbl_value::to_value( - &extract::(¶ms) - .map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INVALID_PARAMS_ERROR - })? - .implementation( - ctx, - ParentInfo { - method, - params: parent, - }, - ) - .map_err(|e| e.into())?, - ) - .map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::PARSE_ERROR - }) - }), - } - } -} -impl DynCommand { - pub fn from_sync + FromArgMatches + CommandFactory + Serialize>( - contains: Contains, - ) -> Self { - Self { - name: Cmd::NAME, - metadata: Cmd::metadata(), - implementation: Some(Implementation::for_sync::(contains)), - cli: Some(CliBindings::from_leaf::()), - subcommands: Cmd::subcommands(ParentChain::(PhantomData)), - } - } - pub fn from_sync_no_cli>(contains: Contains) -> Self { - Self { - name: Cmd::NAME, - metadata: Cmd::metadata(), - implementation: Some(Implementation::for_sync::(contains)), - cli: None, - subcommands: Cmd::subcommands(ParentChain::(PhantomData)), - } - } -} diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index 7cf1fb1..7f48550 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -502,8 +502,7 @@ where match context .call_remote( &full_method.join("."), - imbl_value::to_value(&Flat(params, inherited_params)) - .map_err(parse_error)?, + imbl_value::to_value(&raw_params).map_err(parse_error)?, ) .await { From f7fe1078cd98f00d1b4c9ae722efdb7938b9871c Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Sat, 17 Feb 2024 16:37:43 -0700 Subject: [PATCH 52/79] clone, dont serialize --- rpc-toolkit/src/cli.rs | 9 ++------- rpc-toolkit/src/handler/adapters.rs | 5 +---- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index cf050df..4581bef 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -1,7 +1,6 @@ use std::any::TypeId; use std::collections::VecDeque; use std::ffi::OsString; -use std::time::Duration; use clap::{CommandFactory, FromArgMatches}; use imbl_value::Value; @@ -13,7 +12,7 @@ use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader use url::Url; use yajrc::{Id, RpcError}; -use crate::util::{internal_error, parse_error, Flat, PhantomData}; +use crate::util::{internal_error, parse_error, PhantomData}; use crate::{ AnyHandler, CliBindingsAny, DynHandler, HandleAny, HandleAnyArgs, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, Name, ParentHandler, PrintCliResult, @@ -226,11 +225,7 @@ where .collect::>(); match handle_args .context - .call_remote( - &full_method.join("."), - imbl_value::to_value(&Flat(handle_args.params, handle_args.inherited_params)) - .map_err(parse_error)?, - ) + .call_remote(&full_method.join("."), handle_args.raw_params.clone()) .await { Ok(a) => imbl_value::from_value(a) diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index 7f48550..2b05cd5 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -500,10 +500,7 @@ where EitherContext::C1(context) => { let full_method = parent_method.into_iter().chain(method).collect::>(); match context - .call_remote( - &full_method.join("."), - imbl_value::to_value(&raw_params).map_err(parse_error)?, - ) + .call_remote(&full_method.join("."), raw_params.clone()) .await { Ok(a) => imbl_value::from_value(a) From 85144f1f70d3f0be48cd22ceef31df27fe154c03 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Sat, 17 Feb 2024 18:24:32 -0700 Subject: [PATCH 53/79] thread through inherited params --- rpc-toolkit/src/cli.rs | 4 +++- rpc-toolkit/src/handler/mod.rs | 4 +++- rpc-toolkit/src/handler/parent.rs | 10 ++++++++-- rpc-toolkit/src/server/mod.rs | 3 ++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 4581bef..dd98ca9 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -3,7 +3,7 @@ use std::collections::VecDeque; use std::ffi::OsString; use clap::{CommandFactory, FromArgMatches}; -use imbl_value::Value; +use imbl_value::{InOMap, Value}; use reqwest::header::{ACCEPT, CONTENT_LENGTH, CONTENT_TYPE}; use reqwest::{Client, Method}; use serde::de::DeserializeOwned; @@ -67,6 +67,7 @@ impl parent_method: VecDeque::new(), method: method.clone(), params: params.clone(), + inherited: Value::Object(InOMap::new()), })?; root_handler.cli_display( HandleAnyArgs { @@ -74,6 +75,7 @@ impl parent_method: VecDeque::new(), method, params, + inherited: Value::Object(InOMap::new()), }, res, )?; diff --git a/rpc-toolkit/src/handler/mod.rs b/rpc-toolkit/src/handler/mod.rs index 9e3ebb2..51e5a66 100644 --- a/rpc-toolkit/src/handler/mod.rs +++ b/rpc-toolkit/src/handler/mod.rs @@ -26,6 +26,7 @@ pub(crate) struct HandleAnyArgs { pub(crate) parent_method: VecDeque<&'static str>, pub(crate) method: VecDeque<&'static str>, pub(crate) params: Value, + pub(crate) inherited: Value, } impl HandleAnyArgs { fn downcast( @@ -41,6 +42,7 @@ impl HandleAnyArgs { parent_method, method, params, + inherited, } = self; Ok(HandlerArgs { context: Context::downcast(context).map_err(|_| imbl_value::Error { @@ -50,7 +52,7 @@ impl HandleAnyArgs { parent_method, method, params: imbl_value::from_value(params.clone())?, - inherited_params: imbl_value::from_value(params.clone())?, + inherited_params: imbl_value::from_value(inherited.clone())?, raw_params: params, }) } diff --git a/rpc-toolkit/src/handler/parent.rs b/rpc-toolkit/src/handler/parent.rs index 381ca2d..04fc49d 100644 --- a/rpc-toolkit/src/handler/parent.rs +++ b/rpc-toolkit/src/handler/parent.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; use imbl_value::imbl::{OrdMap, OrdSet}; -use imbl_value::Value; +use imbl_value::{to_value, Value}; use serde::de::DeserializeOwned; use serde::Serialize; use yajrc::RpcError; @@ -172,7 +172,7 @@ where #[async_trait::async_trait] impl Handler for ParentHandler where - Params: Serialize + Send + Sync + 'static, + Params: Send + Sync + 'static, InheritedParams: Serialize + Send + Sync + 'static, { type Context = AnyContext; @@ -182,6 +182,7 @@ where context, mut parent_method, mut method, + inherited_params, raw_params, .. }: HandlerArgsFor, @@ -196,6 +197,7 @@ where parent_method, method, params: raw_params, + inherited: to_value(&inherited_params)?, }) } else { Err(yajrc::METHOD_NOT_FOUND_ERROR) @@ -207,6 +209,7 @@ where context, mut parent_method, mut method, + inherited_params, raw_params, .. }: HandlerArgsFor, @@ -222,6 +225,7 @@ where parent_method, method, params: raw_params, + inherited: to_value(&inherited_params)?, }) .await } else { @@ -326,6 +330,7 @@ where context, mut parent_method, mut method, + inherited_params, raw_params, .. }: HandlerArgsFor, @@ -344,6 +349,7 @@ where parent_method, method, params: raw_params, + inherited: to_value(&inherited_params)?, }, result, ) diff --git a/rpc-toolkit/src/server/mod.rs b/rpc-toolkit/src/server/mod.rs index 91c7fcb..4dbf590 100644 --- a/rpc-toolkit/src/server/mod.rs +++ b/rpc-toolkit/src/server/mod.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use futures::future::{join_all, BoxFuture}; use futures::{Future, FutureExt, Stream, StreamExt}; -use imbl_value::Value; +use imbl_value::{InOMap, Value}; use yajrc::{RpcError, RpcMethod}; use crate::util::{invalid_request, JobRunner}; @@ -66,6 +66,7 @@ impl Server { parent_method: VecDeque::new(), method: method.ok_or_else(|| yajrc::METHOD_NOT_FOUND_ERROR)?, params, + inherited: Value::Object(InOMap::new()), }) .await } From c89e0abdb15dd3bed9adb5339cf0b61a96f32b50 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Sat, 17 Feb 2024 19:25:13 -0700 Subject: [PATCH 54/79] support inheritance mapping --- rpc-toolkit/src/cli.rs | 4 +- rpc-toolkit/src/handler/adapters.rs | 16 +++-- rpc-toolkit/src/handler/from_fn.rs | 8 +-- rpc-toolkit/src/handler/mod.rs | 102 ++++++++++++++++++++-------- rpc-toolkit/src/handler/parent.rs | 68 +++++++++++-------- rpc-toolkit/src/server/mod.rs | 4 +- rpc-toolkit/tests/handler.rs | 4 +- 7 files changed, 134 insertions(+), 72 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index dd98ca9..6ba2d37 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -67,7 +67,7 @@ impl parent_method: VecDeque::new(), method: method.clone(), params: params.clone(), - inherited: Value::Object(InOMap::new()), + inherited: crate::Empty {}, })?; root_handler.cli_display( HandleAnyArgs { @@ -75,7 +75,7 @@ impl parent_method: VecDeque::new(), method, params, - inherited: Value::Object(InOMap::new()), + inherited: crate::Empty {}, }, res, )?; diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index 2b05cd5..a4ea662 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -10,11 +10,11 @@ use serde::de::DeserializeOwned; use serde::Serialize; use yajrc::RpcError; -use crate::util::{internal_error, parse_error, Flat, PhantomData}; +use crate::util::{internal_error, Flat, PhantomData}; use crate::{ iter_from_ctx_and_handler, AnyContext, AnyHandler, CallRemote, CliBindings, DynHandler, EitherContext, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, IntoHandlers, - PrintCliResult, + OrEmpty, PrintCliResult, }; pub trait HandlerExt: Handler + Sized { @@ -106,18 +106,22 @@ impl HandlerTypes for NoCli { type Ok = H::Ok; type Err = H::Err; } -impl IntoHandlers for NoCli +impl IntoHandlers> for NoCli where H: Handler, H::Params: DeserializeOwned, - H::InheritedParams: DeserializeOwned, + H::InheritedParams: OrEmpty>, H::Ok: Serialize + DeserializeOwned, RpcError: From, + A: Send + Sync + 'static, + B: Send + Sync + 'static, { - fn into_handlers(self) -> impl IntoIterator, DynHandler)> { + fn into_handlers(self) -> impl IntoIterator, DynHandler>)> { iter_from_ctx_and_handler( self.0.contexts(), - DynHandler::WithoutCli(Arc::new(AnyHandler::new(self.0))), + DynHandler::WithoutCli(Arc::new(AnyHandler::new( + self.0.with_inherited(|a, b| OrEmpty::from_t(Flat(a, b))), + ))), ) } } diff --git a/rpc-toolkit/src/handler/from_fn.rs b/rpc-toolkit/src/handler/from_fn.rs index a8b6777..02144b8 100644 --- a/rpc-toolkit/src/handler/from_fn.rs +++ b/rpc-toolkit/src/handler/from_fn.rs @@ -461,7 +461,7 @@ where Context: IntoContext, F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, Params: DeserializeOwned + Send + Sync + 'static, - InheritedParams: DeserializeOwned + Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { @@ -477,7 +477,7 @@ where Context: IntoContext, F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, Params: DeserializeOwned + Send + Sync + 'static, - InheritedParams: DeserializeOwned + Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { @@ -515,7 +515,7 @@ where F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, Params: DeserializeOwned + Send + Sync + 'static, - InheritedParams: DeserializeOwned + Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { @@ -532,7 +532,7 @@ where F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, Params: DeserializeOwned + Send + Sync + 'static, - InheritedParams: DeserializeOwned + Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { diff --git a/rpc-toolkit/src/handler/mod.rs b/rpc-toolkit/src/handler/mod.rs index 51e5a66..1a5a643 100644 --- a/rpc-toolkit/src/handler/mod.rs +++ b/rpc-toolkit/src/handler/mod.rs @@ -21,21 +21,20 @@ pub use adapters::*; pub use from_fn::*; pub use parent::*; -pub(crate) struct HandleAnyArgs { +pub(crate) struct HandleAnyArgs { pub(crate) context: AnyContext, pub(crate) parent_method: VecDeque<&'static str>, pub(crate) method: VecDeque<&'static str>, pub(crate) params: Value, - pub(crate) inherited: Value, + pub(crate) inherited: Inherited, } -impl HandleAnyArgs { +impl HandleAnyArgs { fn downcast( self, ) -> Result, imbl_value::Error> where - H: HandlerTypes, + H: HandlerTypes, H::Params: DeserializeOwned, - H::InheritedParams: DeserializeOwned, { let Self { context, @@ -52,7 +51,7 @@ impl HandleAnyArgs { parent_method, method, params: imbl_value::from_value(params.clone())?, - inherited_params: imbl_value::from_value(inherited.clone())?, + inherited_params: inherited, raw_params: params, }) } @@ -60,8 +59,12 @@ impl HandleAnyArgs { #[async_trait::async_trait] pub(crate) trait HandleAny: Send + Sync { - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; - async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result; + type Inherited: Send; + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; + async fn handle_async( + &self, + handle_args: HandleAnyArgs, + ) -> Result; fn metadata( &self, method: VecDeque<&'static str>, @@ -71,10 +74,14 @@ pub(crate) trait HandleAny: Send + Sync { } #[async_trait::async_trait] impl HandleAny for Arc { - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + type Inherited = T::Inherited; + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { self.deref().handle_sync(handle_args) } - async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { + async fn handle_async( + &self, + handle_args: HandleAnyArgs, + ) -> Result { self.deref().handle_async(handle_args).await } fn metadata( @@ -90,13 +97,18 @@ impl HandleAny for Arc { } pub(crate) trait CliBindingsAny { + type Inherited; fn cli_command(&self, ctx_ty: TypeId) -> Command; fn cli_parse( &self, matches: &ArgMatches, ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; - fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError>; + fn cli_display( + &self, + handle_args: HandleAnyArgs, + result: Value, + ) -> Result<(), RpcError>; } pub trait CliBindings: HandlerTypes { @@ -172,24 +184,41 @@ where } } -pub(crate) trait HandleAnyWithCli: HandleAny + CliBindingsAny {} -impl HandleAnyWithCli for T {} +pub(crate) trait HandleAnyWithCli: + HandleAny + CliBindingsAny +{ +} +impl + CliBindingsAny> + HandleAnyWithCli for T +{ +} -#[derive(Clone)] #[allow(private_interfaces)] -pub enum DynHandler { - WithoutCli(Arc), - WithCli(Arc), +pub enum DynHandler { + WithoutCli(Arc>), + WithCli(Arc>), +} +impl Clone for DynHandler { + fn clone(&self) -> Self { + match self { + Self::WithCli(a) => Self::WithCli(a.clone()), + Self::WithoutCli(a) => Self::WithoutCli(a.clone()), + } + } } #[async_trait::async_trait] -impl HandleAny for DynHandler { - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { +impl HandleAny for DynHandler { + type Inherited = Inherited; + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { match self { DynHandler::WithoutCli(h) => h.handle_sync(handle_args), DynHandler::WithCli(h) => h.handle_sync(handle_args), } } - async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { + async fn handle_async( + &self, + handle_args: HandleAnyArgs, + ) -> Result { match self { DynHandler::WithoutCli(h) => h.handle_async(handle_args).await, DynHandler::WithCli(h) => h.handle_async(handle_args).await, @@ -309,11 +338,11 @@ impl std::fmt::Debug for AnyHandler { impl HandleAny for AnyHandler where H::Params: DeserializeOwned, - H::InheritedParams: DeserializeOwned, H::Ok: Serialize, RpcError: From, { - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + type Inherited = H::InheritedParams; + fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { imbl_value::to_value( &self .0 @@ -321,7 +350,10 @@ where ) .map_err(internal_error) } - async fn handle_async(&self, handle_args: HandleAnyArgs) -> Result { + async fn handle_async( + &self, + handle_args: HandleAnyArgs, + ) -> Result { imbl_value::to_value( &self .0 @@ -346,10 +378,10 @@ impl CliBindingsAny for AnyHandler where H: CliBindings, H::Params: DeserializeOwned, - H::InheritedParams: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, { + type Inherited = H::InheritedParams; fn cli_command(&self, ctx_ty: TypeId) -> Command { self.0.cli_command(ctx_ty) } @@ -360,7 +392,11 @@ where ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { self.0.cli_parse(matches, ctx_ty) } - fn cli_display(&self, handle_args: HandleAnyArgs, result: Value) -> Result<(), RpcError> { + fn cli_display( + &self, + handle_args: HandleAnyArgs, + result: Value, + ) -> Result<(), RpcError> { self.0 .cli_display( handle_args.downcast::<_, H>().map_err(invalid_params)?, @@ -373,9 +409,19 @@ where #[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] pub struct Empty {} -pub(crate) trait OrEmpty {} -impl OrEmpty for T {} -impl OrEmpty> for Empty {} +pub(crate) trait OrEmpty { + fn from_t(t: T) -> Self; +} +impl OrEmpty for T { + fn from_t(t: T) -> Self { + t + } +} +impl OrEmpty> for Empty { + fn from_t(t: Flat) -> Self { + Empty {} + } +} #[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] pub enum Never {} diff --git a/rpc-toolkit/src/handler/parent.rs b/rpc-toolkit/src/handler/parent.rs index 04fc49d..0b61eab 100644 --- a/rpc-toolkit/src/handler/parent.rs +++ b/rpc-toolkit/src/handler/parent.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; use imbl_value::imbl::{OrdMap, OrdSet}; -use imbl_value::{to_value, Value}; +use imbl_value::Value; use serde::de::DeserializeOwned; use serde::Serialize; use yajrc::RpcError; @@ -12,33 +12,37 @@ use yajrc::RpcError; use crate::util::{combine, Flat, PhantomData}; use crate::{ AnyContext, AnyHandler, CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, - HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, OrEmpty, + HandlerArgs, HandlerArgsFor, HandlerExt, HandlerTypes, IntoContext, OrEmpty, }; -pub trait IntoHandlers: HandlerTypes { - fn into_handlers(self) -> impl IntoIterator, DynHandler)>; +pub trait IntoHandlers: HandlerTypes { + fn into_handlers(self) -> impl IntoIterator, DynHandler)>; } -impl IntoHandlers for H +impl IntoHandlers> for H where H: Handler + CliBindings, H::Params: DeserializeOwned, - H::InheritedParams: DeserializeOwned, + H::InheritedParams: OrEmpty>, H::Ok: Serialize + DeserializeOwned, RpcError: From, + A: Send + Sync + 'static, + B: Send + Sync + 'static, { - fn into_handlers(self) -> impl IntoIterator, DynHandler)> { + fn into_handlers(self) -> impl IntoIterator, DynHandler>)> { iter_from_ctx_and_handler( intersect_type_ids(self.contexts(), ::Context::type_ids()), - DynHandler::WithCli(Arc::new(AnyHandler::new(self))), + DynHandler::WithCli(Arc::new(AnyHandler::new( + self.with_inherited(|a, b| OrEmpty::from_t(Flat(a, b))), + ))), ) } } -pub(crate) fn iter_from_ctx_and_handler( +pub(crate) fn iter_from_ctx_and_handler( ctx: Option>, - handler: DynHandler, -) -> impl IntoIterator, DynHandler)> { + handler: DynHandler, +) -> impl IntoIterator, DynHandler)> { if let Some(ctx) = ctx { itertools::Either::Left(ctx.into_iter().map(Some)) } else { @@ -67,13 +71,19 @@ impl<'a> std::borrow::Borrow> for Name { } } -#[derive(Clone)] -pub(crate) struct SubcommandMap(pub(crate) OrdMap, DynHandler>>); -impl SubcommandMap { +pub(crate) struct SubcommandMap( + pub(crate) OrdMap, DynHandler>>, +); +impl Clone for SubcommandMap { + fn clone(&self) -> Self { + Self(self.0.clone()) + } +} +impl SubcommandMap { fn insert( &mut self, name: Option<&'static str>, - handlers: impl IntoIterator, DynHandler)>, + handlers: impl IntoIterator, DynHandler)>, ) { let mut for_name = self.0.remove(&name).unwrap_or_default(); for (ctx_ty, handler) in handlers { @@ -82,7 +92,11 @@ impl SubcommandMap { self.0.insert(Name(name), for_name); } - fn get<'a>(&'a self, ctx_ty: TypeId, name: Option<&str>) -> Option<(Name, &'a DynHandler)> { + fn get<'a>( + &'a self, + ctx_ty: TypeId, + name: Option<&str>, + ) -> Option<(Name, &'a DynHandler)> { if let Some((name, for_name)) = self.0.get_key_value(&name) { if let Some(for_ctx) = for_name.get(&Some(ctx_ty)) { Some((*name, for_ctx)) @@ -96,8 +110,8 @@ impl SubcommandMap { } pub struct ParentHandler { - _phantom: PhantomData<(Params, InheritedParams)>, - pub(crate) subcommands: SubcommandMap, + _phantom: PhantomData, + pub(crate) subcommands: SubcommandMap>, metadata: OrdMap<&'static str, Value>, } impl ParentHandler { @@ -141,8 +155,7 @@ impl ParentHandler { #[allow(private_bounds)] pub fn subcommand(mut self, name: &'static str, handler: H) -> Self where - H: IntoHandlers, - H::InheritedParams: OrEmpty>, + H: IntoHandlers>, { self.subcommands .insert(name.into(), handler.into_handlers()); @@ -151,8 +164,7 @@ impl ParentHandler { #[allow(private_bounds)] pub fn root_handler(mut self, handler: H) -> Self where - H: IntoHandlers, - H::InheritedParams: OrEmpty>, + H: IntoHandlers>, { self.subcommands.insert(None, handler.into_handlers()); self @@ -182,9 +194,9 @@ where context, mut parent_method, mut method, + params, inherited_params, raw_params, - .. }: HandlerArgsFor, ) -> Result { let cmd = method.pop_front(); @@ -197,7 +209,7 @@ where parent_method, method, params: raw_params, - inherited: to_value(&inherited_params)?, + inherited: Flat(params, inherited_params), }) } else { Err(yajrc::METHOD_NOT_FOUND_ERROR) @@ -209,9 +221,9 @@ where context, mut parent_method, mut method, + params, inherited_params, raw_params, - .. }: HandlerArgsFor, ) -> Result { let cmd = method.pop_front(); @@ -225,7 +237,7 @@ where parent_method, method, params: raw_params, - inherited: to_value(&inherited_params)?, + inherited: Flat(params, inherited_params), }) .await } else { @@ -330,9 +342,9 @@ where context, mut parent_method, mut method, + params, inherited_params, raw_params, - .. }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { @@ -349,7 +361,7 @@ where parent_method, method, params: raw_params, - inherited: to_value(&inherited_params)?, + inherited: Flat(params, inherited_params), }, result, ) diff --git a/rpc-toolkit/src/server/mod.rs b/rpc-toolkit/src/server/mod.rs index 4dbf590..3d86e02 100644 --- a/rpc-toolkit/src/server/mod.rs +++ b/rpc-toolkit/src/server/mod.rs @@ -8,7 +8,7 @@ use imbl_value::{InOMap, Value}; use yajrc::{RpcError, RpcMethod}; use crate::util::{invalid_request, JobRunner}; -use crate::{AnyHandler, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; +use crate::{AnyHandler, Empty, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; pub type GenericRpcMethod = yajrc::GenericRpcMethod; pub type RpcRequest = yajrc::RpcRequest; @@ -66,7 +66,7 @@ impl Server { parent_method: VecDeque::new(), method: method.ok_or_else(|| yajrc::METHOD_NOT_FOUND_ERROR)?, params, - inherited: Value::Object(InOMap::new()), + inherited: crate::Empty {}, }) .await } diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index cc6e203..c07e0c9 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -159,7 +159,7 @@ fn make_api() -> ParentHandler { .subcommand( "fizz", ParentHandler::::new().root_handler( - from_fn(|c: CliContext, _, InheritParams { donde }| { + from_fn(|c: CliContext, _: Empty, InheritParams { donde }| { Ok::<_, RpcError>( format!( "Root Command: Host {host} Donde = {donde}", @@ -174,7 +174,7 @@ fn make_api() -> ParentHandler { .subcommand( "error", ParentHandler::::new().root_handler( - from_fn(|c: CliContext, _, InheritParams { donde }| { + from_fn(|c: CliContext, _: Empty, InheritParams { donde }| { Err::(RpcError { code: 1, message: "This is an example message".into(), From 3bc2c84c6f5545790c42a6d2f05e737a9fa3af0f Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 25 Apr 2024 11:55:12 -0600 Subject: [PATCH 55/79] change CallRemote to be based on remote context --- rpc-toolkit/src/cli.rs | 9 +- rpc-toolkit/src/handler/adapters.rs | 22 ++-- rpc-toolkit/tests/compat.rs | 169 ---------------------------- rpc-toolkit/tests/handler.rs | 15 ++- 4 files changed, 23 insertions(+), 192 deletions(-) delete mode 100644 rpc-toolkit/tests/compat.rs diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 6ba2d37..7ba0cac 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -84,7 +84,7 @@ impl } #[async_trait::async_trait] -pub trait CallRemote: crate::Context { +pub trait CallRemote: crate::Context { async fn call_remote(&self, method: &str, params: Value) -> Result; } @@ -207,8 +207,9 @@ where type Err = RemoteHandler::Err; } #[async_trait::async_trait] -impl Handler for CallRemoteHandler +impl Handler for CallRemoteHandler where + Context: CallRemote, RemoteHandler: Handler, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, @@ -237,9 +238,9 @@ where } } } -impl PrintCliResult - for CallRemoteHandler +impl PrintCliResult for CallRemoteHandler where + Context: CallRemote, RemoteHandler: PrintCliResult, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index a4ea662..fc40baa 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -40,7 +40,7 @@ pub trait HandlerExt: Handler + Sized { ) -> InheritanceHandler where F: Fn(Params, InheritedParams) -> Self::InheritedParams; - fn with_remote_cli(self) -> RemoteCli; + fn with_call_remote(self) -> RemoteCaller; } impl HandlerExt for T { @@ -90,8 +90,8 @@ impl HandlerExt for T { inherit: f, } } - fn with_remote_cli(self) -> RemoteCli { - RemoteCli { + fn with_call_remote(self) -> RemoteCaller { + RemoteCaller { _phantom: PhantomData::new(), handler: self, } @@ -452,11 +452,11 @@ where } } -pub struct RemoteCli { +pub struct RemoteCaller { _phantom: PhantomData, handler: H, } -impl Clone for RemoteCli { +impl Clone for RemoteCaller { fn clone(&self) -> Self { Self { _phantom: PhantomData::new(), @@ -464,12 +464,12 @@ impl Clone for RemoteCli { } } } -impl Debug for RemoteCli { +impl Debug for RemoteCaller { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("RemoteCli").field(&self.handler).finish() + f.debug_tuple("RemoteCaller").field(&self.handler).finish() } } -impl HandlerTypes for RemoteCli +impl HandlerTypes for RemoteCaller where H: HandlerTypes, { @@ -479,9 +479,9 @@ where type Err = H::Err; } #[async_trait::async_trait] -impl Handler for RemoteCli +impl Handler for RemoteCaller where - Context: CallRemote, + Context: CallRemote, H: Handler, H::Params: Serialize, H::InheritedParams: Serialize, @@ -541,7 +541,7 @@ where self.handler.method_from_dots(method, ctx_ty) } } -impl PrintCliResult for RemoteCli +impl PrintCliResult for RemoteCaller where Context: IntoContext, H: PrintCliResult, diff --git a/rpc-toolkit/tests/compat.rs b/rpc-toolkit/tests/compat.rs deleted file mode 100644 index cd97121..0000000 --- a/rpc-toolkit/tests/compat.rs +++ /dev/null @@ -1,169 +0,0 @@ -use std::fmt::Display; -use std::str::FromStr; -use std::sync::Arc; - -use futures::FutureExt; -use hyper::Request; -use rpc_toolkit::clap::Arg; -use rpc_toolkit::hyper::http::Error as HttpError; -use rpc_toolkit::hyper::Response; -use rpc_toolkit::serde::{Deserialize, Serialize}; -use rpc_toolkit::url::Host; -use rpc_toolkit::yajrc::RpcError; -use rpc_toolkit::{command, Context}; - -#[derive(Debug, Clone)] -pub struct AppState(Arc); -impl From for () { - fn from(_: AppState) -> Self { - () - } -} - -#[derive(Debug)] -pub struct ConfigSeed { - host: Host, - port: u16, -} - -impl Context for AppState {} - -#[command( - about = "Does the thing", - subcommands("dothething2::", self(dothething_impl(async))) -)] -async fn dothething( - #[context] _ctx: AppState, - #[arg(short = 'a')] arg1: Option, - #[arg(short = 'b', default)] val: String, - #[arg(short = 'c', help = "I am the flag `c`!", default)] arg3: bool, - #[arg(stdin)] structured: U, -) -> Result<(Option, String, bool, U), RpcError> -where - U: Serialize + for<'a> Deserialize<'a> + FromStr + Clone + 'static, - U::Err: Display, -{ - Ok((arg1, val, arg3, structured)) -} - -async fn dothething_impl( - ctx: AppState, - parent_data: (Option, String, bool, U), -) -> Result { - Ok(format!( - "{:?}, {:?}, {}, {}, {}", - ctx, - parent_data.0, - parent_data.1, - parent_data.2, - serde_json::to_string_pretty(&parent_data.3)? - )) -} - -#[command(about = "Does the thing")] -fn dothething2( - #[parent_data] parent_data: (Option, String, bool, U), - #[arg(stdin)] structured2: U, -) -> Result -where - U: Serialize + for<'a> Deserialize<'a> + FromStr + Clone + 'static, - U::Err: Display, -{ - Ok(format!( - "{:?}, {}, {}, {}, {}", - parent_data.0, - parent_data.1, - parent_data.2, - serde_json::to_string_pretty(&parent_data.3)?, - serde_json::to_string_pretty(&structured2)?, - )) -} - -// #[tokio::test] -// async fn test_rpc() { -// use tokio::io::AsyncWriteExt; - -// let seed = Arc::new(ConfigSeed { -// host: Host::parse("localhost").unwrap(), -// port: 8000, -// }); -// let server = rpc_server!({ -// command: dothething::, -// context: AppState(seed), -// middleware: [ -// cors, -// ], -// }); -// let handle = tokio::spawn(server); -// let mut cmd = tokio::process::Command::new("cargo") -// .arg("test") -// .arg("--package") -// .arg("rpc-toolkit") -// .arg("--test") -// .arg("test") -// .arg("--") -// .arg("cli_test") -// .arg("--exact") -// .arg("--nocapture") -// .arg("--") -// // .arg("-b") -// // .arg("test") -// .arg("dothething2") -// .stdin(std::process::Stdio::piped()) -// .stdout(std::process::Stdio::piped()) -// .spawn() -// .unwrap(); -// cmd.stdin -// .take() -// .unwrap() -// .write_all(b"TEST\nHAHA") -// .await -// .unwrap(); -// let out = cmd.wait_with_output().await.unwrap(); -// assert!(out.status.success()); -// assert!(dbg!(std::str::from_utf8(&out.stdout).unwrap()) -// .contains("\nNone, test, false, \"TEST\", \"HAHA\"\n")); -// handle.abort(); -// } - -// #[test] -// fn cli_test() { -// let app = dothething::build_app(); -// let mut skip = true; -// let args = std::iter::once(std::ffi::OsString::from("cli_test")) -// .chain(std::env::args_os().into_iter().skip_while(|a| { -// if a == "--" { -// skip = false; -// return true; -// } -// skip -// })) -// .collect::>(); -// if skip { -// return; -// } -// let matches = app.get_matches_from(args); -// let seed = Arc::new(ConfigSeed { -// host: Host::parse("localhost").unwrap(), -// port: 8000, -// }); -// dothething::cli_handler::(AppState(seed), (), None, &matches, "".into(), ()) -// .unwrap(); -// } - -// #[test] -// #[ignore] -// fn cli_example() { -// run_cli! ({ -// command: dothething::, -// app: app => app -// .arg(Arg::with_name("host").long("host").short('h').takes_value(true)) -// .arg(Arg::with_name("port").long("port").short('p').takes_value(true)), -// context: matches => AppState(Arc::new(ConfigSeed { -// host: Host::parse(matches.value_of("host").unwrap_or("localhost")).unwrap(), -// port: matches.value_of("port").unwrap_or("8000").parse().unwrap(), -// })) -// }) -// } - -// //////////////////////////////////////////////// diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index c07e0c9..380294e 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -1,4 +1,3 @@ -use std::any::TypeId; use std::ffi::OsString; use std::fmt::Display; use std::path::{Path, PathBuf}; @@ -62,7 +61,7 @@ impl Context for CliContext { } } #[async_trait::async_trait] -impl CallRemote for CliContext { +impl CallRemote for CliContext { async fn call_remote(&self, method: &str, params: Value) -> Result { call_remote_socket( tokio::net::UnixStream::connect(&self.0.host).await.unwrap(), @@ -130,7 +129,7 @@ fn make_api() -> ParentHandler { )) }, ) - .with_remote_cli::(), + .with_call_remote::(), ) .subcommand( "hello", @@ -174,7 +173,7 @@ fn make_api() -> ParentHandler { .subcommand( "error", ParentHandler::::new().root_handler( - from_fn(|c: CliContext, _: Empty, InheritParams { donde }| { + from_fn(|_: CliContext, _: Empty, InheritParams { .. }| { Err::(RpcError { code: 1, message: "This is an example message".into(), @@ -199,14 +198,14 @@ fn test_cli() { make_cli() .run( ["test-cli", "hello", "me"] - .into_iter() + .iter() .map(|s| OsString::from(s)), ) .unwrap(); make_cli() .run( ["test-cli", "fizz", "buzz"] - .into_iter() + .iter() .map(|s| OsString::from(s)), ) .unwrap(); @@ -230,7 +229,7 @@ async fn test_server() { "echo", "foo", ] - .into_iter() + .iter() .map(|s| OsString::from(s)), ) .unwrap(); @@ -242,7 +241,7 @@ async fn test_server() { "echo", "bar", ] - .into_iter() + .iter() .map(|s| OsString::from(s)), ) .unwrap(); From 94cf1ff5bf5bf0b29e1891fc70b9ca7ec0834173 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 25 Apr 2024 12:13:26 -0600 Subject: [PATCH 56/79] remove async_trait where possible --- rpc-toolkit/src/cli.rs | 10 +++++--- rpc-toolkit/src/handler/adapters.rs | 10 ++++---- rpc-toolkit/src/handler/from_fn.rs | 20 +++++++-------- rpc-toolkit/src/handler/mod.rs | 36 ++++++++++++++------------- rpc-toolkit/src/handler/parent.rs | 2 +- rpc-toolkit/src/server/http.rs | 38 +++++++++++++++++++---------- rpc-toolkit/tests/handler.rs | 2 +- rpc-toolkit/tests/test.rs | 6 ++--- 8 files changed, 71 insertions(+), 53 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 7ba0cac..11f9a9c 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -3,6 +3,7 @@ use std::collections::VecDeque; use std::ffi::OsString; use clap::{CommandFactory, FromArgMatches}; +use futures::Future; use imbl_value::{InOMap, Value}; use reqwest::header::{ACCEPT, CONTENT_LENGTH, CONTENT_TYPE}; use reqwest::{Client, Method}; @@ -83,9 +84,12 @@ impl } } -#[async_trait::async_trait] pub trait CallRemote: crate::Context { - async fn call_remote(&self, method: &str, params: Value) -> Result; + fn call_remote( + &self, + method: &str, + params: Value, + ) -> impl Future> + Send; } pub async fn call_remote_http( @@ -206,7 +210,7 @@ where type Ok = RemoteHandler::Ok; type Err = RemoteHandler::Err; } -#[async_trait::async_trait] + impl Handler for CallRemoteHandler where Context: CallRemote, diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index fc40baa..7d9ad75 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -134,7 +134,7 @@ impl HandlerTypes for NoDisplay { type Ok = H::Ok; type Err = H::Err; } -#[async_trait::async_trait] + impl Handler for NoDisplay where H: Handler, @@ -221,7 +221,7 @@ where type Ok = H::Ok; type Err = H::Err; } -#[async_trait::async_trait] + impl Handler for CustomDisplay where H: Handler, @@ -355,7 +355,7 @@ where type Ok = H::Ok; type Err = H::Err; } -#[async_trait::async_trait] + impl Handler for CustomDisplayFn where Context: Send + Sync + 'static, @@ -478,7 +478,7 @@ where type Ok = H::Ok; type Err = H::Err; } -#[async_trait::async_trait] + impl Handler for RemoteCaller where Context: CallRemote, @@ -610,7 +610,7 @@ where type Ok = H::Ok; type Err = H::Err; } -#[async_trait::async_trait] + impl Handler for InheritanceHandler where Params: Send + Sync + 'static, diff --git a/rpc-toolkit/src/handler/from_fn.rs b/rpc-toolkit/src/handler/from_fn.rs index 02144b8..c77a0f2 100644 --- a/rpc-toolkit/src/handler/from_fn.rs +++ b/rpc-toolkit/src/handler/from_fn.rs @@ -151,7 +151,7 @@ where type Ok = T; type Err = E; } -#[async_trait::async_trait] + impl Handler for FromFn> where @@ -203,7 +203,7 @@ where type Ok = T; type Err = E; } -#[async_trait::async_trait] + impl Handler for FromFnAsync> where @@ -238,7 +238,7 @@ where type Ok = T; type Err = E; } -#[async_trait::async_trait] + impl Handler for FromFn where F: Fn() -> Result + Send + Sync + Clone + 'static, @@ -275,7 +275,7 @@ where type Ok = T; type Err = E; } -#[async_trait::async_trait] + impl Handler for FromFnAsync where F: Fn() -> Fut + Send + Sync + Clone + 'static, @@ -307,7 +307,7 @@ where type Ok = T; type Err = E; } -#[async_trait::async_trait] + impl Handler for FromFn where Context: IntoContext, @@ -349,7 +349,7 @@ where type Ok = T; type Err = E; } -#[async_trait::async_trait] + impl Handler for FromFnAsync where Context: IntoContext, @@ -383,7 +383,7 @@ where type Ok = T; type Err = E; } -#[async_trait::async_trait] + impl Handler for FromFn where Context: IntoContext, @@ -430,7 +430,7 @@ where type Ok = T; type Err = E; } -#[async_trait::async_trait] + impl Handler for FromFnAsync where Context: IntoContext, @@ -470,7 +470,7 @@ where type Ok = T; type Err = E; } -#[async_trait::async_trait] + impl Handler for FromFn where @@ -524,7 +524,7 @@ where type Ok = T; type Err = E; } -#[async_trait::async_trait] + impl Handler for FromFnAsync where diff --git a/rpc-toolkit/src/handler/mod.rs b/rpc-toolkit/src/handler/mod.rs index 1a5a643..31d9587 100644 --- a/rpc-toolkit/src/handler/mod.rs +++ b/rpc-toolkit/src/handler/mod.rs @@ -4,6 +4,7 @@ use std::ops::Deref; use std::sync::Arc; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser}; +use futures::Future; use imbl_value::imbl::{OrdMap, OrdSet}; use imbl_value::Value; use serde::de::DeserializeOwned; @@ -267,7 +268,6 @@ pub trait HandlerTypes { type Err: Send + Sync; } -#[async_trait::async_trait] pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { type Context: IntoContext; fn handle_sync( @@ -279,27 +279,29 @@ pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { .runtime() .block_on(self.handle_async(handle_args)) } - async fn handle_async( + fn handle_async( &self, handle_args: HandlerArgsFor, - ) -> Result; - async fn handle_async_with_sync( - &self, + ) -> impl Future> + Send; + fn handle_async_with_sync<'a>( + &'a self, handle_args: HandlerArgsFor, - ) -> Result { - self.handle_sync(handle_args) + ) -> impl Future> + Send + 'a { + async move { self.handle_sync(handle_args) } } - async fn handle_async_with_sync_blocking( - &self, + fn handle_async_with_sync_blocking<'a>( + &'a self, handle_args: HandlerArgsFor, - ) -> Result { - let s = self.clone(); - handle_args - .context - .runtime() - .spawn_blocking(move || s.handle_sync(handle_args)) - .await - .unwrap() + ) -> impl Future> + Send + 'a { + async move { + let s = self.clone(); + handle_args + .context + .runtime() + .spawn_blocking(move || s.handle_sync(handle_args)) + .await + .unwrap() + } } #[allow(unused_variables)] fn metadata( diff --git a/rpc-toolkit/src/handler/parent.rs b/rpc-toolkit/src/handler/parent.rs index 0b61eab..9220d15 100644 --- a/rpc-toolkit/src/handler/parent.rs +++ b/rpc-toolkit/src/handler/parent.rs @@ -181,7 +181,7 @@ where type Ok = Value; type Err = RpcError; } -#[async_trait::async_trait] + impl Handler for ParentHandler where Params: Send + Sync + 'static, diff --git a/rpc-toolkit/src/server/http.rs b/rpc-toolkit/src/server/http.rs index f433825..22cebb1 100644 --- a/rpc-toolkit/src/server/http.rs +++ b/rpc-toolkit/src/server/http.rs @@ -5,7 +5,7 @@ use axum::extract::Request; use axum::handler::Handler; use axum::response::Response; use futures::future::{join_all, BoxFuture}; -use futures::FutureExt; +use futures::{Future, FutureExt}; use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; use http_body_util::BodyExt; use imbl_value::imbl::Vector; @@ -40,30 +40,41 @@ pub fn json_http_response(t: &T) -> Response { .unwrap_or_else(|_| fallback_rpc_error_response()) } -#[async_trait::async_trait] pub trait Middleware: Clone + Send + Sync + 'static { type Metadata: DeserializeOwned + Send + 'static; #[allow(unused_variables)] - async fn process_http_request( + fn process_http_request( &mut self, context: &Context, request: &mut Request, - ) -> Result<(), Response> { - Ok(()) + ) -> impl Future> + Send { + async { Ok(()) } } #[allow(unused_variables)] - async fn process_rpc_request( + fn process_rpc_request( &mut self, context: &Context, metadata: Self::Metadata, request: &mut RpcRequest, - ) -> Result<(), RpcResponse> { - Ok(()) + ) -> impl Future> + Send { + async { Ok(()) } } #[allow(unused_variables)] - async fn process_rpc_response(&mut self, context: &Context, response: &mut RpcResponse) {} + fn process_rpc_response( + &mut self, + context: &Context, + response: &mut RpcResponse, + ) -> impl Future + Send { + async { () } + } #[allow(unused_variables)] - async fn process_http_response(&mut self, context: &Context, response: &mut Response) {} + fn process_http_response( + &mut self, + context: &Context, + response: &mut Response, + ) -> impl Future + Send { + async { () } + } } #[allow(private_bounds)] @@ -101,7 +112,7 @@ impl + Send + Sync> _Middleware< context: &'a Context, request: &'a mut Request, ) -> BoxFuture<'a, Result<(), Response>> { - >::process_http_request(self, context, request) + >::process_http_request(self, context, request).boxed() } fn process_rpc_request<'a>( &'a mut self, @@ -118,20 +129,21 @@ impl + Send + Sync> _Middleware< }, request, ) + .boxed() } fn process_rpc_response<'a>( &'a mut self, context: &'a Context, response: &'a mut RpcResponse, ) -> BoxFuture<'a, ()> { - >::process_rpc_response(self, context, response) + >::process_rpc_response(self, context, response).boxed() } fn process_http_response<'a>( &'a mut self, context: &'a Context, response: &'a mut Response, ) -> BoxFuture<'a, ()> { - >::process_http_response(self, context, response) + >::process_http_response(self, context, response).boxed() } } diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index 380294e..ea599c5 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -60,7 +60,7 @@ impl Context for CliContext { self.0.rt.get().unwrap().handle().clone() } } -#[async_trait::async_trait] + impl CallRemote for CliContext { async fn call_remote(&self, method: &str, params: Value) -> Result { call_remote_socket( diff --git a/rpc-toolkit/tests/test.rs b/rpc-toolkit/tests/test.rs index bc555b0..5ef6b81 100644 --- a/rpc-toolkit/tests/test.rs +++ b/rpc-toolkit/tests/test.rs @@ -19,14 +19,14 @@ // impl Context for CliContext { // type Metadata = (); // } -// #[async_trait::async_trait] +// // impl CliContextSocket for CliContext { // type Stream = UnixStream; // async fn connect(&self) -> std::io::Result { // UnixStream::connect(&self.0).await // } // } -// #[async_trait::async_trait] +// // impl rpc_toolkit::CliContext for CliContext { // async fn call_remote( // &self, @@ -92,7 +92,7 @@ // println!("{}", res); // } // } -// #[async_trait::async_trait] +// // impl AsyncCommand for Thing1 { // async fn implementation( // self, From 94da122d6cb94260b4049bfade94b200095123ea Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 2 May 2024 17:30:57 -0600 Subject: [PATCH 57/79] require empty params for root handler --- rpc-toolkit/src/handler/parent.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rpc-toolkit/src/handler/parent.rs b/rpc-toolkit/src/handler/parent.rs index 9220d15..09d79b1 100644 --- a/rpc-toolkit/src/handler/parent.rs +++ b/rpc-toolkit/src/handler/parent.rs @@ -164,7 +164,7 @@ impl ParentHandler { #[allow(private_bounds)] pub fn root_handler(mut self, handler: H) -> Self where - H: IntoHandlers>, + H: IntoHandlers> + HandlerTypes, { self.subcommands.insert(None, handler.into_handlers()); self From 320d8323596d4050ab1047123fec26a621743394 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 13:06:30 -0600 Subject: [PATCH 58/79] add "Extra" to call_remote --- rpc-toolkit/src/cli.rs | 13 ++++-- rpc-toolkit/src/handler/adapters.rs | 38 ++++++++++-------- rpc-toolkit/src/util.rs | 62 +++++++++++++++++++++++++++-- rpc-toolkit/tests/handler.rs | 4 +- 4 files changed, 91 insertions(+), 26 deletions(-) diff --git a/rpc-toolkit/src/cli.rs b/rpc-toolkit/src/cli.rs index 11f9a9c..9e0c993 100644 --- a/rpc-toolkit/src/cli.rs +++ b/rpc-toolkit/src/cli.rs @@ -15,7 +15,7 @@ use yajrc::{Id, RpcError}; use crate::util::{internal_error, parse_error, PhantomData}; use crate::{ - AnyHandler, CliBindingsAny, DynHandler, HandleAny, HandleAnyArgs, Handler, HandlerArgs, + AnyHandler, CliBindingsAny, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, Name, ParentHandler, PrintCliResult, }; @@ -84,11 +84,12 @@ impl } } -pub trait CallRemote: crate::Context { +pub trait CallRemote: crate::Context { fn call_remote( &self, method: &str, params: Value, + extra: Extra, ) -> impl Future> + Send; } @@ -171,7 +172,7 @@ pub async fn call_remote_socket( .result } -pub struct CallRemoteHandler { +struct CallRemoteHandler { _phantom: PhantomData, handler: RemoteHandler, } @@ -232,7 +233,11 @@ where .collect::>(); match handle_args .context - .call_remote(&full_method.join("."), handle_args.raw_params.clone()) + .call_remote( + &full_method.join("."), + handle_args.raw_params.clone(), + Empty {}, + ) .await { Ok(a) => imbl_value::from_value(a) diff --git a/rpc-toolkit/src/handler/adapters.rs b/rpc-toolkit/src/handler/adapters.rs index 7d9ad75..c5cdf8d 100644 --- a/rpc-toolkit/src/handler/adapters.rs +++ b/rpc-toolkit/src/handler/adapters.rs @@ -10,11 +10,11 @@ use serde::de::DeserializeOwned; use serde::Serialize; use yajrc::RpcError; -use crate::util::{internal_error, Flat, PhantomData}; +use crate::util::{internal_error, invalid_params, without, Flat, PhantomData}; use crate::{ iter_from_ctx_and_handler, AnyContext, AnyHandler, CallRemote, CliBindings, DynHandler, - EitherContext, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, IntoHandlers, - OrEmpty, PrintCliResult, + EitherContext, Empty, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, + IntoHandlers, OrEmpty, PrintCliResult, }; pub trait HandlerExt: Handler + Sized { @@ -40,7 +40,7 @@ pub trait HandlerExt: Handler + Sized { ) -> InheritanceHandler where F: Fn(Params, InheritedParams) -> Self::InheritedParams; - fn with_call_remote(self) -> RemoteCaller; + fn with_call_remote(self) -> RemoteCaller; } impl HandlerExt for T { @@ -90,7 +90,7 @@ impl HandlerExt for T { inherit: f, } } - fn with_call_remote(self) -> RemoteCaller { + fn with_call_remote(self) -> RemoteCaller { RemoteCaller { _phantom: PhantomData::new(), handler: self, @@ -452,11 +452,11 @@ where } } -pub struct RemoteCaller { - _phantom: PhantomData, +pub struct RemoteCaller { + _phantom: PhantomData<(Context, Extra)>, handler: H, } -impl Clone for RemoteCaller { +impl Clone for RemoteCaller { fn clone(&self) -> Self { Self { _phantom: PhantomData::new(), @@ -464,29 +464,31 @@ impl Clone for RemoteCaller { } } } -impl Debug for RemoteCaller { +impl Debug for RemoteCaller { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("RemoteCaller").field(&self.handler).finish() } } -impl HandlerTypes for RemoteCaller +impl HandlerTypes for RemoteCaller where H: HandlerTypes, + Extra: Send + Sync + 'static, { - type Params = H::Params; + type Params = Flat; type InheritedParams = H::InheritedParams; type Ok = H::Ok; type Err = H::Err; } -impl Handler for RemoteCaller +impl Handler for RemoteCaller where - Context: CallRemote, + Context: CallRemote, H: Handler, H::Params: Serialize, H::InheritedParams: Serialize, H::Ok: DeserializeOwned, H::Err: From, + Extra: Serialize + Send + Sync + 'static, { type Context = EitherContext; async fn handle_async( @@ -495,7 +497,7 @@ where context, parent_method, method, - params, + params: Flat(params, extra), inherited_params, raw_params, }: HandlerArgsFor, @@ -504,7 +506,11 @@ where EitherContext::C1(context) => { let full_method = parent_method.into_iter().chain(method).collect::>(); match context - .call_remote(&full_method.join("."), raw_params.clone()) + .call_remote( + &full_method.join("."), + without(raw_params, &extra).map_err(invalid_params)?, + extra, + ) .await { Ok(a) => imbl_value::from_value(a) @@ -553,7 +559,7 @@ where context, parent_method, method, - params, + params: Flat(params, _), inherited_params, raw_params, }: HandlerArgsFor, diff --git a/rpc-toolkit/src/util.rs b/rpc-toolkit/src/util.rs index b457e58..c7b71b2 100644 --- a/rpc-toolkit/src/util.rs +++ b/rpc-toolkit/src/util.rs @@ -10,10 +10,21 @@ use serde::{Deserialize, Serialize}; use yajrc::RpcError; pub fn extract(value: &Value) -> Result { - imbl_value::from_value(value.clone()).map_err(|e| RpcError { - data: Some(e.to_string().into()), - ..yajrc::INVALID_PARAMS_ERROR - }) + imbl_value::from_value(value.clone()).map_err(invalid_params) +} + +pub fn without(value: Value, remove: &T) -> Result { + let to_remove = imbl_value::to_value(remove)?; + let (Value::Object(mut value), Value::Object(to_remove)) = (value, to_remove) else { + return Err(imbl_value::Error { + kind: imbl_value::ErrorKind::Serialization, + source: serde_json::Error::custom("params must be object"), + }); + }; + for k in to_remove.keys() { + value.remove(k); + } + Ok(Value::Object(value)) } pub fn combine(v1: Value, v2: Value) -> Result { @@ -103,6 +114,49 @@ where .serialize(serializer) } } +impl clap::CommandFactory for Flat +where + A: clap::CommandFactory, + B: clap::Args, +{ + fn command() -> clap::Command { + B::augment_args(A::command()) + } + fn command_for_update() -> clap::Command { + B::augment_args_for_update(A::command_for_update()) + } +} +impl clap::FromArgMatches for Flat +where + A: clap::FromArgMatches, + B: clap::FromArgMatches, +{ + fn from_arg_matches(matches: &clap::ArgMatches) -> Result { + Ok(Self( + A::from_arg_matches(matches)?, + B::from_arg_matches(matches)?, + )) + } + fn from_arg_matches_mut(matches: &mut clap::ArgMatches) -> Result { + Ok(Self( + A::from_arg_matches_mut(matches)?, + B::from_arg_matches_mut(matches)?, + )) + } + fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> { + self.0.update_from_arg_matches(matches)?; + self.1.update_from_arg_matches(matches)?; + Ok(()) + } + fn update_from_arg_matches_mut( + &mut self, + matches: &mut clap::ArgMatches, + ) -> Result<(), clap::Error> { + self.0.update_from_arg_matches_mut(matches)?; + self.1.update_from_arg_matches_mut(matches)?; + Ok(()) + } +} pub fn poll_select_all<'a, T>( futs: &mut Vec>, diff --git a/rpc-toolkit/tests/handler.rs b/rpc-toolkit/tests/handler.rs index ea599c5..5eb3929 100644 --- a/rpc-toolkit/tests/handler.rs +++ b/rpc-toolkit/tests/handler.rs @@ -62,7 +62,7 @@ impl Context for CliContext { } impl CallRemote for CliContext { - async fn call_remote(&self, method: &str, params: Value) -> Result { + async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result { call_remote_socket( tokio::net::UnixStream::connect(&self.0.host).await.unwrap(), method, @@ -129,7 +129,7 @@ fn make_api() -> ParentHandler { )) }, ) - .with_call_remote::(), + .with_call_remote::(), ) .subcommand( "hello", From 5374aef88d8df7d6b5cb5608aec2c8902dbde117 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 13:15:49 -0600 Subject: [PATCH 59/79] un-workspace --- .gitignore | 1 + Cargo.toml | 44 +- rpc-toolkit-macro-internals/.gitignore | 2 - rpc-toolkit-macro-internals/Cargo.toml | 14 - .../src/command/build.rs | 1511 ----------------- .../src/command/mod.rs | 102 -- .../src/command/parse.rs | 851 ---------- rpc-toolkit-macro-internals/src/lib.rs | 12 - rpc-toolkit-macro/.gitignore | 2 - rpc-toolkit-macro/Cargo.toml | 17 - rpc-toolkit-macro/src/lib.rs | 44 - rpc-toolkit/.gitignore | 2 - rpc-toolkit/Cargo.toml | 42 - {rpc-toolkit/src => src}/cli.rs | 0 {rpc-toolkit/src => src}/command_helpers.rs | 0 {rpc-toolkit/src => src}/context.rs | 0 {rpc-toolkit/src => src}/handler/adapters.rs | 0 {rpc-toolkit/src => src}/handler/from_fn.rs | 0 {rpc-toolkit/src => src}/handler/mod.rs | 0 {rpc-toolkit/src => src}/handler/parent.rs | 0 {rpc-toolkit/src => src}/lib.rs | 0 {rpc-toolkit/src => src}/metadata.rs | 0 {rpc-toolkit/src => src}/server/http.rs | 0 {rpc-toolkit/src => src}/server/mod.rs | 0 {rpc-toolkit/src => src}/server/socket.rs | 0 {rpc-toolkit/src => src}/util.rs | 0 {rpc-toolkit/tests => tests}/handler.rs | 0 {rpc-toolkit/tests => tests}/test.rs | 0 28 files changed, 43 insertions(+), 2601 deletions(-) delete mode 100644 rpc-toolkit-macro-internals/.gitignore delete mode 100644 rpc-toolkit-macro-internals/Cargo.toml delete mode 100644 rpc-toolkit-macro-internals/src/command/build.rs delete mode 100644 rpc-toolkit-macro-internals/src/command/mod.rs delete mode 100644 rpc-toolkit-macro-internals/src/command/parse.rs delete mode 100644 rpc-toolkit-macro-internals/src/lib.rs delete mode 100644 rpc-toolkit-macro/.gitignore delete mode 100644 rpc-toolkit-macro/Cargo.toml delete mode 100644 rpc-toolkit-macro/src/lib.rs delete mode 100644 rpc-toolkit/.gitignore delete mode 100644 rpc-toolkit/Cargo.toml rename {rpc-toolkit/src => src}/cli.rs (100%) rename {rpc-toolkit/src => src}/command_helpers.rs (100%) rename {rpc-toolkit/src => src}/context.rs (100%) rename {rpc-toolkit/src => src}/handler/adapters.rs (100%) rename {rpc-toolkit/src => src}/handler/from_fn.rs (100%) rename {rpc-toolkit/src => src}/handler/mod.rs (100%) rename {rpc-toolkit/src => src}/handler/parent.rs (100%) rename {rpc-toolkit/src => src}/lib.rs (100%) rename {rpc-toolkit/src => src}/metadata.rs (100%) rename {rpc-toolkit/src => src}/server/http.rs (100%) rename {rpc-toolkit/src => src}/server/mod.rs (100%) rename {rpc-toolkit/src => src}/server/socket.rs (100%) rename {rpc-toolkit/src => src}/util.rs (100%) rename {rpc-toolkit/tests => tests}/handler.rs (100%) rename {rpc-toolkit/tests => tests}/test.rs (100%) diff --git a/.gitignore b/.gitignore index 2f7896d..948e252 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target/ +rpc.sock \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 333618a..839835f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,42 @@ -[workspace] -members = ["rpc-toolkit", "rpc-toolkit-macro", "rpc-toolkit-macro-internals"] +[package] +authors = ["Aiden McClelland "] +edition = "2018" +name = "rpc-toolkit" +version = "0.2.3" +description = "A toolkit for creating JSON-RPC 2.0 servers with automatic cli bindings" +license = "MIT" +documentation = "https://docs.rs/rpc-toolkit" +keywords = ["json", "rpc", "cli"] +repository = "https://github.com/Start9Labs/rpc-toolkit" + + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +cbor = ["serde_cbor"] +default = [] + +[dependencies] +axum = "0.7.3" +async-stream = "0.3" +async-trait = "0.1" +clap = { version = "4", features = ["derive"] } +futures = "0.3" +http = "1" +http-body-util = "0.1" +# hyper = { version = "1", features = ["server", "http1", "http2", "client"] } +itertools = "0.12" +imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } +lazy_format = "2" +lazy_static = "1.4" +openssl = { version = "0.10", features = ["vendored"] } +pin-project = "1" +reqwest = { version = "0.11" } +rpc-toolkit-macro = { version = "0.2.2", path = "../rpc-toolkit-macro" } +serde = { version = "1.0", features = ["derive"] } +serde_cbor = { version = "0.11", optional = true } +serde_json = "1.0" +thiserror = "1.0" +tokio = { version = "1", features = ["full"] } +tokio-stream = { version = "0.1", features = ["io-util", "net"] } +url = "2" +yajrc = "0.1" diff --git a/rpc-toolkit-macro-internals/.gitignore b/rpc-toolkit-macro-internals/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/rpc-toolkit-macro-internals/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/rpc-toolkit-macro-internals/Cargo.toml b/rpc-toolkit-macro-internals/Cargo.toml deleted file mode 100644 index dde611b..0000000 --- a/rpc-toolkit-macro-internals/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -authors = ["Aiden McClelland "] -edition = "2018" -name = "rpc-toolkit-macro-internals" -version = "0.2.2" -description = "internals for macros for rpc-toolkit" -license = "MIT" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -proc-macro2 = "1.0" -quote = "1.0" -syn = { version = "1.0", features = ["full", "fold"] } -itertools = "0.12" diff --git a/rpc-toolkit-macro-internals/src/command/build.rs b/rpc-toolkit-macro-internals/src/command/build.rs deleted file mode 100644 index 8b3190f..0000000 --- a/rpc-toolkit-macro-internals/src/command/build.rs +++ /dev/null @@ -1,1511 +0,0 @@ -use std::collections::HashSet; - -use itertools::MultiUnzip; -use proc_macro2::*; -use quote::*; -use syn::fold::Fold; -use syn::punctuated::Punctuated; -use syn::spanned::Spanned; -use syn::token::{Add, Comma, Where}; - -use super::parse::*; -use super::*; - -// fn ctx_trait(ctx_ty: Option, opt: &mut Options) -> TokenStream { -// let mut bounds: Punctuated = Punctuated::new(); -// bounds.push(macro_try!(parse2(quote! { ::rpc_toolkit::Context }))); -// let mut rpc_bounds = bounds.clone(); -// let mut cli_bounds = bounds; - -// let (use_cli, use_rpc) = match &opt.common().exec_ctx { -// ExecutionContext::CliOnly(_) => (Some(None), false), -// ExecutionContext::RpcOnly(_) | ExecutionContext::Standard => (None, true), -// ExecutionContext::Local(_) => (Some(None), true), -// ExecutionContext::CustomCli { context, .. } => (Some(Some(context.clone())), true), -// }; - -// if let Options::Parent(ParentOptions { -// subcommands, -// self_impl, -// .. -// }) = opt -// { -// if let Some(ctx_ty) = ctx_ty { -// cli_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); -// cli_bounds.push(macro_try!(parse2(quote! { Clone }))); -// rpc_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); -// rpc_bounds.push(macro_try!(parse2(quote! { Clone }))); -// } -// if let Some(SelfImplInfo { context, .. }) = self_impl { -// if let Some(cli_ty) = use_cli.as_ref() { -// if let Some(cli_ty) = cli_ty { -// cli_bounds.push(macro_try!(parse2(quote! { Into<#cli_ty> }))); -// } else { -// cli_bounds.push(macro_try!(parse2(quote! { Into<#context> }))); -// } -// } -// if use_rpc { -// rpc_bounds.push(macro_try!(parse2(quote! { Into<#context> }))); -// } -// } -// for subcmd in subcommands { -// let mut path = subcmd.clone(); -// std::mem::take(&mut path.segments.last_mut().unwrap().arguments); -// cli_bounds.push(macro_try!(parse2(quote! { #path::CommandContextCli }))); -// rpc_bounds.push(macro_try!(parse2(quote! { #path::CommandContextRpc }))); -// } -// } else { -// if let Some(cli_ty) = use_cli.as_ref() { -// if let Some(cli_ty) = cli_ty { -// cli_bounds.push(macro_try!(parse2(quote! { Into<#cli_ty> }))); -// } else if let Some(ctx_ty) = &ctx_ty { -// cli_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); -// } -// } -// if use_rpc { -// if let Some(ctx_ty) = &ctx_ty { -// rpc_bounds.push(macro_try!(parse2(quote! { Into<#ctx_ty> }))); -// } -// } -// } - -// let res = quote! { -// pub trait CommandContextCli: #cli_bounds {} -// impl CommandContextCli for T where T: #cli_bounds {} - -// pub trait CommandContextRpc: #rpc_bounds {} -// impl CommandContextRpc for T where T: #rpc_bounds {} -// }; -// res -// } - -// fn metadata(full_options: &Options) -> TokenStream { -// let options = match full_options { -// Options::Leaf(a) => a, -// Options::Parent(ParentOptions { common, .. }) => common, -// }; -// let fallthrough = |ty: &str| { -// let getter_name = Ident::new(&format!("get_{}", ty), Span::call_site()); -// match &*full_options { -// Options::Parent(ParentOptions { subcommands, .. }) => { -// let subcmd_handler = subcommands.iter().map(|subcmd| { -// let mut subcmd = subcmd.clone(); -// subcmd.segments.last_mut().unwrap().arguments = PathArguments::None; -// quote_spanned!{ subcmd.span() => -// [#subcmd::NAME, rest] => if let Some(val) = #subcmd::Metadata.#getter_name(rest, key) { -// return Some(val); -// }, -// } -// }); -// quote! { -// if !command.is_empty() { -// match command.splitn(2, ".").chain(std::iter::repeat("")).take(2).collect::>().as_slice() { -// #( -// #subcmd_handler -// )* -// _ => () -// } -// } -// } -// } -// _ => quote! {}, -// } -// }; -// fn impl_getter>( -// ty: &str, -// metadata: I, -// fallthrough: TokenStream, -// ) -> TokenStream { -// let getter_name = Ident::new(&format!("get_{}", ty), Span::call_site()); -// let ty: Type = syn::parse_str(ty).unwrap(); -// quote! { -// fn #getter_name(&self, command: &str, key: &str) -> Option<#ty> { -// #fallthrough -// match key { -// #(#metadata)* -// _ => None, -// } -// } -// } -// } -// let bool_metadata = options -// .metadata -// .iter() -// .filter(|(_, lit)| matches!(lit, Lit::Bool(_))) -// .map(|(name, value)| { -// let name = LitStr::new(&name.to_string(), name.span()); -// quote! { -// #name => Some(#value), -// } -// }); -// let number_metadata = |ty: &str| { -// let ty: Type = syn::parse_str(ty).unwrap(); -// options -// .metadata -// .iter() -// .filter(|(_, lit)| matches!(lit, Lit::Int(_) | Lit::Float(_) | Lit::Byte(_))) -// .map(move |(name, value)| { -// let name = LitStr::new(&name.to_string(), name.span()); -// quote! { -// #name => Some(#value as #ty), -// } -// }) -// }; -// let char_metadata = options -// .metadata -// .iter() -// .filter(|(_, lit)| matches!(lit, Lit::Char(_))) -// .map(|(name, value)| { -// let name = LitStr::new(&name.to_string(), name.span()); -// quote! { -// #name => Some(#value), -// } -// }); -// let str_metadata = options -// .metadata -// .iter() -// .filter(|(_, lit)| matches!(lit, Lit::Str(_))) -// .map(|(name, value)| { -// let name = LitStr::new(&name.to_string(), name.span()); -// quote! { -// #name => Some(#value), -// } -// }); -// let bstr_metadata = options -// .metadata -// .iter() -// .filter(|(_, lit)| matches!(lit, Lit::ByteStr(_))) -// .map(|(name, value)| { -// let name = LitStr::new(&name.to_string(), name.span()); -// quote! { -// #name => Some(#value), -// } -// }); - -// let bool_getter = impl_getter("bool", bool_metadata, fallthrough("bool")); -// let u8_getter = impl_getter("u8", number_metadata("u8"), fallthrough("u8")); -// let u16_getter = impl_getter("u16", number_metadata("u16"), fallthrough("u16")); -// let u32_getter = impl_getter("u32", number_metadata("u32"), fallthrough("u32")); -// let u64_getter = impl_getter("u64", number_metadata("u64"), fallthrough("u64")); -// let usize_getter = impl_getter("usize", number_metadata("usize"), fallthrough("usize")); -// let i8_getter = impl_getter("i8", number_metadata("i8"), fallthrough("i8")); -// let i16_getter = impl_getter("i16", number_metadata("i16"), fallthrough("i16")); -// let i32_getter = impl_getter("i32", number_metadata("i32"), fallthrough("i32")); -// let i64_getter = impl_getter("i64", number_metadata("i64"), fallthrough("i64")); -// let isize_getter = impl_getter("isize", number_metadata("isize"), fallthrough("isize")); -// let f32_getter = impl_getter("f32", number_metadata("f32"), fallthrough("f32")); -// let f64_getter = impl_getter("f64", number_metadata("f64"), fallthrough("f64")); -// let char_getter = impl_getter("char", char_metadata, fallthrough("char")); -// let str_fallthrough = fallthrough("str"); -// let str_getter = quote! { -// fn get_str(&self, command: &str, key: &str) -> Option<&'static str> { -// #str_fallthrough -// match key { -// #(#str_metadata)* -// _ => None, -// } -// } -// }; -// let bstr_fallthrough = fallthrough("bstr"); -// let bstr_getter = quote! { -// fn get_bstr(&self, command: &str, key: &str) -> Option<&'static [u8]> { -// #bstr_fallthrough -// match key { -// #(#bstr_metadata)* -// _ => None, -// } -// } -// }; - -// let res = quote! { -// #[derive(Clone, Copy, Default)] -// pub struct Metadata; - -// #[allow(overflowing_literals)] -// impl ::rpc_toolkit::Metadata for Metadata { -// #bool_getter -// #u8_getter -// #u16_getter -// #u32_getter -// #u64_getter -// #usize_getter -// #i8_getter -// #i16_getter -// #i32_getter -// #i64_getter -// #isize_getter -// #f32_getter -// #f64_getter -// #char_getter -// #str_getter -// #bstr_getter -// } -// }; -// // panic!("{}", res); -// res -// } - -// fn build_app(name: LitStr, opt: &mut Options, params: &mut [ParamType]) -> TokenStream { -// let about = opt.common().about.clone().into_iter(); -// let (subcommand, subcommand_required) = if let Options::Parent(opt) = opt { -// ( -// opt.subcommands -// .iter() -// .map(|subcmd| { -// let mut path = subcmd.clone(); -// path.segments.last_mut().unwrap().arguments = PathArguments::None; -// path -// }) -// .collect(), -// opt.self_impl.is_none(), -// ) -// } else { -// (Vec::new(), false) -// }; -// let arg = params -// .iter_mut() -// .filter_map(|param| { -// if let ParamType::Arg(arg) = param { -// if arg.stdin.is_some() { -// return None; -// } -// let name = arg.name.clone().unwrap(); -// let name_str = arg.rename.clone().unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); -// let help = arg.help.clone().into_iter(); -// let short = arg.short.clone().into_iter(); -// let long = arg.long.clone().into_iter(); -// let mut modifications = TokenStream::default(); -// let ty_span = arg.ty.span(); -// if let Type::Path(p) = &mut arg.ty { -// if p.path.is_ident("bool") -// && arg.parse.is_none() -// && (arg.short.is_some() || arg.long.is_some()) -// { -// arg.check_is_present = true; -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.takes_value(false); -// }); -// } else if arg.count.is_some() { -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.takes_value(false); -// arg = arg.multiple(true); -// }); -// } else { -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.takes_value(true); -// }); -// if let Some(_) = &arg.default { -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.required(false); -// }); -// } else if p.path.segments.last().unwrap().ident == "Option" { -// arg.optional = true; -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.required(false); -// }); -// } else if arg.multiple.is_some() { -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.multiple(true); -// }); -// } else { -// modifications.extend(quote_spanned! { ty_span => -// arg = arg.required(true); -// }); -// } -// } -// }; -// Some(quote! { -// { -// let mut arg = ::rpc_toolkit::command_helpers::prelude::Arg::with_name(#name_str); -// #( -// arg = arg.help(#help); -// )* -// #( -// arg = arg.short(#short); -// )* -// #( -// arg = arg.long(#long); -// )* -// #modifications - -// arg -// } -// }) -// } else { -// None -// } -// }) -// .collect::>(); -// let required = LitBool::new(subcommand_required, Span::call_site()); -// let alias = &opt.common().aliases; -// quote! { -// pub fn build_app() -> ::rpc_toolkit::command_helpers::prelude::App<'static> { -// let mut app = ::rpc_toolkit::command_helpers::prelude::App::new(#name); -// #( -// app = app.about(#about); -// )* -// #( -// app = app.alias(#alias); -// )* -// #( -// app = app.arg(#arg); -// )* -// #( -// app = app.subcommand(#subcommand::build_app()); -// )* -// if #required { -// app = app.setting(::rpc_toolkit::command_helpers::prelude::AppSettings::SubcommandRequired); -// } -// app -// } -// } -// } - -struct GenericFilter<'a> { - src: &'a Generics, - lifetimes: HashSet, - types: HashSet, -} -impl<'a> GenericFilter<'a> { - fn new(src: &'a Generics) -> Self { - GenericFilter { - src, - lifetimes: HashSet::new(), - types: HashSet::new(), - } - } - fn finish(self) -> Generics { - let mut params: Punctuated = Default::default(); - let mut where_clause = self - .src - .where_clause - .as_ref() - .map(|wc| WhereClause { - where_token: wc.where_token, - predicates: Default::default(), - }) - .unwrap_or_else(|| WhereClause { - where_token: Where(Span::call_site()), - predicates: Default::default(), - }); - for src_param in &self.src.params { - match src_param { - GenericParam::Lifetime(l) if self.lifetimes.contains(&l.lifetime) => { - params.push(src_param.clone()) - } - GenericParam::Type(t) if self.types.contains(&t.ident) => { - params.push(src_param.clone()) - } - _ => (), - } - } - for src_predicate in self.src.where_clause.iter().flat_map(|wc| &wc.predicates) { - match src_predicate { - WherePredicate::Lifetime(l) if self.lifetimes.contains(&l.lifetime) => { - where_clause.predicates.push(src_predicate.clone()) - } - WherePredicate::Type(PredicateType { - bounded_ty: Type::Path(t), - .. - }) if self.types.contains(&t.path.segments.first().unwrap().ident) => { - where_clause.predicates.push(src_predicate.clone()) - } - _ => (), - } - } - Generics { - lt_token: if params.is_empty() { - None - } else { - self.src.lt_token.clone() - }, - gt_token: if params.is_empty() { - None - } else { - self.src.gt_token.clone() - }, - params, - where_clause: if where_clause.predicates.is_empty() { - None - } else { - Some(where_clause) - }, - } - } -} -impl<'a> Fold for GenericFilter<'a> { - fn fold_lifetime(&mut self, i: Lifetime) -> Lifetime { - self.lifetimes - .extend(self.src.params.iter().filter_map(|param| match param { - GenericParam::Lifetime(l) if l.lifetime == i => Some(l.lifetime.clone()), - _ => None, - })); - i - } - fn fold_type(&mut self, i: Type) -> Type { - self.types.extend( - self.src - .params - .iter() - .filter_map(|param| match (param, &i) { - (GenericParam::Type(t), Type::Path(i)) - if &i.path.segments.first().unwrap().ident == &t.ident => - { - Some(t.ident.clone()) - } - _ => None, - }), - ); - i - } -} - -// fn rpc_handler( -// fn_name: &Ident, -// fn_generics: &Generics, -// opt: &Options, -// params: &[ParamType], -// ) -> TokenStream { -// let mut parent_data_ty = quote! { () }; -// let mut generics = fn_generics.clone(); -// generics.params.push(macro_try!(syn::parse2( -// quote! { GenericContext: CommandContextRpc } -// ))); -// if generics.lt_token.is_none() { -// generics.lt_token = Some(Default::default()); -// } -// if generics.gt_token.is_none() { -// generics.gt_token = Some(Default::default()); -// } -// let mut param_def = Vec::new(); -// for param in params { -// match param { -// ParamType::Arg(arg) => { -// let name = arg.name.clone().unwrap(); -// let rename = arg -// .rename -// .clone() -// .unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// let ty = arg.ty.clone(); -// let def = quote! { -// #[serde(rename = #rename)] -// #field_name: #ty, -// }; -// let def = match &arg.default { -// Some(Some(default)) => { -// quote! { -// #[serde(default = #default)] -// #def -// } -// } -// Some(None) => { -// quote! { -// #[serde(default)] -// #def -// } -// } -// None => def, -// }; -// param_def.push(def); -// } -// ParamType::ParentData(ty) => parent_data_ty = quote! { #ty }, -// _ => (), -// } -// } -// let (_, fn_type_generics, _) = fn_generics.split_for_impl(); -// let fn_turbofish = fn_type_generics.as_turbofish(); -// let fn_path: Path = macro_try!(syn::parse2(quote! { super::#fn_name#fn_turbofish })); -// let mut param_generics_filter = GenericFilter::new(fn_generics); -// for param in params { -// if let ParamType::Arg(a) = param { -// param_generics_filter.fold_type(a.ty.clone()); -// } -// } -// let param_generics = param_generics_filter.finish(); -// let (_, param_ty_generics, _) = param_generics.split_for_impl(); -// let param_struct_def = quote! { -// #[allow(dead_code)] -// #[derive(::rpc_toolkit::command_helpers::prelude::Deserialize)] -// pub struct Params#param_ty_generics { -// #( -// #param_def -// )* -// #[serde(flatten)] -// #[serde(default)] -// rest: ::rpc_toolkit::command_helpers::prelude::Value, -// } -// }; -// let param = params.iter().map(|param| match param { -// ParamType::Arg(arg) => { -// let name = arg.name.clone().unwrap(); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// quote! { args.#field_name } -// } -// ParamType::Context(ty) => { -// if matches!(opt, Options::Parent { .. }) { -// quote! { >::into(ctx.clone()) } -// } else { -// quote! { >::into(ctx) } -// } -// } -// ParamType::ParentData(_) => { -// quote! { parent_data } -// } -// ParamType::Request => quote! { request }, -// ParamType::Response => quote! { response }, -// ParamType::None => unreachable!(), -// }); -// match opt { -// Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::CliOnly(_)) => quote! { -// #param_struct_def - -// pub async fn rpc_handler#generics( -// _ctx: GenericContext, -// _parent_data: #parent_data_ty, -// _request: &::rpc_toolkit::command_helpers::prelude::RequestParts, -// _response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, -// method: &str, -// _args: Params#param_ty_generics, -// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { -// Err(::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(method.into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR -// }) -// } -// }, -// Options::Leaf(opt) => { -// let invocation = if opt.is_async { -// quote! { -// #fn_path(#(#param),*).await? -// } -// } else if opt.blocking.is_some() { -// quote! { -// ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #fn_path(#(#param),*)).await? -// } -// } else { -// quote! { -// #fn_path(#(#param),*)? -// } -// }; -// quote! { -// #param_struct_def - -// pub async fn rpc_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// request: &::rpc_toolkit::command_helpers::prelude::RequestParts, -// response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, -// method: &str, -// args: Params#param_ty_generics, -// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { -// if method.is_empty() { -// Ok(::rpc_toolkit::command_helpers::prelude::to_value(#invocation)?) -// } else { -// Err(::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(method.into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR -// }) -// } -// } -// } -// } -// Options::Parent(ParentOptions { -// common, -// subcommands, -// self_impl, -// }) => { -// let cmd_preprocess = if common.is_async { -// quote! { -// let parent_data = #fn_path(#(#param),*).await?; -// } -// } else if common.blocking.is_some() { -// quote! { -// let parent_data = ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #fn_path(#(#param),*)).await?; -// } -// } else { -// quote! { -// let parent_data = #fn_path(#(#param),*)?; -// } -// }; -// let subcmd_impl = subcommands.iter().map(|subcommand| { -// let mut subcommand = subcommand.clone(); -// let mut rpc_handler = PathSegment { -// ident: Ident::new("rpc_handler", Span::call_site()), -// arguments: std::mem::replace( -// &mut subcommand.segments.last_mut().unwrap().arguments, -// PathArguments::None, -// ), -// }; -// rpc_handler.arguments = match rpc_handler.arguments { -// PathArguments::None => PathArguments::AngleBracketed( -// syn::parse2(quote! { :: }) -// .unwrap(), -// ), -// PathArguments::AngleBracketed(mut a) => { -// a.args.push(syn::parse2(quote! { GenericContext }).unwrap()); -// PathArguments::AngleBracketed(a) -// } -// _ => unreachable!(), -// }; -// quote_spanned!{ subcommand.span() => -// [#subcommand::NAME, rest] => #subcommand::#rpc_handler(ctx, parent_data, request, response, rest, ::rpc_toolkit::command_helpers::prelude::from_value(args.rest)?).await -// } -// }); -// let subcmd_impl = quote! { -// match method.splitn(2, ".").chain(std::iter::repeat("")).take(2).collect::>().as_slice() { -// #( -// #subcmd_impl, -// )* -// _ => Err(::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(method.into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR -// }) -// } -// }; -// match self_impl { -// Some(self_impl) if !matches!(common.exec_ctx, ExecutionContext::CliOnly(_)) => { -// let self_impl_fn = &self_impl.path; -// let self_impl = if self_impl.is_async { -// quote_spanned! { self_impl_fn.span() => -// #self_impl_fn(Into::into(ctx), parent_data).await? -// } -// } else if self_impl.blocking { -// quote_spanned! { self_impl_fn.span() => -// { -// let ctx = Into::into(ctx); -// ::rpc_toolkit::command_helpers::prelude::spawn_blocking(move || #self_impl_fn(ctx, parent_data)).await? -// } -// } -// } else { -// quote_spanned! { self_impl_fn.span() => -// #self_impl_fn(Into::into(ctx), parent_data)? -// } -// }; -// quote! { -// #param_struct_def - -// pub async fn rpc_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// request: &::rpc_toolkit::command_helpers::prelude::RequestParts, -// response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, -// method: &str, -// args: Params#param_ty_generics, -// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #cmd_preprocess - -// if method.is_empty() { -// Ok(::rpc_toolkit::command_helpers::prelude::to_value(&#self_impl)?) -// } else { -// #subcmd_impl -// } -// } -// } -// } -// _ => { -// quote! { -// #param_struct_def - -// pub async fn rpc_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// request: &::rpc_toolkit::command_helpers::prelude::RequestParts, -// response: &mut ::rpc_toolkit::command_helpers::prelude::ResponseParts, -// method: &str, -// args: Params#param_ty_generics, -// ) -> Result<::rpc_toolkit::command_helpers::prelude::Value, ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #cmd_preprocess - -// #subcmd_impl -// } -// } -// } -// } -// } -// } -// } - -// fn cli_handler( -// fn_name: &Ident, -// fn_generics: &Generics, -// opt: &mut Options, -// params: &[ParamType], -// ) -> TokenStream { -// let mut parent_data_ty = quote! { () }; -// let mut generics = fn_generics.clone(); -// generics.params.push(macro_try!(syn::parse2( -// quote! { ParentParams: ::rpc_toolkit::command_helpers::prelude::Serialize } -// ))); -// generics.params.push(macro_try!(syn::parse2( -// quote! { GenericContext: CommandContextCli } -// ))); -// if generics.lt_token.is_none() { -// generics.lt_token = Some(Default::default()); -// } -// if generics.gt_token.is_none() { -// generics.gt_token = Some(Default::default()); -// } -// let (_, fn_type_generics, _) = fn_generics.split_for_impl(); -// let fn_turbofish = fn_type_generics.as_turbofish(); -// let fn_path: Path = macro_try!(syn::parse2(quote! { super::#fn_name#fn_turbofish })); -// let is_parent = matches!(opt, Options::Parent { .. }); -// let param: Vec<_> = params -// .iter() -// .map(|param| match param { -// ParamType::Arg(arg) => { -// let name = arg.name.clone().unwrap(); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// quote! { params.#field_name.clone() } -// } -// ParamType::Context(ty) => { -// if is_parent { -// quote! { >::into(ctx.clone()) } -// } else { -// quote! { >::into(ctx) } -// } -// } -// ParamType::ParentData(ty) => { -// parent_data_ty = quote! { #ty }; -// quote! { parent_data } -// } -// ParamType::Request => quote! { request }, -// ParamType::Response => quote! { response }, -// ParamType::None => unreachable!(), -// }) -// .collect(); -// let mut param_generics_filter = GenericFilter::new(fn_generics); -// for param in params { -// if let ParamType::Arg(a) = param { -// param_generics_filter.fold_type(a.ty.clone()); -// } -// } -// let mut param_generics = param_generics_filter.finish(); -// param_generics.params.push(macro_try!(syn::parse2(quote! { -// ParentParams: ::rpc_toolkit::command_helpers::prelude::Serialize -// }))); -// if param_generics.lt_token.is_none() { -// param_generics.lt_token = Some(Default::default()); -// } -// if param_generics.gt_token.is_none() { -// param_generics.gt_token = Some(Default::default()); -// } -// let (_, param_ty_generics, _) = param_generics.split_for_impl(); -// let mut arg_def = Vec::new(); -// for param in params { -// match param { -// ParamType::Arg(arg) => { -// let name = arg.name.clone().unwrap(); -// let rename = arg -// .rename -// .clone() -// .unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// let ty = arg.ty.clone(); -// arg_def.push(quote! { -// #[serde(rename = #rename)] -// #field_name: #ty, -// }) -// } -// _ => (), -// } -// } -// let arg = params -// .iter() -// .filter_map(|param| { -// if let ParamType::Arg(a) = param { -// Some(a) -// } else { -// None -// } -// }) -// .map(|arg| { -// let name = arg.name.clone().unwrap(); -// let arg_name = arg.rename.clone().unwrap_or_else(|| LitStr::new(&name.to_string(), name.span())); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// if arg.stdin.is_some() { -// if let Some(parse) = &arg.parse { -// quote! { -// #field_name: #parse(&mut std::io::stdin(), matches)?, -// } -// } else { -// quote! { -// #field_name: ::rpc_toolkit::command_helpers::prelude::default_stdin_parser(&mut std::io::stdin(), matches)?, -// } -// } -// } else if arg.check_is_present { -// quote! { -// #field_name: matches.is_present(#arg_name), -// } -// } else if arg.count.is_some() { -// quote! { -// #field_name: matches.occurrences_of(#arg_name), -// } -// } else { -// let parse_val = if let Some(parse) = &arg.parse { -// quote! { -// #parse(arg_val, matches) -// } -// } else { -// quote! { -// ::rpc_toolkit::command_helpers::prelude::default_arg_parser(arg_val, matches) -// } -// }; -// if arg.optional { -// quote! { -// #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { -// Some(#parse_val?) -// } else { -// None -// }, -// } -// } else if let Some(default) = &arg.default { -// if let Some(default) = default { -// let path: Path = match syn::parse_str(&default.value()) { -// Ok(a) => a, -// Err(e) => return e.into_compile_error(), -// }; -// quote! { -// #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { -// #parse_val? -// } else { -// #path() -// }, -// } -// } else { -// quote! { -// #field_name: if let Some(arg_val) = matches.value_of(#arg_name) { -// #parse_val? -// } else { -// Default::default() -// }, -// } -// } -// } else if arg.multiple.is_some() { -// quote! { -// #field_name: matches.values_of(#arg_name).iter().flatten().map(|arg_val| #parse_val).collect::>()?, -// } -// } else { -// quote! { -// #field_name: { -// let arg_val = matches.value_of(#arg_name).unwrap(); -// #parse_val? -// }, -// } -// } -// } -// }); -// let param_struct_def = quote! { -// #[derive(::rpc_toolkit::command_helpers::prelude::Serialize)] -// struct Params#param_ty_generics { -// #( -// #arg_def -// )* -// #[serde(flatten)] -// rest: ParentParams, -// } -// let params: Params#param_ty_generics = Params { -// #( -// #arg -// )* -// rest: parent_params, -// }; -// }; -// let create_rt = quote! { -// let rt_ref = if let Some(rt) = rt.as_mut() { -// &*rt -// } else { -// rt = Some(::rpc_toolkit::command_helpers::prelude::Runtime::new().map_err(|e| ::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(format!("{}", e).into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::INTERNAL_ERROR -// })?); -// rt.as_ref().unwrap() -// }; -// }; -// let display = if let Some(display) = &opt.common().display { -// quote! { #display } -// } else { -// quote! { ::rpc_toolkit::command_helpers::prelude::default_display } -// }; -// match opt { -// Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::RpcOnly(_)) => quote! { -// pub fn cli_handler#generics( -// _ctx: GenericContext, -// _parent_data: #parent_data_ty, -// _rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, -// _matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, -// method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, -// _parent_params: ParentParams, -// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { -// Err(::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(method.into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR -// }) -// } -// }, -// Options::Leaf(opt) if matches!(opt.exec_ctx, ExecutionContext::Standard) => { -// let param = param.into_iter().map(|_| quote! { unreachable!() }); -// let invocation = if opt.is_async { -// quote! { -// rt_ref.block_on(#fn_path(#(#param),*))? -// } -// } else { -// quote! { -// #fn_path(#(#param),*)? -// } -// }; -// quote! { -// pub fn cli_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, -// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, -// method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, -// parent_params: ParentParams, -// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #param_struct_def - -// #create_rt - -// #[allow(unreachable_code)] -// let return_ty = if true { -// ::rpc_toolkit::command_helpers::prelude::PhantomData -// } else { -// let ctx_new = unreachable!(); -// ::rpc_toolkit::command_helpers::prelude::match_types(&ctx, &ctx_new); -// let ctx = ctx_new; -// ::rpc_toolkit::command_helpers::prelude::make_phantom(#invocation) -// }; - -// let res = rt_ref.block_on(::rpc_toolkit::command_helpers::prelude::call_remote(ctx, method.as_ref(), params, return_ty))?; -// Ok(#display(res.result?, matches)) -// } -// } -// } -// Options::Leaf(opt) => { -// if let ExecutionContext::CustomCli { -// ref cli, is_async, .. -// } = opt.exec_ctx -// { -// let fn_path = cli; -// let cli_param = params.iter().filter_map(|param| match param { -// ParamType::Arg(arg) => { -// let name = arg.name.clone().unwrap(); -// let field_name = Ident::new(&format!("arg_{}", name), name.span()); -// Some(quote! { params.#field_name.clone() }) -// } -// ParamType::Context(_) => Some(quote! { Into::into(ctx) }), -// ParamType::ParentData(_) => Some(quote! { parent_data }), -// ParamType::Request => None, -// ParamType::Response => None, -// ParamType::None => unreachable!(), -// }); -// let invocation = if is_async { -// quote! { -// rt_ref.block_on(#fn_path(#(#cli_param),*))? -// } -// } else { -// quote! { -// #fn_path(#(#cli_param),*)? -// } -// }; -// let display_res = if let Some(display_fn) = &opt.display { -// quote! { -// #display_fn(#invocation, matches) -// } -// } else { -// quote! { -// ::rpc_toolkit::command_helpers::prelude::default_display(#invocation, matches) -// } -// }; -// let rt_action = if is_async { -// create_rt -// } else { -// quote! { -// drop(rt); -// } -// }; -// quote! { -// pub fn cli_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, -// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, -// _method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, -// parent_params: ParentParams -// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #param_struct_def - -// #rt_action - -// Ok(#display_res) -// } -// } -// } else { -// let invocation = if opt.is_async { -// quote! { -// rt_ref.block_on(#fn_path(#(#param),*))? -// } -// } else { -// quote! { -// #fn_path(#(#param),*)? -// } -// }; -// let display_res = if let Some(display_fn) = &opt.display { -// quote! { -// #display_fn(#invocation, matches) -// } -// } else { -// quote! { -// ::rpc_toolkit::command_helpers::prelude::default_display(#invocation, matches) -// } -// }; -// let rt_action = if opt.is_async { -// create_rt -// } else { -// quote! { -// drop(rt); -// } -// }; -// quote! { -// pub fn cli_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, -// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, -// _method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, -// parent_params: ParentParams -// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #param_struct_def - -// #rt_action - -// Ok(#display_res) -// } -// } -// } -// } -// Options::Parent(ParentOptions { -// common, -// subcommands, -// self_impl, -// }) => { -// let cmd_preprocess = if common.is_async { -// quote! { -// #create_rt -// let parent_data = rt_ref.block_on(#fn_path(#(#param),*))?; -// } -// } else { -// quote! { -// let parent_data = #fn_path(#(#param),*)?; -// } -// }; -// let subcmd_impl = subcommands.iter().map(|subcommand| { -// let mut subcommand = subcommand.clone(); -// let mut cli_handler = PathSegment { -// ident: Ident::new("cli_handler", Span::call_site()), -// arguments: std::mem::replace( -// &mut subcommand.segments.last_mut().unwrap().arguments, -// PathArguments::None, -// ), -// }; -// cli_handler.arguments = match cli_handler.arguments { -// PathArguments::None => PathArguments::AngleBracketed( -// syn::parse2(quote! { :: }) -// .unwrap(), -// ), -// PathArguments::AngleBracketed(mut a) => { -// a.args -// .push(syn::parse2(quote! { Params#param_ty_generics }).unwrap()); -// a.args.push(syn::parse2(quote! { GenericContext }).unwrap()); -// PathArguments::AngleBracketed(a) -// } -// _ => unreachable!(), -// }; -// quote_spanned! { subcommand.span() => -// Some((#subcommand::NAME, sub_m)) => { -// let method = if method.is_empty() { -// #subcommand::NAME.into() -// } else { -// method + "." + #subcommand::NAME -// }; -// #subcommand::#cli_handler(ctx, parent_data, rt, sub_m, method, params) -// }, -// } -// }); -// let self_impl = match (self_impl, &common.exec_ctx) { -// (Some(self_impl), ExecutionContext::CliOnly(_)) -// | (Some(self_impl), ExecutionContext::Local(_)) -// | (Some(self_impl), ExecutionContext::CustomCli { .. }) => { -// let (self_impl_fn, is_async) = -// if let ExecutionContext::CustomCli { cli, is_async, .. } = &common.exec_ctx -// { -// (cli, *is_async) -// } else { -// (&self_impl.path, self_impl.is_async) -// }; -// let create_rt = if common.is_async { -// None -// } else { -// Some(create_rt) -// }; -// let self_impl = if is_async { -// quote_spanned! { self_impl_fn.span() => -// #create_rt -// rt_ref.block_on(#self_impl_fn(Into::into(ctx), parent_data))? -// } -// } else { -// quote_spanned! { self_impl_fn.span() => -// #self_impl_fn(Into::into(ctx), parent_data)? -// } -// }; -// quote! { -// Ok(#display(#self_impl, matches)), -// } -// } -// (Some(self_impl), ExecutionContext::Standard) => { -// let self_impl_fn = &self_impl.path; -// let self_impl = if self_impl.is_async { -// quote! { -// rt_ref.block_on(#self_impl_fn(unreachable!(), parent_data)) -// } -// } else { -// quote! { -// #self_impl_fn(unreachable!(), parent_data) -// } -// }; -// let create_rt = if common.is_async { -// None -// } else { -// Some(create_rt) -// }; -// quote! { -// { -// #create_rt - -// #[allow(unreachable_code)] -// let return_ty = if true { -// ::rpc_toolkit::command_helpers::prelude::PhantomData -// } else { -// ::rpc_toolkit::command_helpers::prelude::make_phantom(#self_impl?) -// }; - -// let res = rt_ref.block_on(::rpc_toolkit::command_helpers::prelude::call_remote(ctx, method.as_ref(), params, return_ty))?; -// Ok(#display(res.result?, matches)) -// } -// } -// } -// (None, _) | (Some(_), ExecutionContext::RpcOnly(_)) => quote! { -// Err(::rpc_toolkit::command_helpers::prelude::RpcError { -// data: Some(method.into()), -// ..::rpc_toolkit::command_helpers::prelude::yajrc::METHOD_NOT_FOUND_ERROR -// }), -// }, -// }; -// quote! { -// pub fn cli_handler#generics( -// ctx: GenericContext, -// parent_data: #parent_data_ty, -// mut rt: Option<::rpc_toolkit::command_helpers::prelude::Runtime>, -// matches: &::rpc_toolkit::command_helpers::prelude::ArgMatches, -// method: ::rpc_toolkit::command_helpers::prelude::Cow<'_, str>, -// parent_params: ParentParams, -// ) -> Result<(), ::rpc_toolkit::command_helpers::prelude::RpcError> { -// #param_struct_def - -// #cmd_preprocess - -// match matches.subcommand() { -// #( -// #subcmd_impl -// )* -// _ => #self_impl -// } -// } -// } -// } -// } -// } - -fn build_params(params: Vec, generics: &Generics) -> (TokenStream, Generics) { - let mut param_generics_filter = GenericFilter::new(generics); - for param in ¶ms { - param_generics_filter.fold_type(param.ty.clone()); - } - let param_generics = param_generics_filter.finish(); - let (impl_generics, ty_generics, where_clause) = param_generics.split_for_impl(); - let param_arg = params.iter().enumerate().map(|(idx, p)| { - let mut res = TokenStream::new(); - let p_ty = &p.ty; - if let Some(rename) = &p.rename { - res.extend(quote! { #[serde(rename = #rename)] }); - } else if let Some(name) = &p.name { - let name = LitStr::new(&name.to_string(), name.span()); - res.extend(quote! { #[serde(rename = #name)] }); - }; - if let Some(default) = &p.default { - res.extend(quote! { #[serde(#default)] }); - } - let arg_ident = Ident::new(&format!("arg_{idx}"), Span::call_site()); - let p_name = p.name.as_ref().unwrap_or(&arg_ident); - res.extend(quote! { pub #p_name: #p_ty, }); - res - }); - let (clap_param_arg, clap_param_from_matches): (Vec<_>, Vec<_>) = params - .iter() - .enumerate() - .map(|(idx, p)| { - let (mut arg, mut from_matches) = (TokenStream::new(), TokenStream::new()); - let arg_ident = Ident::new(&format!("arg_{idx}"), Span::call_site()); - let p_name = p - .name - .as_ref() - .unwrap_or(&arg_ident); - if p.stdin.is_some() { - let parser = p - .parse.as_ref() - .map(|p| quote! { #p }) - .unwrap_or(quote!(::rpc_toolkit::command_helpers::default_stdin_parser)); - from_matches.extend(quote! { #p_name: #parser(&mut std::io::stdin(), matches)? }); - } else if matches!(&p.ty, Type::Path(p) if p.path.is_ident("bool")) { - arg.extend(if p.clap_attr.is_empty() { - quote! { #[arg] } - } else { - let clap_attr = &p.clap_attr; - quote! { #[arg(#(#clap_attr),*)] } - }); - arg.extend(quote! { #p_name: bool, }); - from_matches.extend(quote! { #p_name: clap_args.#p_name, }); - } else if matches!(&p.ty, Type::Path(p) if p.path.segments.first().unwrap().ident == "Option") { - let parser = p - .parse.as_ref() - .map(|p| quote!(#p)) - .unwrap_or(quote!(::rpc_toolkit::command_helpers::default_arg_parser)); - arg.extend(if p.clap_attr.is_empty() { - quote! { #[arg] } - } else { - let clap_attr = &p.clap_attr; - quote! { #[arg(#(#clap_attr),*)] } - }); - arg.extend(quote! { #p_name: Option, }); - from_matches.extend(quote! { #p_name: clap_args.#p_name.as_ref().map(|arg_str| #parser(arg_str, matches)).transpose()?, }); - } else { - let parser = p - .parse.as_ref() - .map(|p| quote!(#p)) - .unwrap_or(quote!(::rpc_toolkit::command_helpers::default_arg_parser)); - arg.extend(if p.clap_attr.is_empty() { - quote! { #[arg] } - } else { - let clap_attr = &p.clap_attr; - quote! { #[arg(#(#clap_attr),*)] } - }); - arg.extend(quote! { #p_name: String, }); - from_matches.extend(quote! { #p_name: #parser(&clap_args.#p_name, matches)?, }); - } - (arg, from_matches) - }) - .unzip(); - ( - quote! { - #[derive(::rpc_toolkit::serde::Serialize, ::rpc_toolkit::serde::Deserialize)] - pub struct Params #ty_generics { - #( - #param_arg - )* - } - #[derive(::rpc_toolkit::clap::Parser)] - struct ClapParams { - #( - #clap_param_arg - )* - } - impl #impl_generics ::rpc_toolkit::command_helpers::clap::FromArgMatches for Params #ty_generics #where_clause { - fn from_arg_matches(matches: &::rpc_toolkit::command_helpers::clap::ArgMatches) -> Result { - let clap_args = ClapParams::from_arg_matches(matches)?; - Ok(Self { - #( - #clap_param_from_matches - )* - }) - } - fn update_from_arg_matches(&mut self, matches: &::rpc_toolkit::command_helpers::clap::ArgMatches) -> Result<(), ::rpc_toolkit::command_helpers::clap::Error> { - unimplemented!() - } - } - impl #impl_generics ::rpc_toolkit::command_helpers::clap::CommandFactory for Params #ty_generics #where_clause { - fn command() -> ::rpc_toolkit::command_helpers::clap::Command { - ClapParams::command() - } - fn command_for_update() -> ::rpc_toolkit::command_helpers::clap::Command { - ClapParams::command_for_update() - } - } - }, - param_generics, - ) -} - -fn build_inherited(parent_data: Option, generics: &Generics) -> (TokenStream, Generics) { - let mut inherited_generics_filter = GenericFilter::new(generics); - if let Some(inherited) = &parent_data { - inherited_generics_filter.fold_type(inherited.clone()); - } - let inherited_generics = inherited_generics_filter.finish(); - if let Some(inherited) = parent_data { - let (_, ty_generics, _) = inherited_generics.split_for_impl(); - ( - quote! { - type InheritedParams #ty_generics = #inherited; - }, - inherited_generics, - ) - } else { - ( - quote! { type InheritedParams = ::rpc_toolkit::Empty; }, - inherited_generics, - ) - } -} - -pub fn build(args: AttributeArgs, mut item: ItemFn) -> TokenStream { - let params = macro_try!(parse_param_attrs(&mut item)); - let mut opt = macro_try!(parse_command_attr(args)); - let fn_vis = &item.vis; - let fn_name = &item.sig.ident; - let (params_impl, params_generics) = build_params( - params - .iter() - .filter_map(|a| { - if let ParamType::Arg(a) = a { - Some(a.clone()) - } else { - None - } - }) - .collect(), - &item.sig.generics, - ); - let (_, params_ty_generics, _) = params_generics.split_for_impl(); - let (inherited_impl, inherited_generics) = build_inherited( - params.iter().find_map(|a| { - if let ParamType::ParentData(a) = a { - Some(a.clone()) - } else { - None - } - }), - &item.sig.generics, - ); - let (_, inherited_ty_generics, _) = inherited_generics.split_for_impl(); - let mut params_and_inherited_generics_filter = GenericFilter::new(&item.sig.generics); - params_and_inherited_generics_filter.fold_generics(params_generics.clone()); - params_and_inherited_generics_filter.fold_generics(inherited_generics.clone()); - let params_and_inherited_generics = params_and_inherited_generics_filter.finish(); - let (_, params_and_inherited_ty_generics, _) = params_and_inherited_generics.split_for_impl(); - let phantom_ty = params_and_inherited_generics - .type_params() - .map(|t| &t.ident) - .map(|t| quote! { #t }) - .chain( - params_and_inherited_generics - .lifetimes() - .map(|l| &l.lifetime) - .map(|l| quote! { &#l () }), - ); - let phantom_ty = quote! { ( #( #phantom_ty, )* ) }; - let module = if let Options::Parent(parent) = &opt { - // let subcommands = parent.subcommands.iter().map(|c| quote! { .subcommand_with_inherited_remote_cli(c::handler(), |a, b| ) }) - // quote! { - // pub type Handler #params_and_inherited_ty_generics = ::rpc_toolkit::ParentHandler; - // #params_impl - // #inherited_impl - // pub fn handler #params_and_inherited_ty_generics () -> Handler #params_and_inherited_ty_generics { - // Handler::new() - // #( - // #subcommands - // )* - // } - // } - Error::new(Span::call_site(), "derived parent handlers not implemented").to_compile_error() - } else { - let (ok_ty, err_ty) = match &item.sig.output { - ReturnType::Type(_, p) => match &**p { - Type::Path(p) if p.path.segments.last().unwrap().ident == "Result" => { - match &p.path.segments.last().unwrap().arguments { - PathArguments::AngleBracketed(a) if a.args.len() == 2 => { - match (a.args.first(), a.args.last()) { - ( - Some(GenericArgument::Type(ok)), - Some(GenericArgument::Type(err)), - ) => (ok, err), - _ => { - return Error::new(a.span(), "return type must be a Result") - .to_compile_error() - } - } - } - a => { - return Error::new(a.span(), "return type must be a Result") - .to_compile_error() - } - } - } - a => { - return Error::new(a.span(), "return type must be a Result").to_compile_error(); - } - }, - a => return Error::new(a.span(), "return type must be a Result").to_compile_error(), - }; - let handler_impl: TokenStream = todo!(); - let cli_bindings_impl: TokenStream = todo!(); - quote! { - pub struct Handler #params_and_inherited_ty_generics (::core::marker::PhantomData<#phantom_ty>); - impl #params_and_inherited_ty_generics ::core::fmt::Debug for Handler #params_and_inherited_ty_generics { - fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { - f.debug_tuple("Handler") - .finish() - } - } - impl #params_and_inherited_ty_generics ::core::clone::Clone for Handler #params_and_inherited_ty_generics { - fn clone(&self) -> Self { - Self(::core::marker::PhantomData) - } - } - impl #params_and_inherited_ty_generics ::core::marker::Copy for Handler #params_and_inherited_ty_generics { } - #params_impl - #inherited_impl - impl #params_and_inherited_ty_generics ::rpc_toolkit::HandlerTypes for Handler #params_and_inherited_ty_generics { - type Params = Params; - type InheritedParams = InheritedParams; - type Ok = #ok_ty; - type Err = #err_ty; - } - #handler_impl - #cli_bindings_impl - pub fn handler #params_and_inherited_ty_generics () -> Handler #params_and_inherited_ty_generics { - Handler(::core::marker::PhantomData) - } - } - }; - - let fn_rename = opt - .common() - .rename - .clone() - .unwrap_or(LitStr::new(&fn_name.to_string(), fn_name.span())); - - let res = quote! { - #item - #fn_vis mod #fn_name { - use super::*; - pub const NAME: &str = #fn_rename; - #module - } - }; - if let Some(debug) = &opt.common().macro_debug { - Error::new(debug.span(), res).into_compile_error() - } else { - res - } -} diff --git a/rpc-toolkit-macro-internals/src/command/mod.rs b/rpc-toolkit-macro-internals/src/command/mod.rs deleted file mode 100644 index 662064a..0000000 --- a/rpc-toolkit-macro-internals/src/command/mod.rs +++ /dev/null @@ -1,102 +0,0 @@ -use std::collections::HashMap; - -use syn::*; - -pub mod build; -mod parse; - -pub enum ExecutionContext { - Standard, - CliOnly(Path), - RpcOnly(Path), - Local(Path), - CustomCli { - custom: Path, - cli: Path, - context: Type, - is_async: bool, - }, -} -impl Default for ExecutionContext { - fn default() -> Self { - ExecutionContext::Standard - } -} - -#[derive(Default)] -pub struct LeafOptions { - macro_debug: Option, - blocking: Option, - is_async: bool, - aliases: Vec, - about: Option, - rename: Option, - exec_ctx: ExecutionContext, - display: Option, - metadata: HashMap, - clap_attr: Vec, -} - -pub struct SelfImplInfo { - path: Path, - context: Type, - is_async: bool, - blocking: bool, -} -pub struct ParentOptions { - common: LeafOptions, - subcommands: Vec, - self_impl: Option, -} -impl From for ParentOptions { - fn from(opt: LeafOptions) -> Self { - ParentOptions { - common: opt, - subcommands: Default::default(), - self_impl: Default::default(), - } - } -} - -pub enum Options { - Leaf(LeafOptions), - Parent(ParentOptions), -} -impl Options { - fn to_parent(&mut self) -> Result<&mut ParentOptions> { - if let Options::Leaf(opt) = self { - *self = Options::Parent(std::mem::replace(opt, Default::default()).into()); - } - Ok(match self { - Options::Parent(a) => a, - _ => unreachable!(), - }) - } - fn common(&mut self) -> &mut LeafOptions { - match self { - Options::Leaf(ref mut opt) => opt, - Options::Parent(opt) => &mut opt.common, - } - } -} - -#[derive(Clone)] -pub struct ArgOptions { - ty: Type, - name: Option, - rename: Option, - parse: Option, - stdin: Option, - default: Option, - clap_attr: Vec, -} - -#[derive(Clone)] -pub enum ParamType { - None, - Arg(ArgOptions), - Context(Type), - ParentData(Type), - Request, - Response, -} diff --git a/rpc-toolkit-macro-internals/src/command/parse.rs b/rpc-toolkit-macro-internals/src/command/parse.rs deleted file mode 100644 index c504b7d..0000000 --- a/rpc-toolkit-macro-internals/src/command/parse.rs +++ /dev/null @@ -1,851 +0,0 @@ -use syn::spanned::Spanned; - -use super::*; - -pub fn parse_command_attr(args: AttributeArgs) -> Result { - let mut opt = Options::Leaf(Default::default()); - for arg in args { - match arg { - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("macro_debug") => { - opt.common().macro_debug = Some(p); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("subcommands") => { - let inner = opt.to_parent()?; - if !inner.subcommands.is_empty() { - return Err(Error::new(list.span(), "duplicate argument `subcommands`")); - } - for subcmd in list.nested { - match subcmd { - NestedMeta::Meta(Meta::Path(subcmd)) => inner.subcommands.push(subcmd), - NestedMeta::Lit(Lit::Str(s)) => { - inner.subcommands.push(syn::parse_str(&s.value())?) - } - NestedMeta::Meta(Meta::List(mut self_impl)) - if self_impl.path.is_ident("self") => - { - if self_impl.nested.len() == 1 { - match self_impl.nested.pop().unwrap().into_value() { - NestedMeta::Meta(Meta::Path(self_impl)) => { - if inner.self_impl.is_some() { - return Err(Error::new( - self_impl.span(), - "duplicate argument `self`", - )); - } - inner.self_impl = Some(SelfImplInfo { - path: self_impl, - context: parse2(quote::quote! { () }).unwrap(), - is_async: false, - blocking: false, - }) - } - NestedMeta::Meta(Meta::List(l)) => { - if inner.self_impl.is_some() { - return Err(Error::new( - self_impl.span(), - "duplicate argument `self`", - )); - } - let mut is_async = false; - let mut blocking = false; - let mut context: Type = - parse2(quote::quote! { () }).unwrap(); - for meta in &l.nested { - match meta { - NestedMeta::Meta(Meta::Path(p)) - if p.is_ident("async") => - { - is_async = true; - if blocking { - return Err(Error::new( - p.span(), - "`async` and `blocking` are mutually exclusive", - )); - } - } - NestedMeta::Meta(Meta::Path(p)) - if p.is_ident("blocking") => - { - blocking = true; - if is_async { - return Err(Error::new( - p.span(), - "`async` and `blocking` are mutually exclusive", - )); - } - } - NestedMeta::Meta(Meta::List(p)) - if p.path.is_ident("context") => - { - context = if let Some(NestedMeta::Meta( - Meta::Path(context), - )) = p.nested.first() - { - Type::Path(TypePath { - path: context.clone(), - qself: None, - }) - } else { - return Err(Error::new( - p.span(), - "`context` requires a path argument", - )); - } - } - arg => { - return Err(Error::new( - arg.span(), - "unknown argument", - )) - } - } - } - inner.self_impl = Some(SelfImplInfo { - path: l.path, - context, - is_async, - blocking, - }) - } - a => { - return Err(Error::new( - a.span(), - "`self` implementation must be a path, or a list", - )) - } - } - } else { - return Err(Error::new( - self_impl.nested.span(), - "`self` can only have one implementation", - )); - } - } - arg => { - return Err(Error::new(arg.span(), "unknown argument to `subcommands`")) - } - } - } - if inner.subcommands.is_empty() { - return Err(Error::new( - list.path.span(), - "`subcommands` requires at least 1 argument", - )); - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("subcommands") => { - return Err(Error::new( - p.span(), - "`subcommands` requires at least 1 argument", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("subcommands") => { - return Err(Error::new( - nv.path.span(), - "`subcommands` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("display") => { - if list.nested.len() == 1 { - match &list.nested[0] { - NestedMeta::Meta(Meta::Path(display_impl)) => { - if opt.common().display.is_some() { - return Err(Error::new( - display_impl.span(), - "duplicate argument `display`", - )); - } - opt.common().display = Some(display_impl.clone()) - } - a => { - return Err(Error::new( - a.span(), - "`display` implementation must be a path", - )) - } - } - } else { - return Err(Error::new( - list.nested.span(), - "`display` can only have one implementation", - )); - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("display") => { - return Err(Error::new(p.span(), "`display` requires an argument")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("display") => { - return Err(Error::new( - nv.path.span(), - "`display` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("custom_cli") => { - if list.nested.len() == 1 { - match &opt.common().exec_ctx { - ExecutionContext::Standard => match &list.nested[0] { - NestedMeta::Meta(Meta::Path(custom_cli_impl)) => { - opt.common().exec_ctx = ExecutionContext::CustomCli { - custom: list.path, - context: parse2(quote::quote!{ () }).unwrap(), - cli: custom_cli_impl.clone(), - is_async: false, - } - } - NestedMeta::Meta(Meta::List(custom_cli_impl)) => { - let mut is_async = false; - let mut context: Type = parse2(quote::quote! { () }).unwrap(); - for meta in &custom_cli_impl.nested { - match meta { - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("async") => is_async = true, - NestedMeta::Meta(Meta::List(p)) if p.path.is_ident("context") => { - context = if let Some(NestedMeta::Meta(Meta::Path(context))) = p.nested.first() { - Type::Path(TypePath { - path: context.clone(), - qself: None, - }) - } else { - return Err(Error::new(p.span(), "`context` requires a path argument")); - } - } - arg => return Err(Error::new(arg.span(), "unknown argument")), - } - } - opt.common().exec_ctx = ExecutionContext::CustomCli { - custom: list.path, - context, - cli: custom_cli_impl.path.clone(), - is_async, - }; - } - a => { - return Err(Error::new( - a.span(), - "`custom_cli` implementation must be a path, or a list with 1 argument", - )) - } - }, - ExecutionContext::CliOnly(_) => { - return Err(Error::new(list.span(), "duplicate argument: `cli_only`")) - } - ExecutionContext::RpcOnly(_) => { - return Err(Error::new( - list.span(), - "`cli_only` and `rpc_only` are mutually exclusive", - )) - } - ExecutionContext::Local(_) => { - return Err(Error::new( - list.span(), - "`cli_only` and `local` are mutually exclusive", - )) - } - ExecutionContext::CustomCli { .. } => { - return Err(Error::new( - list.span(), - "`cli_only` and `custom_cli` are mutually exclusive", - )) - } - } - } else { - return Err(Error::new( - list.nested.span(), - "`custom_cli` can only have one implementation", - )); - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("custom_cli") => { - return Err(Error::new(p.span(), "`custom_cli` requires an argument")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("custom_cli") => { - return Err(Error::new( - nv.path.span(), - "`custom_cli` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("aliases") => { - if !opt.common().aliases.is_empty() { - return Err(Error::new(list.span(), "duplicate argument `alias`")); - } - for nested in list.nested { - match nested { - NestedMeta::Lit(Lit::Str(alias)) => opt.common().aliases.push(alias), - a => return Err(Error::new(a.span(), "`alias` must be a string")), - } - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("alias") => { - return Err(Error::new(p.span(), "`alias` requires an argument")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("alias") => { - if !opt.common().aliases.is_empty() { - return Err(Error::new(nv.path.span(), "duplicate argument `alias`")); - } - if let Lit::Str(alias) = nv.lit { - opt.common().aliases.push(alias); - } else { - return Err(Error::new(nv.lit.span(), "`alias` must be a string")); - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("cli_only") => { - match &opt.common().exec_ctx { - ExecutionContext::Standard => { - opt.common().exec_ctx = ExecutionContext::CliOnly(p) - } - ExecutionContext::CliOnly(_) => { - return Err(Error::new(p.span(), "duplicate argument: `cli_only`")) - } - ExecutionContext::RpcOnly(_) => { - return Err(Error::new( - p.span(), - "`cli_only` and `rpc_only` are mutually exclusive", - )) - } - ExecutionContext::Local(_) => { - return Err(Error::new( - p.span(), - "`cli_only` and `local` are mutually exclusive", - )) - } - ExecutionContext::CustomCli { .. } => { - return Err(Error::new( - p.span(), - "`cli_only` and `custom_cli` are mutually exclusive", - )) - } - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("cli_only") => { - return Err(Error::new( - list.path.span(), - "`cli_only` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("cli_only") => { - return Err(Error::new( - nv.path.span(), - "`cli_only` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("rpc_only") => { - match &opt.common().exec_ctx { - ExecutionContext::Standard => { - opt.common().exec_ctx = ExecutionContext::RpcOnly(p) - } - ExecutionContext::RpcOnly(_) => { - return Err(Error::new(p.span(), "duplicate argument: `rpc_only`")) - } - ExecutionContext::CliOnly(_) => { - return Err(Error::new( - p.span(), - "`rpc_only` and `cli_only` are mutually exclusive", - )) - } - ExecutionContext::Local(_) => { - return Err(Error::new( - p.span(), - "`rpc_only` and `local` are mutually exclusive", - )) - } - ExecutionContext::CustomCli { .. } => { - return Err(Error::new( - p.span(), - "`rpc_only` and `custom_cli` are mutually exclusive", - )) - } - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("rpc_only") => { - return Err(Error::new( - list.path.span(), - "`rpc_only` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rpc_only") => { - return Err(Error::new( - nv.path.span(), - "`rpc_only` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("local") => { - match &opt.common().exec_ctx { - ExecutionContext::Standard => { - opt.common().exec_ctx = ExecutionContext::Local(p) - } - ExecutionContext::Local(_) => { - return Err(Error::new(p.span(), "duplicate argument: `local`")) - } - ExecutionContext::RpcOnly(_) => { - return Err(Error::new( - p.span(), - "`local` and `rpc_only` are mutually exclusive", - )) - } - ExecutionContext::CliOnly(_) => { - return Err(Error::new( - p.span(), - "`local` and `cli_only` are mutually exclusive", - )) - } - ExecutionContext::CustomCli { .. } => { - return Err(Error::new( - p.span(), - "`local` and `custom_cli` are mutually exclusive", - )) - } - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("local") => { - return Err(Error::new( - list.path.span(), - "`local` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("local") => { - return Err(Error::new(nv.path.span(), "`local` cannot be assigned to")); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("blocking") => { - if opt.common().blocking.is_some() { - return Err(Error::new(p.span(), "duplicate argument `blocking`")); - } - opt.common().blocking = Some(p); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("blocking") => { - return Err(Error::new( - list.path.span(), - "`blocking` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("blocking") => { - return Err(Error::new( - nv.path.span(), - "`blocking` cannot be assigned to", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("about") => { - if let Lit::Str(about) = nv.lit { - if opt.common().about.is_some() { - return Err(Error::new(about.span(), "duplicate argument `about`")); - } - opt.common().about = Some(about); - } else { - return Err(Error::new(nv.lit.span(), "about message must be a string")); - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("about") => { - return Err(Error::new( - list.path.span(), - "`about` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("about") => { - return Err(Error::new(p.span(), "`about` must be assigned to")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rename") => { - if let Lit::Str(rename) = nv.lit { - if opt.common().rename.is_some() { - return Err(Error::new(rename.span(), "duplicate argument `rename`")); - } - opt.common().rename = Some(rename); - } else { - return Err(Error::new(nv.lit.span(), "`rename` must be a string")); - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("rename") => { - return Err(Error::new( - list.path.span(), - "`rename` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("rename") => { - return Err(Error::new(p.span(), "`rename` must be assigned to")); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("metadata") => { - for meta in list.nested { - match meta { - NestedMeta::Meta(Meta::NameValue(metadata_pair)) => { - let ident = metadata_pair.path.get_ident().ok_or(Error::new( - metadata_pair.path.span(), - "must be an identifier", - ))?; - if opt - .common() - .metadata - .insert(ident.clone(), metadata_pair.lit) - .is_some() - { - return Err(Error::new( - ident.span(), - format!("duplicate metadata `{}`", ident), - )); - } - } - a => { - return Err(Error::new( - a.span(), - "`metadata` takes a list of identifiers to be assigned to", - )) - } - } - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("metadata") => { - return Err(Error::new( - p.span(), - "`metadata` takes a list of identifiers to be assigned to", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("metadata") => { - return Err(Error::new( - nv.path.span(), - "`metadata` cannot be assigned to", - )); - } - _ => { - return Err(Error::new(arg.span(), "unknown argument")); - } - } - } - if let Options::Parent(opt) = &opt { - if opt.self_impl.is_none() { - if let Some(display) = &opt.common.display { - return Err(Error::new( - display.span(), - "cannot define `display` for a command without an implementation", - )); - } - match &opt.common.exec_ctx { - ExecutionContext::CliOnly(cli_only) => { - return Err(Error::new( - cli_only.span(), - "cannot define `cli_only` for a command without an implementation", - )) - } - ExecutionContext::RpcOnly(rpc_only) => { - return Err(Error::new( - rpc_only.span(), - "cannot define `rpc_only` for a command without an implementation", - )) - } - _ => (), - } - } - } - Ok(opt) -} - -pub fn parse_arg_attr(attr: Attribute, arg: PatType) -> Result { - let arg_span = arg.span(); - let meta = attr.parse_meta()?; - let mut opt = ArgOptions { - ty: *arg.ty, - name: match *arg.pat { - Pat::Ident(i) => Some(i.ident), - _ => None, - }, - rename: None, - parse: None, - stdin: None, - default: None, - clap_attr: Vec::new(), - }; - match meta { - Meta::List(list) => { - for arg in list.nested { - match arg { - NestedMeta::Meta(Meta::List(mut list)) if list.path.is_ident("parse") => { - if list.nested.len() == 1 { - match list.nested.pop().unwrap().into_value() { - NestedMeta::Meta(Meta::Path(parse_impl)) => { - if opt.parse.is_some() { - return Err(Error::new( - list.span(), - "duplicate argument `parse`", - )); - } - opt.parse = Some(parse_impl) - } - a => { - return Err(Error::new( - a.span(), - "`parse` implementation must be a path", - )) - } - } - } else { - return Err(Error::new( - list.nested.span(), - "`parse` can only have one implementation", - )); - } - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("parse") => { - return Err(Error::new(p.span(), "`parse` requires an argument")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("parse") => { - return Err(Error::new(nv.path.span(), "`parse` cannot be assigned to")); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("stdin") => { - if !opt.clap_attr.is_empty() { - return Err(Error::new( - p.span(), - "`stdin` and clap parser attributes are mutually exclusive", - )); - } - opt.stdin = Some(p); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("stdin") => { - return Err(Error::new( - list.path.span(), - "`stdin` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("stdin") => { - return Err(Error::new(nv.path.span(), "`stdin` cannot be assigned to")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("rename") => { - if let Lit::Str(rename) = nv.lit { - if opt.rename.is_some() { - return Err(Error::new( - rename.span(), - "duplicate argument `rename`", - )); - } - opt.clap_attr - .push(NestedMeta::Meta(Meta::NameValue(MetaNameValue { - path: Path::from(Ident::new("name", nv.path.span())), - eq_token: nv.eq_token, - lit: Lit::Str(rename.clone()), - }))); - opt.rename = Some(rename); - } else { - return Err(Error::new(nv.lit.span(), "`rename` must be a string")); - } - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("rename") => { - return Err(Error::new( - list.path.span(), - "`rename` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("rename") => { - return Err(Error::new(p.span(), "`rename` must be assigned to")); - } - NestedMeta::Meta(Meta::NameValue(nv)) if nv.path.is_ident("default") => { - return Err(Error::new(nv.lit.span(), "`default` cannot be assigned to")); - } - NestedMeta::Meta(Meta::List(list)) if list.path.is_ident("default") => { - return Err(Error::new( - list.path.span(), - "`default` does not take any arguments", - )); - } - NestedMeta::Meta(Meta::Path(p)) if p.is_ident("default") => { - opt.clap_attr - .push(NestedMeta::Meta(Meta::Path(Path::from(Ident::new( - "default_value_t", - p.span(), - ))))); - opt.default = Some(p); - } - unknown => { - if opt.stdin.is_some() { - return Err(Error::new( - unknown.span(), - "`stdin` and clap parser attributes are mutually exclusive", - )); - } - opt.clap_attr.push(unknown); - } - } - } - } - Meta::Path(_) => (), - Meta::NameValue(nv) => return Err(Error::new(nv.span(), "`arg` cannot be assigned to")), - } - if opt.name.is_none() { - return Err(Error::new( - arg_span, - "cannot infer name for pattern argument", - )); - } - Ok(opt) -} - -pub fn parse_param_attrs(item: &mut ItemFn) -> Result> { - let mut params = Vec::new(); - for param in item.sig.inputs.iter_mut() { - if let FnArg::Typed(param) = param { - let mut ty = ParamType::None; - let mut i = 0; - while i != param.attrs.len() { - if param.attrs[i].path.is_ident("arg") { - let attr = param.attrs.remove(i); - if matches!(ty, ParamType::None) { - ty = ParamType::Arg(parse_arg_attr(attr, param.clone())?); - } else if matches!(ty, ParamType::Arg(_)) { - return Err(Error::new( - attr.span(), - "`arg` attribute may only be specified once", - )); - } else if matches!(ty, ParamType::Context(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `context` are mutually exclusive", - )); - } else if matches!(ty, ParamType::ParentData(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `parent_data` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Request) { - return Err(Error::new( - attr.span(), - "`arg` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Response) { - return Err(Error::new( - attr.span(), - "`arg` and `response` are mutually exclusive", - )); - } - } else if param.attrs[i].path.is_ident("context") { - let attr = param.attrs.remove(i); - if matches!(ty, ParamType::None) { - ty = ParamType::Context(*param.ty.clone()); - } else if matches!(ty, ParamType::Context(_)) { - return Err(Error::new( - attr.span(), - "`context` attribute may only be specified once", - )); - } else if matches!(ty, ParamType::Arg(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `context` are mutually exclusive", - )); - } else if matches!(ty, ParamType::ParentData(_)) { - return Err(Error::new( - attr.span(), - "`context` and `parent_data` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Request) { - return Err(Error::new( - attr.span(), - "`context` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Response) { - return Err(Error::new( - attr.span(), - "`context` and `response` are mutually exclusive", - )); - } - } else if param.attrs[i].path.is_ident("parent_data") { - let attr = param.attrs.remove(i); - if matches!(ty, ParamType::None) { - ty = ParamType::ParentData(*param.ty.clone()); - } else if matches!(ty, ParamType::ParentData(_)) { - return Err(Error::new( - attr.span(), - "`parent_data` attribute may only be specified once", - )); - } else if matches!(ty, ParamType::Arg(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `parent_data` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Context(_)) { - return Err(Error::new( - attr.span(), - "`context` and `parent_data` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Request) { - return Err(Error::new( - attr.span(), - "`parent_data` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Response) { - return Err(Error::new( - attr.span(), - "`parent_data` and `response` are mutually exclusive", - )); - } - } else if param.attrs[i].path.is_ident("request") { - let attr = param.attrs.remove(i); - if matches!(ty, ParamType::None) { - ty = ParamType::Request; - } else if matches!(ty, ParamType::Request) { - return Err(Error::new( - attr.span(), - "`request` attribute may only be specified once", - )); - } else if matches!(ty, ParamType::Arg(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Context(_)) { - return Err(Error::new( - attr.span(), - "`context` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::ParentData(_)) { - return Err(Error::new( - attr.span(), - "`parent_data` and `request` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Response) { - return Err(Error::new( - attr.span(), - "`request` and `response` are mutually exclusive", - )); - } - } else if param.attrs[i].path.is_ident("response") { - let attr = param.attrs.remove(i); - if matches!(ty, ParamType::None) { - ty = ParamType::Response; - } else if matches!(ty, ParamType::Response) { - return Err(Error::new( - attr.span(), - "`response` attribute may only be specified once", - )); - } else if matches!(ty, ParamType::Arg(_)) { - return Err(Error::new( - attr.span(), - "`arg` and `response` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Context(_)) { - return Err(Error::new( - attr.span(), - "`context` and `response` are mutually exclusive", - )); - } else if matches!(ty, ParamType::ParentData(_)) { - return Err(Error::new( - attr.span(), - "`parent_data` and `response` are mutually exclusive", - )); - } else if matches!(ty, ParamType::Request) { - return Err(Error::new( - attr.span(), - "`request` and `response` are mutually exclusive", - )); - } - } else { - i += 1; - } - } - if matches!(ty, ParamType::None) { - return Err(Error::new( - param.span(), - "must specify either `arg`, `context`, `parent_data`, `request`, or `response` attributes", - )); - } - params.push(ty) - } else { - return Err(Error::new( - param.span(), - "commands may not take `self` as an argument", - )); - } - } - Ok(params) -} diff --git a/rpc-toolkit-macro-internals/src/lib.rs b/rpc-toolkit-macro-internals/src/lib.rs deleted file mode 100644 index 2bbe0cb..0000000 --- a/rpc-toolkit-macro-internals/src/lib.rs +++ /dev/null @@ -1,12 +0,0 @@ -macro_rules! macro_try { - ($x:expr) => { - match $x { - Ok(a) => a, - Err(e) => return e.to_compile_error(), - } - }; -} - -mod command; - -pub use command::build::build as build_command; diff --git a/rpc-toolkit-macro/.gitignore b/rpc-toolkit-macro/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/rpc-toolkit-macro/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/rpc-toolkit-macro/Cargo.toml b/rpc-toolkit-macro/Cargo.toml deleted file mode 100644 index 4be093c..0000000 --- a/rpc-toolkit-macro/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -authors = ["Aiden McClelland "] -edition = "2018" -name = "rpc-toolkit-macro" -version = "0.2.2" -description = "macros for rpc-toolkit" -license = "MIT" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = "1.0" -rpc-toolkit-macro-internals = { version = "=0.2.2", path = "../rpc-toolkit-macro-internals" } -syn = "1.0" diff --git a/rpc-toolkit-macro/src/lib.rs b/rpc-toolkit-macro/src/lib.rs deleted file mode 100644 index 63965e3..0000000 --- a/rpc-toolkit-macro/src/lib.rs +++ /dev/null @@ -1,44 +0,0 @@ -use proc_macro::{Span, TokenStream}; -use rpc_toolkit_macro_internals::*; - -#[proc_macro_attribute] -pub fn command(args: TokenStream, item: TokenStream) -> TokenStream { - let args = syn::parse_macro_input!(args as syn::AttributeArgs); - let item = syn::parse_macro_input!(item as syn::ItemFn); - build_command(args, item).into() -} - -/// `#[arg(...)]` -> Take this argument as a parameter -/// - `#[arg(help = "Help text")]` -> Set help text for the arg -/// - `#[arg(alias = "ls")]` -> Set the alias `ls` in the CLI -/// - `#[arg(aliases("show", "ls"))]` -> Set the aliases `ls` and `show` in the CLI -/// - `#[arg(rename = "new_name")]` -> Set the name of the arg to `new_name` in the RPC and CLI -/// - `#[arg(short = "a")]` -> Set the "short" representation of the arg to `-a` on the CLI -/// - `#[arg(long = "arg")]` -> Set the "long" representation of the arg to `--arg` on the CLI -/// - `#[arg(parse(custom_parse_fn))]` -> Use the function `custom_parse_fn` to parse the arg from the CLI -/// - `custom_parse_fn :: Into err => (&str, &ArgMatches) -> Result` -/// - note: `arg` is the type of the argument -/// - `#[arg(stdin)]` -> Parse the argument from stdin when using the CLI -/// - `custom_parse_fn :: Into err => (&[u8], &ArgMatches) -> Result` -/// - `#[arg(count)]` -> Treat argument as flag, count occurrences -/// - `#[arg(multiple)]` -> Allow the arg to be specified multiple times. Collect the args after parsing. -#[proc_macro_attribute] -pub fn arg(_: TokenStream, _: TokenStream) -> TokenStream { - syn::Error::new( - Span::call_site().into(), - "`arg` is only allowed on arguments of a function with the `command` attribute", - ) - .to_compile_error() - .into() -} - -/// - `#[context]` -> Passes the application context into this parameter -#[proc_macro_attribute] -pub fn context(_: TokenStream, _: TokenStream) -> TokenStream { - syn::Error::new( - Span::call_site().into(), - "`context` is only allowed on arguments of a function with the `command` attribute", - ) - .to_compile_error() - .into() -} diff --git a/rpc-toolkit/.gitignore b/rpc-toolkit/.gitignore deleted file mode 100644 index 96ef6c0..0000000 --- a/rpc-toolkit/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/target -Cargo.lock diff --git a/rpc-toolkit/Cargo.toml b/rpc-toolkit/Cargo.toml deleted file mode 100644 index 839835f..0000000 --- a/rpc-toolkit/Cargo.toml +++ /dev/null @@ -1,42 +0,0 @@ -[package] -authors = ["Aiden McClelland "] -edition = "2018" -name = "rpc-toolkit" -version = "0.2.3" -description = "A toolkit for creating JSON-RPC 2.0 servers with automatic cli bindings" -license = "MIT" -documentation = "https://docs.rs/rpc-toolkit" -keywords = ["json", "rpc", "cli"] -repository = "https://github.com/Start9Labs/rpc-toolkit" - - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[features] -cbor = ["serde_cbor"] -default = [] - -[dependencies] -axum = "0.7.3" -async-stream = "0.3" -async-trait = "0.1" -clap = { version = "4", features = ["derive"] } -futures = "0.3" -http = "1" -http-body-util = "0.1" -# hyper = { version = "1", features = ["server", "http1", "http2", "client"] } -itertools = "0.12" -imbl-value = { git = "https://github.com/Start9Labs/imbl-value.git" } -lazy_format = "2" -lazy_static = "1.4" -openssl = { version = "0.10", features = ["vendored"] } -pin-project = "1" -reqwest = { version = "0.11" } -rpc-toolkit-macro = { version = "0.2.2", path = "../rpc-toolkit-macro" } -serde = { version = "1.0", features = ["derive"] } -serde_cbor = { version = "0.11", optional = true } -serde_json = "1.0" -thiserror = "1.0" -tokio = { version = "1", features = ["full"] } -tokio-stream = { version = "0.1", features = ["io-util", "net"] } -url = "2" -yajrc = "0.1" diff --git a/rpc-toolkit/src/cli.rs b/src/cli.rs similarity index 100% rename from rpc-toolkit/src/cli.rs rename to src/cli.rs diff --git a/rpc-toolkit/src/command_helpers.rs b/src/command_helpers.rs similarity index 100% rename from rpc-toolkit/src/command_helpers.rs rename to src/command_helpers.rs diff --git a/rpc-toolkit/src/context.rs b/src/context.rs similarity index 100% rename from rpc-toolkit/src/context.rs rename to src/context.rs diff --git a/rpc-toolkit/src/handler/adapters.rs b/src/handler/adapters.rs similarity index 100% rename from rpc-toolkit/src/handler/adapters.rs rename to src/handler/adapters.rs diff --git a/rpc-toolkit/src/handler/from_fn.rs b/src/handler/from_fn.rs similarity index 100% rename from rpc-toolkit/src/handler/from_fn.rs rename to src/handler/from_fn.rs diff --git a/rpc-toolkit/src/handler/mod.rs b/src/handler/mod.rs similarity index 100% rename from rpc-toolkit/src/handler/mod.rs rename to src/handler/mod.rs diff --git a/rpc-toolkit/src/handler/parent.rs b/src/handler/parent.rs similarity index 100% rename from rpc-toolkit/src/handler/parent.rs rename to src/handler/parent.rs diff --git a/rpc-toolkit/src/lib.rs b/src/lib.rs similarity index 100% rename from rpc-toolkit/src/lib.rs rename to src/lib.rs diff --git a/rpc-toolkit/src/metadata.rs b/src/metadata.rs similarity index 100% rename from rpc-toolkit/src/metadata.rs rename to src/metadata.rs diff --git a/rpc-toolkit/src/server/http.rs b/src/server/http.rs similarity index 100% rename from rpc-toolkit/src/server/http.rs rename to src/server/http.rs diff --git a/rpc-toolkit/src/server/mod.rs b/src/server/mod.rs similarity index 100% rename from rpc-toolkit/src/server/mod.rs rename to src/server/mod.rs diff --git a/rpc-toolkit/src/server/socket.rs b/src/server/socket.rs similarity index 100% rename from rpc-toolkit/src/server/socket.rs rename to src/server/socket.rs diff --git a/rpc-toolkit/src/util.rs b/src/util.rs similarity index 100% rename from rpc-toolkit/src/util.rs rename to src/util.rs diff --git a/rpc-toolkit/tests/handler.rs b/tests/handler.rs similarity index 100% rename from rpc-toolkit/tests/handler.rs rename to tests/handler.rs diff --git a/rpc-toolkit/tests/test.rs b/tests/test.rs similarity index 100% rename from rpc-toolkit/tests/test.rs rename to tests/test.rs From 2b97135a2b318604bb51908d4409e6103640d2d7 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 13:35:33 -0600 Subject: [PATCH 60/79] update Cargo.toml --- Cargo.lock | 208 ++++++++++++++++++----------------------------------- Cargo.toml | 5 +- 2 files changed, 71 insertions(+), 142 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 66c0f6a..18e6099 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,7 +103,7 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -114,7 +114,7 @@ checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -133,10 +133,10 @@ dependencies = [ "axum-core", "bytes", "futures-util", - "http 1.0.0", - "http-body 1.0.0", + "http", + "http-body", "http-body-util", - "hyper 1.1.0", + "hyper", "hyper-util", "itoa", "matchit", @@ -166,8 +166,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.0.0", - "http-body 1.0.0", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", @@ -195,9 +195,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" @@ -275,7 +275,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -429,7 +429,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -479,25 +479,6 @@ version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -[[package]] -name = "h2" -version = "0.3.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.11", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "h2" version = "0.4.2" @@ -509,7 +490,7 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http 1.0.0", + "http", "indexmap", "slab", "tokio", @@ -550,17 +531,6 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" -[[package]] -name = "http" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.0.0" @@ -572,17 +542,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.11", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.0" @@ -590,7 +549,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" dependencies = [ "bytes", - "http 1.0.0", + "http", ] [[package]] @@ -601,8 +560,8 @@ checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" dependencies = [ "bytes", "futures-util", - "http 1.0.0", - "http-body 1.0.0", + "http", + "http-body", "pin-project-lite", ] @@ -618,30 +577,6 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -[[package]] -name = "hyper" -version = "0.14.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.24", - "http 0.2.11", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.1.0" @@ -651,44 +586,50 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.2", - "http 1.0.0", - "http-body 1.0.0", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", "tokio", + "want", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.28", + "http-body-util", + "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] name = "hyper-util" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.0.0", - "http-body 1.0.0", - "hyper 1.1.0", + "http", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", + "tower", + "tower-service", "tracing", ] @@ -920,7 +861,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -997,7 +938,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -1062,20 +1003,22 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.23" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" dependencies = [ "base64", "bytes", "encoding_rs", "futures-core", "futures-util", - "h2 0.3.24", - "http 0.2.11", - "http-body 0.4.6", - "hyper 0.14.28", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -1084,9 +1027,11 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -1107,7 +1052,7 @@ dependencies = [ "axum", "clap", "futures", - "http 1.0.0", + "http", "http-body-util", "imbl-value", "itertools", @@ -1116,7 +1061,6 @@ dependencies = [ "openssl", "pin-project", "reqwest", - "rpc-toolkit-macro", "serde", "serde_cbor", "serde_json", @@ -1127,25 +1071,6 @@ dependencies = [ "yajrc", ] -[[package]] -name = "rpc-toolkit-macro" -version = "0.2.2" -dependencies = [ - "proc-macro2", - "rpc-toolkit-macro-internals", - "syn 1.0.109", -] - -[[package]] -name = "rpc-toolkit-macro-internals" -version = "0.2.2" -dependencies = [ - "itertools", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1165,6 +1090,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustls-pemfile" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +dependencies = [ + "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" + [[package]] name = "rustversion" version = "1.0.14" @@ -1242,7 +1183,7 @@ checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -1318,17 +1259,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.48" @@ -1397,7 +1327,7 @@ checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -1442,7 +1372,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] [[package]] @@ -1620,7 +1550,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.48", + "syn", "wasm-bindgen-shared", ] @@ -1654,7 +1584,7 @@ checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1809,9 +1739,9 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "winreg" -version = "0.50.0" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" dependencies = [ "cfg-if", "windows-sys 0.48.0", @@ -1858,5 +1788,5 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.48", + "syn", ] diff --git a/Cargo.toml b/Cargo.toml index 839835f..19f3831 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ cbor = ["serde_cbor"] default = [] [dependencies] -axum = "0.7.3" +axum = "0.7" async-stream = "0.3" async-trait = "0.1" clap = { version = "4", features = ["derive"] } @@ -30,8 +30,7 @@ lazy_format = "2" lazy_static = "1.4" openssl = { version = "0.10", features = ["vendored"] } pin-project = "1" -reqwest = { version = "0.11" } -rpc-toolkit-macro = { version = "0.2.2", path = "../rpc-toolkit-macro" } +reqwest = { version = "0.12" } serde = { version = "1.0", features = ["derive"] } serde_cbor = { version = "0.11", optional = true } serde_json = "1.0" From 5279528a97c5743f5f9bf780ea10bf48b14494c3 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 13:38:33 -0600 Subject: [PATCH 61/79] remove command --- src/lib.rs | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ef404ef..d8ebfcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,28 +2,6 @@ pub use cli::*; // pub use command::*; pub use context::*; pub use handler::*; -/// `#[command(...)]` -/// - `#[command(cli_only)]` -> executed by CLI instead of RPC server (leaf commands only) -/// - `#[command(rpc_only)]` -> no CLI bindings (leaf commands only) -/// - `#[command(local)]` -> executed wherever it was invoked. (By RPC when hit from RPC server, by cli when invoked from CLI) -/// - `#[command(blocking)]` -> run with [spawn_blocking](tokio::task::spawn_blocking) if in an async context -/// - `#[command(about = "About text")]` -> Set about text for the command -/// - `#[command(rename = "new_name")]` -> Set the name of the command to `new_name` in the RPC and CLI -/// - `#[command(subcommands(...))]` -> Set this as the parent command for the listed subcommands -/// - note: the return type of the decorated function must be the [Context] required by its subcommands, and all args must implement [Clone](std::clone::Clone). -/// - `#[command(subcommands(self(self_command_impl)))]` -> If no subcommand is provided, call this function -/// - `self_command_impl :: Context ctx, Display res, Into err => ctx -> Result` -/// - note: [Display](std::fmt::Display) is not required for `res` if it has a custom display function that will take it as input -/// - if `self_command_impl` is async, write `self(self_command_impl(async))` -/// - if `self_command_impl` is blocking, write `self(self_command_impl(blocking))` -/// - default: require a subcommand if subcommands are specified -/// - `#[command(display(custom_display_fn))]` -> Use the function `custom_display_fn` to display the command's result (leaf commands only) -/// - `custom_display_fn :: res -> ()` -/// - note: `res` is the type of the decorated command's output -/// - default: `default_display` -/// -/// See also: [arg](rpc_toolkit_macro::arg), [context](rpc_toolkit_macro::context) -pub use rpc_toolkit_macro::command; pub use server::*; pub use {clap, futures, reqwest, serde, serde_json, tokio, url, yajrc}; From 33229337a41806cf0712296c7d8e2229ea31118b Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 13:49:31 -0600 Subject: [PATCH 62/79] add Extra to CallRemoteHandler --- src/cli.rs | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 9e0c993..9de0e55 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -13,7 +13,7 @@ use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader use url::Url; use yajrc::{Id, RpcError}; -use crate::util::{internal_error, parse_error, PhantomData}; +use crate::util::{internal_error, invalid_params, parse_error, without, Flat, PhantomData}; use crate::{ AnyHandler, CliBindingsAny, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, Name, ParentHandler, PrintCliResult, @@ -172,11 +172,11 @@ pub async fn call_remote_socket( .result } -struct CallRemoteHandler { - _phantom: PhantomData, +pub struct CallRemoteHandler { + _phantom: PhantomData<(Context, Extra)>, handler: RemoteHandler, } -impl CallRemoteHandler { +impl CallRemoteHandler { pub fn new(handler: RemoteHandler) -> Self { Self { _phantom: PhantomData::new(), @@ -184,7 +184,9 @@ impl CallRemoteHandler { } } } -impl Clone for CallRemoteHandler { +impl Clone + for CallRemoteHandler +{ fn clone(&self) -> Self { Self { _phantom: PhantomData::new(), @@ -192,34 +194,39 @@ impl Clone for CallRemoteHandler std::fmt::Debug for CallRemoteHandler { +impl std::fmt::Debug + for CallRemoteHandler +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("CallRemoteHandler").finish() } } -impl HandlerTypes for CallRemoteHandler +impl HandlerTypes + for CallRemoteHandler where RemoteHandler: HandlerTypes, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, + Extra: Send + Sync + 'static, { - type Params = RemoteHandler::Params; + type Params = Flat; type InheritedParams = RemoteHandler::InheritedParams; type Ok = RemoteHandler::Ok; type Err = RemoteHandler::Err; } -impl Handler for CallRemoteHandler +impl Handler for CallRemoteHandler where - Context: CallRemote, + Context: CallRemote, RemoteHandler: Handler, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, + Extra: Serialize + Send + Sync + 'static, { type Context = Context; async fn handle_async( @@ -235,8 +242,9 @@ where .context .call_remote( &full_method.join("."), - handle_args.raw_params.clone(), - Empty {}, + without(handle_args.raw_params.clone(), &handle_args.params.1) + .map_err(invalid_params)?, + handle_args.params.1, ) .await { @@ -247,7 +255,8 @@ where } } } -impl PrintCliResult for CallRemoteHandler +impl PrintCliResult + for CallRemoteHandler where Context: CallRemote, RemoteHandler: PrintCliResult, @@ -255,6 +264,7 @@ where RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, + Extra: Send + Sync + 'static, { type Context = Context; fn print( @@ -274,7 +284,7 @@ where context, parent_method, method, - params, + params: params.0, inherited_params, raw_params, }, From e2d05fd93e6bfd001365e7d867d834c6bc7a2672 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 13:52:42 -0600 Subject: [PATCH 63/79] pub util --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index d8ebfcb..6369216 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,4 +10,4 @@ pub mod command_helpers; mod context; mod handler; mod server; -mod util; +pub mod util; From 02e65718662ebbf19a44193d62e8ca59e7fb24f0 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 14:51:14 -0600 Subject: [PATCH 64/79] undo extra for with_call_remote --- src/handler/adapters.rs | 32 +++++++++++++------------------- tests/handler.rs | 2 +- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index c5cdf8d..a738d6a 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -40,7 +40,7 @@ pub trait HandlerExt: Handler + Sized { ) -> InheritanceHandler where F: Fn(Params, InheritedParams) -> Self::InheritedParams; - fn with_call_remote(self) -> RemoteCaller; + fn with_call_remote(self) -> RemoteCaller; } impl HandlerExt for T { @@ -90,7 +90,7 @@ impl HandlerExt for T { inherit: f, } } - fn with_call_remote(self) -> RemoteCaller { + fn with_call_remote(self) -> RemoteCaller { RemoteCaller { _phantom: PhantomData::new(), handler: self, @@ -452,11 +452,11 @@ where } } -pub struct RemoteCaller { - _phantom: PhantomData<(Context, Extra)>, +pub struct RemoteCaller { + _phantom: PhantomData, handler: H, } -impl Clone for RemoteCaller { +impl Clone for RemoteCaller { fn clone(&self) -> Self { Self { _phantom: PhantomData::new(), @@ -464,31 +464,29 @@ impl Clone for RemoteCaller { } } } -impl Debug for RemoteCaller { +impl Debug for RemoteCaller { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("RemoteCaller").field(&self.handler).finish() } } -impl HandlerTypes for RemoteCaller +impl HandlerTypes for RemoteCaller where H: HandlerTypes, - Extra: Send + Sync + 'static, { - type Params = Flat; + type Params = H::Params; type InheritedParams = H::InheritedParams; type Ok = H::Ok; type Err = H::Err; } -impl Handler for RemoteCaller +impl Handler for RemoteCaller where - Context: CallRemote, + Context: CallRemote, H: Handler, H::Params: Serialize, H::InheritedParams: Serialize, H::Ok: DeserializeOwned, H::Err: From, - Extra: Serialize + Send + Sync + 'static, { type Context = EitherContext; async fn handle_async( @@ -497,7 +495,7 @@ where context, parent_method, method, - params: Flat(params, extra), + params, inherited_params, raw_params, }: HandlerArgsFor, @@ -506,11 +504,7 @@ where EitherContext::C1(context) => { let full_method = parent_method.into_iter().chain(method).collect::>(); match context - .call_remote( - &full_method.join("."), - without(raw_params, &extra).map_err(invalid_params)?, - extra, - ) + .call_remote(&full_method.join("."), raw_params, Empty {}) .await { Ok(a) => imbl_value::from_value(a) @@ -559,7 +553,7 @@ where context, parent_method, method, - params: Flat(params, _), + params, inherited_params, raw_params, }: HandlerArgsFor, diff --git a/tests/handler.rs b/tests/handler.rs index 5eb3929..74ca8a1 100644 --- a/tests/handler.rs +++ b/tests/handler.rs @@ -129,7 +129,7 @@ fn make_api() -> ParentHandler { )) }, ) - .with_call_remote::(), + .with_call_remote::(), ) .subcommand( "hello", From e733885d98c233f169c6705120cb433b159a77ae Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 18:53:44 -0600 Subject: [PATCH 65/79] wip --- src/cli.rs | 48 +++++------ src/context.rs | 28 +++++-- src/handler/adapters.rs | 133 +++++++++++++----------------- src/handler/from_fn.rs | 178 +++++++++++++++++++++++++++++----------- src/handler/mod.rs | 177 +++++++++++++++------------------------ src/handler/parent.rs | 115 ++++++++++++-------------- src/lib.rs | 2 + src/server/mod.rs | 8 +- tests/handler.rs | 6 +- 9 files changed, 362 insertions(+), 333 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 9de0e55..68f91bf 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -4,7 +4,7 @@ use std::ffi::OsString; use clap::{CommandFactory, FromArgMatches}; use futures::Future; -use imbl_value::{InOMap, Value}; +use imbl_value::Value; use reqwest::header::{ACCEPT, CONTENT_LENGTH, CONTENT_TYPE}; use reqwest::{Client, Method}; use serde::de::DeserializeOwned; @@ -15,7 +15,7 @@ use yajrc::{Id, RpcError}; use crate::util::{internal_error, invalid_params, parse_error, without, Flat, PhantomData}; use crate::{ - AnyHandler, CliBindingsAny, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, + AnyHandler, CliBindingsAny, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, Name, ParentHandler, PrintCliResult, }; @@ -45,17 +45,17 @@ impl let ctx_ty = TypeId::of::(); let mut cmd = Config::command(); for (name, handlers) in &self.root_handler.subcommands.0 { - if let (Name(Some(name)), Some(DynHandler::WithCli(handler))) = ( + if let (Name(Some(name)), Some(cli)) = ( name, if let Some(handler) = handlers.get(&Some(ctx_ty)) { - Some(handler) + handler.cli() } else if let Some(handler) = handlers.get(&None) { - Some(handler) + handler.cli() } else { None }, ) { - cmd = cmd.subcommand(handler.cli_command(ctx_ty).name(name)); + cmd = cmd.subcommand(cli.cli_command(ctx_ty).name(name)); } } let matches = cmd.get_matches_from(args); @@ -172,11 +172,13 @@ pub async fn call_remote_socket( .result } -pub struct CallRemoteHandler { - _phantom: PhantomData<(Context, Extra)>, +pub struct CallRemoteHandler { + _phantom: PhantomData<(Context, RemoteContext, Extra)>, handler: RemoteHandler, } -impl CallRemoteHandler { +impl + CallRemoteHandler +{ pub fn new(handler: RemoteHandler) -> Self { Self { _phantom: PhantomData::new(), @@ -184,8 +186,8 @@ impl CallRemoteHandler Clone - for CallRemoteHandler +impl Clone + for CallRemoteHandler { fn clone(&self) -> Self { Self { @@ -202,8 +204,8 @@ impl std::fmt::Debug } } -impl HandlerTypes - for CallRemoteHandler +impl HandlerTypes + for CallRemoteHandler where RemoteHandler: HandlerTypes, RemoteHandler::Params: Serialize, @@ -218,17 +220,18 @@ where type Err = RemoteHandler::Err; } -impl Handler for CallRemoteHandler +impl Handler + for CallRemoteHandler where - Context: CallRemote, - RemoteHandler: Handler, + Context: CallRemote, + RemoteContext: IntoContext, + RemoteHandler: Handler, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, Extra: Serialize + Send + Sync + 'static, { - type Context = Context; async fn handle_async( &self, handle_args: HandlerArgsFor, @@ -255,18 +258,17 @@ where } } } -impl PrintCliResult - for CallRemoteHandler +impl PrintCliResult + for CallRemoteHandler where - Context: CallRemote, - RemoteHandler: PrintCliResult, + Context: CallRemote, + RemoteHandler: PrintCliResult, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, Extra: Send + Sync + 'static, { - type Context = Context; fn print( &self, HandlerArgs { @@ -276,7 +278,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.handler.print( diff --git a/src/context.rs b/src/context.rs index a5a0deb..328cb6d 100644 --- a/src/context.rs +++ b/src/context.rs @@ -84,6 +84,8 @@ impl IntoContext for EitherContext { } } } +impl From for EitherContext {} +impl From for EitherContext {} pub struct AnyContext(Box); impl AnyContext { @@ -114,9 +116,25 @@ impl IntoContext for AnyContext { } } -mod sealed { - pub(crate) trait Sealed {} - impl Sealed for C {} - impl Sealed for super::EitherContext {} - impl Sealed for super::AnyContext {} +pub(crate) mod sealed { + use std::any::TypeId; + + pub(crate) trait Sealed { + fn contains_type_id(id: TypeId) -> bool; + } + impl Sealed for C { + fn contains_type_id(id: TypeId) -> bool { + id == TypeId::of::() + } + } + impl Sealed for super::EitherContext { + fn contains_type_id(id: TypeId) -> bool { + C1::contains_type_id(id) || C2::contains_type_id(id) + } + } + impl Sealed for super::AnyContext { + fn contains_type_id(_: TypeId) -> bool { + true + } + } } diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index a738d6a..4f473b3 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -1,7 +1,6 @@ use std::any::TypeId; use std::collections::VecDeque; use std::fmt::Debug; -use std::sync::Arc; use clap::{CommandFactory, FromArgMatches}; use imbl_value::imbl::{OrdMap, OrdSet}; @@ -10,25 +9,25 @@ use serde::de::DeserializeOwned; use serde::Serialize; use yajrc::RpcError; -use crate::util::{internal_error, invalid_params, without, Flat, PhantomData}; +use crate::util::{internal_error, Flat, PhantomData}; use crate::{ - iter_from_ctx_and_handler, AnyContext, AnyHandler, CallRemote, CliBindings, DynHandler, - EitherContext, Empty, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, - IntoHandlers, OrEmpty, PrintCliResult, + AnyContext, CallRemote, CliBindings, EitherContext, Empty, Handler, HandlerArgs, + HandlerArgsFor, HandlerTypes, IntoContext, PrintCliResult, }; -pub trait HandlerExt: Handler + Sized { +pub trait HandlerExt: Handler + Sized { fn no_cli(self) -> NoCli; fn no_display(self) -> NoDisplay; - fn with_custom_display

(self, display: P) -> CustomDisplay + fn with_custom_display(self, display: P) -> CustomDisplay where P: PrintCliResult< + C, Params = Self::Params, InheritedParams = Self::InheritedParams, Ok = Self::Ok, Err = Self::Err, >; - fn with_custom_display_fn( + fn with_custom_display_fn( self, display: F, ) -> CustomDisplayFn @@ -40,19 +39,20 @@ pub trait HandlerExt: Handler + Sized { ) -> InheritanceHandler where F: Fn(Params, InheritedParams) -> Self::InheritedParams; - fn with_call_remote(self) -> RemoteCaller; + fn with_call_remote(self) -> RemoteCaller; } -impl HandlerExt for T { +impl + Sized> HandlerExt for T { fn no_cli(self) -> NoCli { NoCli(self) } fn no_display(self) -> NoDisplay { NoDisplay(self) } - fn with_custom_display

(self, display: P) -> CustomDisplay + fn with_custom_display(self, display: P) -> CustomDisplay where P: PrintCliResult< + C, Params = Self::Params, InheritedParams = Self::InheritedParams, Ok = Self::Ok, @@ -64,7 +64,7 @@ impl HandlerExt for T { handler: self, } } - fn with_custom_display_fn( + fn with_custom_display_fn( self, display: F, ) -> CustomDisplayFn @@ -90,7 +90,7 @@ impl HandlerExt for T { inherit: f, } } - fn with_call_remote(self) -> RemoteCaller { + fn with_call_remote(self) -> RemoteCaller { RemoteCaller { _phantom: PhantomData::new(), handler: self, @@ -106,25 +106,7 @@ impl HandlerTypes for NoCli { type Ok = H::Ok; type Err = H::Err; } -impl IntoHandlers> for NoCli -where - H: Handler, - H::Params: DeserializeOwned, - H::InheritedParams: OrEmpty>, - H::Ok: Serialize + DeserializeOwned, - RpcError: From, - A: Send + Sync + 'static, - B: Send + Sync + 'static, -{ - fn into_handlers(self) -> impl IntoIterator, DynHandler>)> { - iter_from_ctx_and_handler( - self.0.contexts(), - DynHandler::WithoutCli(Arc::new(AnyHandler::new( - self.0.with_inherited(|a, b| OrEmpty::from_t(Flat(a, b))), - ))), - ) - } -} +// TODO: implement Handler #[derive(Debug, Clone)] pub struct NoDisplay(pub H); @@ -135,11 +117,11 @@ impl HandlerTypes for NoDisplay { type Err = H::Err; } -impl Handler for NoDisplay +impl Handler for NoDisplay where - H: Handler, + Context: IntoContext, + H: Handler, { - type Context = H::Context; fn handle_sync( &self, HandlerArgs { @@ -149,7 +131,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, ) -> Result { self.0.handle_sync(HandlerArgs { context, @@ -169,7 +151,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, ) -> Result { self.0 .handle_async(HandlerArgs { @@ -196,13 +178,13 @@ where self.0.method_from_dots(method, ctx_ty) } } -impl PrintCliResult for NoDisplay +impl PrintCliResult for NoDisplay where + Context: IntoContext, H: HandlerTypes, H::Params: FromArgMatches + CommandFactory + Serialize, { - type Context = AnyContext; - fn print(&self, _: HandlerArgsFor, _: Self::Ok) -> Result<(), Self::Err> { + fn print(&self, _: HandlerArgsFor, _: Self::Ok) -> Result<(), Self::Err> { Ok(()) } } @@ -222,12 +204,12 @@ where type Err = H::Err; } -impl Handler for CustomDisplay +impl Handler for CustomDisplay where - H: Handler, + Context: IntoContext, + H: Handler, P: Send + Sync + Clone + 'static, { - type Context = H::Context; fn handle_sync( &self, HandlerArgs { @@ -237,7 +219,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, ) -> Result { self.handler.handle_sync(HandlerArgs { context, @@ -257,7 +239,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, ) -> Result { self.handler .handle_async(HandlerArgs { @@ -284,10 +266,12 @@ where self.handler.method_from_dots(method, ctx_ty) } } -impl PrintCliResult for CustomDisplay +impl PrintCliResult for CustomDisplay where + Context: IntoContext, H: HandlerTypes, P: PrintCliResult< + Context, Params = H::Params, InheritedParams = H::InheritedParams, Ok = H::Ok, @@ -297,7 +281,6 @@ where + Clone + 'static, { - type Context = P::Context; fn print( &self, HandlerArgs { @@ -307,7 +290,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.print.print( @@ -356,13 +339,13 @@ where type Err = H::Err; } -impl Handler for CustomDisplayFn +impl Handler for CustomDisplayFn where - Context: Send + Sync + 'static, - H: Handler, + Context: IntoContext, + C: 'static, + H: Handler, F: Send + Sync + Clone + 'static, { - type Context = H::Context; fn handle_sync( &self, HandlerArgs { @@ -372,7 +355,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, ) -> Result { self.handler.handle_sync(HandlerArgs { context, @@ -392,7 +375,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, ) -> Result { self.handler .handle_async(HandlerArgs { @@ -419,13 +402,12 @@ where self.handler.method_from_dots(method, ctx_ty) } } -impl PrintCliResult for CustomDisplayFn +impl PrintCliResult for CustomDisplayFn where Context: IntoContext, H: HandlerTypes, F: Fn(HandlerArgsFor, H::Ok) -> Result<(), H::Err> + Send + Sync + Clone + 'static, { - type Context = Context; fn print( &self, HandlerArgs { @@ -479,16 +461,17 @@ where type Err = H::Err; } -impl Handler for RemoteCaller +impl Handler> + for RemoteCaller where - Context: CallRemote, - H: Handler, + Context: CallRemote, + RemoteContext: IntoContext, + H: Handler, H::Params: Serialize, H::InheritedParams: Serialize, H::Ok: DeserializeOwned, H::Err: From, { - type Context = EitherContext; async fn handle_async( &self, HandlerArgs { @@ -498,7 +481,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, Self>, ) -> Result { match context { EitherContext::C1(context) => { @@ -535,18 +518,17 @@ where self.handler.metadata(method, ctx_ty) } fn contexts(&self) -> Option> { - Self::Context::type_ids() + Context::type_ids() } fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { self.handler.method_from_dots(method, ctx_ty) } } -impl PrintCliResult for RemoteCaller +impl PrintCliResult for RemoteCaller where Context: IntoContext, - H: PrintCliResult, + H: PrintCliResult, { - type Context = H::Context; fn print( &self, HandlerArgs { @@ -556,7 +538,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.handler.print( @@ -611,14 +593,15 @@ where type Err = H::Err; } -impl Handler for InheritanceHandler +impl Handler + for InheritanceHandler where + Context: IntoContext, Params: Send + Sync + 'static, InheritedParams: Send + Sync + 'static, - H: Handler, + H: Handler, F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { - type Context = H::Context; fn handle_sync( &self, HandlerArgs { @@ -628,7 +611,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, ) -> Result { self.handler.handle_sync(HandlerArgs { context, @@ -648,7 +631,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, ) -> Result { self.handler .handle_async(HandlerArgs { @@ -676,15 +659,15 @@ where } } -impl CliBindings +impl CliBindings for InheritanceHandler where + Context: IntoContext, Params: Send + Sync + 'static, InheritedParams: Send + Sync + 'static, - H: CliBindings, + H: CliBindings, F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { - type Context = H::Context; fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { self.handler.cli_command(ctx_ty) } @@ -704,7 +687,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { self.handler.cli_display( diff --git a/src/handler/from_fn.rs b/src/handler/from_fn.rs index c77a0f2..253132a 100644 --- a/src/handler/from_fn.rs +++ b/src/handler/from_fn.rs @@ -2,14 +2,16 @@ use std::any::TypeId; use std::collections::VecDeque; use std::fmt::Display; +use clap::{CommandFactory, FromArgMatches}; use futures::Future; use imbl_value::imbl::OrdMap; use imbl_value::Value; use serde::de::DeserializeOwned; +use serde::Serialize; use crate::util::PhantomData; use crate::{ - AnyContext, Empty, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, + CliBindings, Empty, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, PrintCliResult, }; @@ -42,18 +44,62 @@ impl std::fmt::Debug for FromFn { .finish() } } -impl PrintCliResult for FromFn +impl PrintCliResult for FromFn where + Context: IntoContext, Self: HandlerTypes, ::Ok: Display, { - type Context = AnyContext; - fn print( + fn print(&self, _: HandlerArgsFor, result: Self::Ok) -> Result<(), Self::Err> { + Ok(println!("{result}")) + } +} +impl CliBindings for FromFn +where + Context: IntoContext, + Self: HandlerTypes, + Self::Params: CommandFactory + FromArgMatches + Serialize, + Self: PrintCliResult, +{ + fn cli_command(&self, _: TypeId) -> clap::Command { + Self::Params::command() + } + fn cli_parse( &self, - _: HandlerArgsFor, + matches: &clap::ArgMatches, + _: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { - Ok(println!("{result}")) + self.print( + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) } } @@ -106,18 +152,62 @@ impl std::fmt::Debug for FromFnAsync { f.debug_struct("FromFnAsync").finish() } } -impl PrintCliResult for FromFnAsync +impl PrintCliResult for FromFnAsync where + Context: IntoContext, Self: HandlerTypes, ::Ok: Display, { - type Context = AnyContext; - fn print( + fn print(&self, _: HandlerArgsFor, result: Self::Ok) -> Result<(), Self::Err> { + Ok(println!("{result}")) + } +} +impl CliBindings for FromFnAsync +where + Context: IntoContext, + Self: HandlerTypes, + Self::Params: CommandFactory + FromArgMatches + Serialize, + Self: PrintCliResult, +{ + fn cli_command(&self, _: TypeId) -> clap::Command { + Self::Params::command() + } + fn cli_parse( &self, - _: HandlerArgsFor, + matches: &clap::ArgMatches, + _: TypeId, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { - Ok(println!("{result}")) + self.print( + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) } } @@ -152,7 +242,7 @@ where type Err = E; } -impl Handler +impl Handler for FromFn> where F: Fn(HandlerArgs) -> Result @@ -166,16 +256,15 @@ where Params: Send + Sync + 'static, InheritedParams: Send + Sync + 'static, { - type Context = Context; fn handle_sync( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { (self.function)(handle_args) } async fn handle_async( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { if self.blocking { self.handle_async_with_sync_blocking(handle_args).await @@ -204,7 +293,7 @@ where type Err = E; } -impl Handler +impl Handler for FromFnAsync> where F: Fn(HandlerArgs) -> Fut + Send + Sync + Clone + 'static, @@ -215,10 +304,9 @@ where Params: Send + Sync + 'static, InheritedParams: Send + Sync + 'static, { - type Context = Context; async fn handle_async( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { (self.function)(handle_args).await } @@ -239,19 +327,19 @@ where type Err = E; } -impl Handler for FromFn +impl Handler for FromFn where + Context: IntoContext, F: Fn() -> Result + Send + Sync + Clone + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Context = AnyContext; - fn handle_sync(&self, _: HandlerArgsFor) -> Result { + fn handle_sync(&self, _: HandlerArgsFor) -> Result { (self.function)() } async fn handle_async( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { if self.blocking { self.handle_async_with_sync_blocking(handle_args).await @@ -276,18 +364,15 @@ where type Err = E; } -impl Handler for FromFnAsync +impl Handler for FromFnAsync where + Context: IntoContext, F: Fn() -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Context = AnyContext; - async fn handle_async( - &self, - _: HandlerArgsFor, - ) -> Result { + async fn handle_async(&self, _: HandlerArgsFor) -> Result { (self.function)().await } fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { @@ -308,23 +393,22 @@ where type Err = E; } -impl Handler for FromFn +impl Handler for FromFn where Context: IntoContext, F: Fn(Context) -> Result + Send + Sync + Clone + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Context = Context; fn handle_sync( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { (self.function)(handle_args.context) } async fn handle_async( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { if self.blocking { self.handle_async_with_sync_blocking(handle_args).await @@ -350,7 +434,7 @@ where type Err = E; } -impl Handler for FromFnAsync +impl Handler for FromFnAsync where Context: IntoContext, F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, @@ -358,10 +442,9 @@ where T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Context = Context; async fn handle_async( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { (self.function)(handle_args.context).await } @@ -384,7 +467,7 @@ where type Err = E; } -impl Handler for FromFn +impl Handler for FromFn where Context: IntoContext, F: Fn(Context, Params) -> Result + Send + Sync + Clone + 'static, @@ -392,10 +475,9 @@ where T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Context = Context; fn handle_sync( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { let HandlerArgs { context, params, .. @@ -404,7 +486,7 @@ where } async fn handle_async( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { if self.blocking { self.handle_async_with_sync_blocking(handle_args).await @@ -431,7 +513,8 @@ where type Err = E; } -impl Handler for FromFnAsync +impl Handler + for FromFnAsync where Context: IntoContext, F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, @@ -440,10 +523,9 @@ where T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Context = Context; async fn handle_async( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { let HandlerArgs { context, params, .. @@ -471,7 +553,7 @@ where type Err = E; } -impl Handler +impl Handler for FromFn where Context: IntoContext, @@ -481,10 +563,9 @@ where T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Context = Context; fn handle_sync( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { let HandlerArgs { context, @@ -496,7 +577,7 @@ where } async fn handle_async( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { if self.blocking { self.handle_async_with_sync_blocking(handle_args).await @@ -525,7 +606,7 @@ where type Err = E; } -impl Handler +impl Handler for FromFnAsync where Context: IntoContext, @@ -536,10 +617,9 @@ where T: Send + Sync + 'static, E: Send + Sync + 'static, { - type Context = Context; async fn handle_async( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { let HandlerArgs { context, diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 31d9587..c7bb9a5 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1,9 +1,10 @@ use std::any::TypeId; use std::collections::VecDeque; +use std::marker::PhantomData; use std::ops::Deref; use std::sync::Arc; -use clap::{ArgMatches, Command, CommandFactory, FromArgMatches, Parser}; +use clap::{ArgMatches, Command, Parser}; use futures::Future; use imbl_value::imbl::{OrdMap, OrdSet}; use imbl_value::Value; @@ -72,6 +73,7 @@ pub(crate) trait HandleAny: Send + Sync { ctx_ty: TypeId, ) -> OrdMap<&'static str, Value>; fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option>; + fn cli(&self) -> Option<&dyn CliBindingsAny>; } #[async_trait::async_trait] impl HandleAny for Arc { @@ -95,6 +97,9 @@ impl HandleAny for Arc { fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { self.deref().method_from_dots(method, ctx_ty) } + fn cli(&self) -> Option<&dyn CliBindingsAny> { + self.deref().cli() + } } pub(crate) trait CliBindingsAny { @@ -112,8 +117,8 @@ pub(crate) trait CliBindingsAny { ) -> Result<(), RpcError>; } -pub trait CliBindings: HandlerTypes { - type Context: IntoContext; +pub trait CliBindings: HandlerTypes { + const NO_CLI: bool = false; fn cli_command(&self, ctx_ty: TypeId) -> Command; fn cli_parse( &self, @@ -122,124 +127,57 @@ pub trait CliBindings: HandlerTypes { ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; fn cli_display( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err>; } -pub trait PrintCliResult: HandlerTypes { - type Context: IntoContext; +pub trait PrintCliResult: HandlerTypes { fn print( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err>; } -impl CliBindings for T -where - T: HandlerTypes, - T::Params: CommandFactory + FromArgMatches + Serialize, - T: PrintCliResult, -{ - type Context = T::Context; - fn cli_command(&self, _: TypeId) -> clap::Command { - Self::Params::command() - } - fn cli_parse( - &self, - matches: &clap::ArgMatches, - _: TypeId, - ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - Self::Params::from_arg_matches(matches).and_then(|a| { - Ok(( - VecDeque::new(), - imbl_value::to_value(&a) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, - )) - }) - } - fn cli_display( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.print( - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }, - result, - ) - } -} - -pub(crate) trait HandleAnyWithCli: - HandleAny + CliBindingsAny -{ -} -impl + CliBindingsAny> - HandleAnyWithCli for T -{ -} - #[allow(private_interfaces)] -pub enum DynHandler { - WithoutCli(Arc>), - WithCli(Arc>), +pub struct DynHandler(Arc>); +impl DynHandler { + pub fn iter + CliBindings>( + h: H, + ) -> Option, Self)>> { + iter_from_ctx_and_handler(ctx, handler) + } } impl Clone for DynHandler { fn clone(&self) -> Self { - match self { - Self::WithCli(a) => Self::WithCli(a.clone()), - Self::WithoutCli(a) => Self::WithoutCli(a.clone()), - } + Self(self.0.clone()) } } #[async_trait::async_trait] impl HandleAny for DynHandler { type Inherited = Inherited; fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { - match self { - DynHandler::WithoutCli(h) => h.handle_sync(handle_args), - DynHandler::WithCli(h) => h.handle_sync(handle_args), - } + self.0.handle_sync(handle_args) } async fn handle_async( &self, handle_args: HandleAnyArgs, ) -> Result { - match self { - DynHandler::WithoutCli(h) => h.handle_async(handle_args).await, - DynHandler::WithCli(h) => h.handle_async(handle_args).await, - } + self.0.handle_async(handle_args).await } fn metadata( &self, method: VecDeque<&'static str>, ctx_ty: TypeId, ) -> OrdMap<&'static str, Value> { - match self { - DynHandler::WithoutCli(h) => h.metadata(method, ctx_ty), - DynHandler::WithCli(h) => h.metadata(method, ctx_ty), - } + self.0.metadata(method, ctx_ty) } fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - match self { - DynHandler::WithoutCli(h) => h.method_from_dots(method, ctx_ty), - DynHandler::WithCli(h) => h.method_from_dots(method, ctx_ty), - } + self.0.method_from_dots(method, ctx_ty) + } + fn cli(&self) -> Option<&dyn CliBindingsAny> { + self.0.cli() } } @@ -268,11 +206,10 @@ pub trait HandlerTypes { type Err: Send + Sync; } -pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { - type Context: IntoContext; +pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { fn handle_sync( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> Result { handle_args .context @@ -281,17 +218,17 @@ pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { } fn handle_async( &self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> impl Future> + Send; fn handle_async_with_sync<'a>( &'a self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> impl Future> + Send + 'a { async move { self.handle_sync(handle_args) } } fn handle_async_with_sync_blocking<'a>( &'a self, - handle_args: HandlerArgsFor, + handle_args: HandlerArgsFor, ) -> impl Future> + Send + 'a { async move { let s = self.clone(); @@ -312,7 +249,7 @@ pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { OrdMap::new() } fn contexts(&self) -> Option> { - Self::Context::type_ids() + Context::type_ids() } #[allow(unused_variables)] fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { @@ -324,30 +261,40 @@ pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { } } -pub(crate) struct AnyHandler(H); -impl AnyHandler { +pub(crate) struct AnyHandler { + _phantom: PhantomData, + handler: H, +} +impl AnyHandler { pub(crate) fn new(handler: H) -> Self { - Self(handler) + Self { + _phantom: PhantomData, + handler, + } } } -impl std::fmt::Debug for AnyHandler { +impl std::fmt::Debug for AnyHandler { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_tuple("AnyHandler").field(&self.0).finish() + f.debug_struct("AnyHandler") + .field("handler", &self.handler) + .finish() } } #[async_trait::async_trait] -impl HandleAny for AnyHandler +impl HandleAny for AnyHandler where + Context: IntoContext, + H: Handler + CliBindings, H::Params: DeserializeOwned, - H::Ok: Serialize, + H::Ok: Serialize + DeserializeOwned, RpcError: From, { type Inherited = H::InheritedParams; fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { imbl_value::to_value( &self - .0 + .handler .handle_sync(handle_args.downcast::<_, H>().map_err(invalid_params)?)?, ) .map_err(internal_error) @@ -358,7 +305,7 @@ where ) -> Result { imbl_value::to_value( &self - .0 + .handler .handle_async(handle_args.downcast::<_, H>().map_err(invalid_params)?) .await?, ) @@ -369,37 +316,45 @@ where method: VecDeque<&'static str>, ctx_ty: TypeId, ) -> OrdMap<&'static str, Value> { - self.0.metadata(method, ctx_ty) + self.handler.metadata(method, ctx_ty) } fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.0.method_from_dots(method, ctx_ty) + self.handler.method_from_dots(method, ctx_ty) + } + fn cli(&self) -> Option<&dyn CliBindingsAny> { + if H::NO_CLI { + None + } else { + Some(self) + } } } -impl CliBindingsAny for AnyHandler +impl CliBindingsAny for AnyHandler where - H: CliBindings, + Context: IntoContext, + H: CliBindings, H::Params: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, { type Inherited = H::InheritedParams; fn cli_command(&self, ctx_ty: TypeId) -> Command { - self.0.cli_command(ctx_ty) + self.handler.cli_command(ctx_ty) } fn cli_parse( &self, matches: &ArgMatches, ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.0.cli_parse(matches, ctx_ty) + self.handler.cli_parse(matches, ctx_ty) } fn cli_display( &self, handle_args: HandleAnyArgs, result: Value, ) -> Result<(), RpcError> { - self.0 + self.handler .cli_display( handle_args.downcast::<_, H>().map_err(invalid_params)?, imbl_value::from_value(result).map_err(internal_error)?, diff --git a/src/handler/parent.rs b/src/handler/parent.rs index 09d79b1..cd8ff91 100644 --- a/src/handler/parent.rs +++ b/src/handler/parent.rs @@ -1,44 +1,18 @@ use std::any::TypeId; use std::collections::VecDeque; -use std::sync::Arc; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; use imbl_value::imbl::{OrdMap, OrdSet}; use imbl_value::Value; -use serde::de::DeserializeOwned; use serde::Serialize; use yajrc::RpcError; use crate::util::{combine, Flat, PhantomData}; use crate::{ - AnyContext, AnyHandler, CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, - HandlerArgs, HandlerArgsFor, HandlerExt, HandlerTypes, IntoContext, OrEmpty, + AnyContext, CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, + HandlerArgsFor, HandlerTypes, IntoContext, }; -pub trait IntoHandlers: HandlerTypes { - fn into_handlers(self) -> impl IntoIterator, DynHandler)>; -} - -impl IntoHandlers> for H -where - H: Handler + CliBindings, - H::Params: DeserializeOwned, - H::InheritedParams: OrEmpty>, - H::Ok: Serialize + DeserializeOwned, - RpcError: From, - A: Send + Sync + 'static, - B: Send + Sync + 'static, -{ - fn into_handlers(self) -> impl IntoIterator, DynHandler>)> { - iter_from_ctx_and_handler( - intersect_type_ids(self.contexts(), ::Context::type_ids()), - DynHandler::WithCli(Arc::new(AnyHandler::new( - self.with_inherited(|a, b| OrEmpty::from_t(Flat(a, b))), - ))), - ) - } -} - pub(crate) fn iter_from_ctx_and_handler( ctx: Option>, handler: DynHandler, @@ -109,12 +83,12 @@ impl SubcommandMap { } } -pub struct ParentHandler { - _phantom: PhantomData, +pub struct ParentHandler { + _phantom: PhantomData, pub(crate) subcommands: SubcommandMap>, metadata: OrdMap<&'static str, Value>, } -impl ParentHandler { +impl ParentHandler { pub fn new() -> Self { Self { _phantom: PhantomData::new(), @@ -127,7 +101,7 @@ impl ParentHandler { self } } -impl Clone for ParentHandler { +impl Clone for ParentHandler { fn clone(&self) -> Self { Self { _phantom: PhantomData::new(), @@ -136,7 +110,9 @@ impl Clone for ParentHandler { } } } -impl std::fmt::Debug for ParentHandler { +impl std::fmt::Debug + for ParentHandler +{ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("ParentHandler") // .field(&self.subcommands) @@ -144,7 +120,9 @@ impl std::fmt::Debug for ParentHandler ParentHandler { +impl + ParentHandler +{ fn get_contexts(&self) -> Option> { let mut set = OrdSet::new(); for ctx_ty in self.subcommands.0.values().flat_map(|c| c.keys()) { @@ -153,25 +131,30 @@ impl ParentHandler { Some(set) } #[allow(private_bounds)] - pub fn subcommand(mut self, name: &'static str, handler: H) -> Self + pub fn subcommand(mut self, name: &'static str, handler: H) -> Self where - H: IntoHandlers>, + H: Handler> + CliBindings, { - self.subcommands - .insert(name.into(), handler.into_handlers()); + if let Some(h) = DynHandler::iter(handler) { + self.subcommands.insert(name.into(), h); + } self } #[allow(private_bounds)] - pub fn root_handler(mut self, handler: H) -> Self + pub fn root_handler(mut self, handler: H) -> Self where - H: IntoHandlers> + HandlerTypes, + H: Handler> + + CliBindings, { - self.subcommands.insert(None, handler.into_handlers()); + if let Some((c, h)) = DynHandler::iter(handler) { + self.subcommands.insert(None, h); + } self } } -impl HandlerTypes for ParentHandler +impl HandlerTypes + for ParentHandler where Params: Send + Sync, InheritedParams: Send + Sync, @@ -182,12 +165,13 @@ where type Err = RpcError; } -impl Handler for ParentHandler +impl Handler + for ParentHandler where + Context: IntoContext, Params: Send + Sync + 'static, InheritedParams: Serialize + Send + Sync + 'static, { - type Context = AnyContext; fn handle_sync( &self, HandlerArgs { @@ -197,7 +181,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, ) -> Result { let cmd = method.pop_front(); if let Some(cmd) = cmd { @@ -205,7 +189,7 @@ where } if let Some((_, sub_handler)) = &self.subcommands.get(context.inner_type_id(), cmd) { sub_handler.handle_sync(HandleAnyArgs { - context, + context: context.upcast(), parent_method, method, params: raw_params, @@ -224,7 +208,7 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, ) -> Result { let cmd = method.pop_front(); if let Some(cmd) = cmd { @@ -233,7 +217,7 @@ where if let Some((_, sub_handler)) = self.subcommands.get(context.inner_type_id(), cmd) { sub_handler .handle_async(HandleAnyArgs { - context, + context: context.upcast(), parent_method, method, params: raw_params, @@ -280,29 +264,30 @@ where } } -impl CliBindings for ParentHandler +impl CliBindings + for ParentHandler where + Context: IntoContext, Params: FromArgMatches + CommandFactory + Serialize + Send + Sync + 'static, InheritedParams: Serialize + Send + Sync + 'static, { - type Context = AnyContext; fn cli_command(&self, ctx_ty: TypeId) -> Command { let mut base = Params::command().subcommand_required(true); for (name, handlers) in &self.subcommands.0 { match ( name, if let Some(handler) = handlers.get(&Some(ctx_ty)) { - Some(handler) + handler.cli() } else if let Some(handler) = handlers.get(&None) { - Some(handler) + handler.cli() } else { None }, ) { - (Name(Some(name)), Some(DynHandler::WithCli(handler))) => { - base = base.subcommand(handler.cli_command(ctx_ty).name(name)); + (Name(Some(name)), Some(cli)) => { + base = base.subcommand(cli.cli_command(ctx_ty).name(name)); } - (Name(None), Some(DynHandler::WithCli(_))) => { + (Name(None), Some(_)) => { base = base.subcommand_required(false); } _ => (), @@ -321,10 +306,12 @@ where Some((name, matches)) => (Some(name), matches), None => (None, matches), }; - if let Some((Name(Some(name)), DynHandler::WithCli(handler))) = - self.subcommands.get(ctx_ty, name) + if let Some((Name(Some(name)), cli)) = self + .subcommands + .get(ctx_ty, name) + .and_then(|(n, h)| h.cli().map(|c| (n, c))) { - let (mut method, params) = handler.cli_parse(matches, ctx_ty)?; + let (mut method, params) = cli.cli_parse(matches, ctx_ty)?; method.push_front(name); Ok(( @@ -345,19 +332,21 @@ where params, inherited_params, raw_params, - }: HandlerArgsFor, + }: HandlerArgsFor, result: Self::Ok, ) -> Result<(), Self::Err> { let cmd = method.pop_front(); if let Some(cmd) = cmd { parent_method.push_back(cmd); } - if let Some((_, DynHandler::WithCli(sub_handler))) = - self.subcommands.get(context.inner_type_id(), cmd) + if let Some((_, cli)) = self + .subcommands + .get(context.inner_type_id(), cmd) + .and_then(|(n, h)| h.cli().map(|c| (n, c))) { - sub_handler.cli_display( + cli.cli_display( HandleAnyArgs { - context, + context: context.upcast(), parent_method, method, params: raw_params, diff --git a/src/lib.rs b/src/lib.rs index 6369216..062b5c5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature = "nightly", feature(const_trait_impl, const_type_id))] + pub use cli::*; // pub use command::*; pub use context::*; diff --git a/src/server/mod.rs b/src/server/mod.rs index 3d86e02..8172522 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -4,11 +4,11 @@ use std::sync::Arc; use futures::future::{join_all, BoxFuture}; use futures::{Future, FutureExt, Stream, StreamExt}; -use imbl_value::{InOMap, Value}; +use imbl_value::Value; use yajrc::{RpcError, RpcMethod}; use crate::util::{invalid_request, JobRunner}; -use crate::{AnyHandler, Empty, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; +use crate::{AnyHandler, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; pub type GenericRpcMethod = yajrc::GenericRpcMethod; pub type RpcRequest = yajrc::RpcRequest; @@ -23,7 +23,7 @@ pub use socket::*; pub struct Server { make_ctx: Arc BoxFuture<'static, Result> + Send + Sync>, - root_handler: Arc>, + root_handler: Arc>>, } impl Clone for Server { fn clone(&self) -> Self { @@ -39,7 +39,7 @@ impl Server { Fut: Future> + Send + 'static, >( make_ctx: MakeCtx, - root_handler: ParentHandler, + root_handler: ParentHandler, ) -> Self { Server { make_ctx: Arc::new(move || make_ctx().boxed()), diff --git a/tests/handler.rs b/tests/handler.rs index 74ca8a1..8b9bde2 100644 --- a/tests/handler.rs +++ b/tests/handler.rs @@ -140,7 +140,7 @@ fn make_api() -> ParentHandler { .subcommand("a_hello", from_fn_async(a_hello)) .subcommand( "dondes", - ParentHandler::::new().subcommand( + ParentHandler::::new().subcommand( "donde", from_fn(|c: CliContext, _: (), donde| { Ok::<_, RpcError>( @@ -157,7 +157,7 @@ fn make_api() -> ParentHandler { ) .subcommand( "fizz", - ParentHandler::::new().root_handler( + ParentHandler::::new().root_handler( from_fn(|c: CliContext, _: Empty, InheritParams { donde }| { Ok::<_, RpcError>( format!( @@ -172,7 +172,7 @@ fn make_api() -> ParentHandler { ) .subcommand( "error", - ParentHandler::::new().root_handler( + ParentHandler::::new().root_handler( from_fn(|_: CliContext, _: Empty, InheritParams { .. }| { Err::(RpcError { code: 1, From ba9bc883384cd2c8adebde7b32aeb83ac6824aed Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 21:29:55 -0600 Subject: [PATCH 66/79] no dynamic context --- src/cli.rs | 82 +++++-- src/context.rs | 135 +----------- src/handler/adapters.rs | 472 ++++++++++++++++++++++++---------------- src/handler/from_fn.rs | 94 ++++---- src/handler/mod.rs | 243 ++++++++++++--------- src/handler/parent.rs | 155 ++++--------- src/server/http.rs | 5 +- src/server/mod.rs | 10 +- tests/handler.rs | 18 +- 9 files changed, 586 insertions(+), 628 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 68f91bf..700a69b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,3 @@ -use std::any::TypeId; use std::collections::VecDeque; use std::ffi::OsString; @@ -15,8 +14,8 @@ use yajrc::{Id, RpcError}; use crate::util::{internal_error, invalid_params, parse_error, without, Flat, PhantomData}; use crate::{ - AnyHandler, CliBindingsAny, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, - HandlerArgsFor, HandlerTypes, IntoContext, Name, ParentHandler, PrintCliResult, + AnyHandler, CliBindings, CliBindingsAny, Empty, HandleAny, HandleAnyArgs, HandlerArgs, + HandlerArgsFor, HandlerFor, HandlerTypes, Name, ParentHandler, PrintCliResult, }; type GenericRpcMethod<'a> = yajrc::GenericRpcMethod<&'a str, Value, Value>; @@ -26,14 +25,14 @@ type RpcResponse<'a> = yajrc::RpcResponse>; pub struct CliApp { _phantom: PhantomData<(Context, Config)>, make_ctx: Box Result + Send + Sync>, - root_handler: ParentHandler, + root_handler: ParentHandler, } impl CliApp { pub fn new Result + Send + Sync + 'static>( make_ctx: MakeCtx, - root_handler: ParentHandler, + root_handler: ParentHandler, ) -> Self { Self { _phantom: PhantomData::new(), @@ -42,29 +41,19 @@ impl } } pub fn run(self, args: impl IntoIterator) -> Result<(), RpcError> { - let ctx_ty = TypeId::of::(); let mut cmd = Config::command(); - for (name, handlers) in &self.root_handler.subcommands.0 { - if let (Name(Some(name)), Some(cli)) = ( - name, - if let Some(handler) = handlers.get(&Some(ctx_ty)) { - handler.cli() - } else if let Some(handler) = handlers.get(&None) { - handler.cli() - } else { - None - }, - ) { - cmd = cmd.subcommand(cli.cli_command(ctx_ty).name(name)); + for (name, handler) in &self.root_handler.subcommands.0 { + if let (Name(Some(name)), Some(cli)) = (name, handler.cli()) { + cmd = cmd.subcommand(cli.cli_command().name(name)); } } let matches = cmd.get_matches_from(args); let config = Config::from_arg_matches(&matches)?; let ctx = (self.make_ctx)(config)?; let root_handler = AnyHandler::new(self.root_handler); - let (method, params) = root_handler.cli_parse(&matches, ctx_ty)?; + let (method, params) = root_handler.cli_parse(&matches)?; let res = root_handler.handle_sync(HandleAnyArgs { - context: ctx.clone().upcast(), + context: ctx.clone(), parent_method: VecDeque::new(), method: method.clone(), params: params.clone(), @@ -72,7 +61,7 @@ impl })?; root_handler.cli_display( HandleAnyArgs { - context: ctx.upcast(), + context: ctx, parent_method: VecDeque::new(), method, params, @@ -220,12 +209,12 @@ where type Err = RemoteHandler::Err; } -impl Handler +impl HandlerFor for CallRemoteHandler where Context: CallRemote, - RemoteContext: IntoContext, - RemoteHandler: Handler, + RemoteContext: crate::Context, + RemoteHandler: HandlerFor, RemoteHandler::Params: Serialize, RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, @@ -294,3 +283,48 @@ where ) } } +impl CliBindings + for CallRemoteHandler +where + Context: crate::Context, + RemoteHandler: CliBindings, + RemoteHandler::Params: Serialize, + RemoteHandler::InheritedParams: Serialize, + RemoteHandler::Ok: DeserializeOwned, + RemoteHandler::Err: From, + Extra: Send + Sync + 'static, +{ + fn cli_command(&self) -> clap::Command { + self.handler.cli_command() + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + self.handler.cli_parse(matches) + } + fn cli_display( + &self, + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandlerArgsFor, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.handler.cli_display( + HandlerArgs { + context, + parent_method, + method, + params: params.0, + inherited_params, + raw_params, + }, + result, + ) + } +} diff --git a/src/context.rs b/src/context.rs index 328cb6d..b1b2054 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,140 +1,7 @@ -use std::any::{Any, TypeId}; - -use imbl_value::imbl::OrdSet; use tokio::runtime::Handle; -pub trait Context: Any + Send + Sync + 'static { - fn inner_type_id(&self) -> TypeId { - ::type_id(&self) - } +pub trait Context: Send + Sync + 'static { fn runtime(&self) -> Handle { Handle::current() } } - -#[allow(private_bounds)] -pub trait IntoContext: sealed::Sealed + Any + Send + Sync + Sized + 'static { - fn runtime(&self) -> Handle; - fn type_ids() -> Option>; - fn inner_type_id(&self) -> TypeId; - fn upcast(self) -> AnyContext; - fn downcast(value: AnyContext) -> Result; -} - -impl IntoContext for C { - fn runtime(&self) -> Handle { - ::runtime(&self) - } - fn type_ids() -> Option> { - let mut set = OrdSet::new(); - set.insert(TypeId::of::()); - Some(set) - } - fn inner_type_id(&self) -> TypeId { - TypeId::of::() - } - fn upcast(self) -> AnyContext { - AnyContext::new(self) - } - fn downcast(value: AnyContext) -> Result { - if value.0.inner_type_id() == TypeId::of::() { - unsafe { Ok(value.downcast_unchecked::()) } - } else { - Err(value) - } - } -} - -pub enum EitherContext { - C1(C1), - C2(C2), -} -impl IntoContext for EitherContext { - fn runtime(&self) -> Handle { - match self { - Self::C1(a) => a.runtime(), - Self::C2(a) => a.runtime(), - } - } - fn type_ids() -> Option> { - let mut set = OrdSet::new(); - set.extend(C1::type_ids()?); - set.extend(C2::type_ids()?); - Some(set) - } - fn inner_type_id(&self) -> TypeId { - match self { - EitherContext::C1(c) => c.inner_type_id(), - EitherContext::C2(c) => c.inner_type_id(), - } - } - fn downcast(value: AnyContext) -> Result { - match C1::downcast(value) { - Ok(a) => Ok(EitherContext::C1(a)), - Err(value) => match C2::downcast(value) { - Ok(a) => Ok(EitherContext::C2(a)), - Err(value) => Err(value), - }, - } - } - fn upcast(self) -> AnyContext { - match self { - Self::C1(c) => c.upcast(), - Self::C2(c) => c.upcast(), - } - } -} -impl From for EitherContext {} -impl From for EitherContext {} - -pub struct AnyContext(Box); -impl AnyContext { - pub fn new(value: C) -> Self { - Self(Box::new(value)) - } - unsafe fn downcast_unchecked(self) -> C { - let raw: *mut dyn Context = Box::into_raw(self.0); - *Box::from_raw(raw as *mut C) - } -} - -impl IntoContext for AnyContext { - fn runtime(&self) -> Handle { - self.0.runtime() - } - fn type_ids() -> Option> { - None - } - fn inner_type_id(&self) -> TypeId { - self.0.inner_type_id() - } - fn downcast(value: AnyContext) -> Result { - Ok(value) - } - fn upcast(self) -> AnyContext { - self - } -} - -pub(crate) mod sealed { - use std::any::TypeId; - - pub(crate) trait Sealed { - fn contains_type_id(id: TypeId) -> bool; - } - impl Sealed for C { - fn contains_type_id(id: TypeId) -> bool { - id == TypeId::of::() - } - } - impl Sealed for super::EitherContext { - fn contains_type_id(id: TypeId) -> bool { - C1::contains_type_id(id) || C2::contains_type_id(id) - } - } - impl Sealed for super::AnyContext { - fn contains_type_id(_: TypeId) -> bool { - true - } - } -} diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index 4f473b3..28986ed 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -3,22 +3,22 @@ use std::collections::VecDeque; use std::fmt::Debug; use clap::{CommandFactory, FromArgMatches}; -use imbl_value::imbl::{OrdMap, OrdSet}; +use imbl_value::imbl::OrdMap; use imbl_value::Value; use serde::de::DeserializeOwned; use serde::Serialize; use yajrc::RpcError; -use crate::util::{internal_error, Flat, PhantomData}; +use crate::util::{Flat, PhantomData}; use crate::{ - AnyContext, CallRemote, CliBindings, EitherContext, Empty, Handler, HandlerArgs, - HandlerArgsFor, HandlerTypes, IntoContext, PrintCliResult, + CallRemote, CallRemoteHandler, CliBindings, DynHandler, Handler, HandlerArgs, HandlerArgsFor, + HandlerFor, HandlerTypes, OrEmpty, PrintCliResult, WithContext, }; -pub trait HandlerExt: Handler + Sized { +pub trait HandlerExt: HandlerFor + Sized { fn no_cli(self) -> NoCli; fn no_display(self) -> NoDisplay; - fn with_custom_display(self, display: P) -> CustomDisplay + fn with_custom_display(self, display: P) -> CustomDisplay where P: PrintCliResult< C, @@ -27,7 +27,7 @@ pub trait HandlerExt: Handler + Sized { Ok = Self::Ok, Err = Self::Err, >; - fn with_custom_display_fn( + fn with_custom_display_fn( self, display: F, ) -> CustomDisplayFn @@ -39,17 +39,17 @@ pub trait HandlerExt: Handler + Sized { ) -> InheritanceHandler where F: Fn(Params, InheritedParams) -> Self::InheritedParams; - fn with_call_remote(self) -> RemoteCaller; + fn with_call_remote(self) -> RemoteCaller; } -impl + Sized> HandlerExt for T { +impl + Sized> HandlerExt for T { fn no_cli(self) -> NoCli { NoCli(self) } fn no_display(self) -> NoDisplay { NoDisplay(self) } - fn with_custom_display(self, display: P) -> CustomDisplay + fn with_custom_display(self, display: P) -> CustomDisplay where P: PrintCliResult< C, @@ -64,7 +64,7 @@ impl + Sized> HandlerExt for handler: self, } } - fn with_custom_display_fn( + fn with_custom_display_fn( self, display: F, ) -> CustomDisplayFn @@ -90,7 +90,7 @@ impl + Sized> HandlerExt for inherit: f, } } - fn with_call_remote(self) -> RemoteCaller { + fn with_call_remote(self) -> RemoteCaller { RemoteCaller { _phantom: PhantomData::new(), handler: self, @@ -106,21 +106,10 @@ impl HandlerTypes for NoCli { type Ok = H::Ok; type Err = H::Err; } -// TODO: implement Handler - -#[derive(Debug, Clone)] -pub struct NoDisplay(pub H); -impl HandlerTypes for NoDisplay { - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; -} - -impl Handler for NoDisplay +impl HandlerFor for NoCli where - Context: IntoContext, - H: Handler, + Context: crate::Context, + H: HandlerFor, { fn handle_sync( &self, @@ -164,23 +153,99 @@ where }) .await } - fn metadata( + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.0.metadata(method) + } + fn method_from_dots(&self, method: &str) -> Option> { + self.0.method_from_dots(method) + } +} +impl CliBindings for NoCli +where + Context: crate::Context, + H: HandlerTypes, +{ + const NO_CLI: bool = true; + fn cli_command(&self) -> clap::Command { + unimplemented!() + } + fn cli_parse( &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.0.metadata(method, ctx_ty) + _: &clap::ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + unimplemented!() } - fn contexts(&self) -> Option> { - self.0.contexts() + fn cli_display(&self, _: HandlerArgsFor, _: Self::Ok) -> Result<(), Self::Err> { + unimplemented!() } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.0.method_from_dots(method, ctx_ty) +} + +#[derive(Debug, Clone)] +pub struct NoDisplay(pub H); +impl HandlerTypes for NoDisplay { + type Params = H::Params; + type InheritedParams = H::InheritedParams; + type Ok = H::Ok; + type Err = H::Err; +} + +impl HandlerFor for NoDisplay +where + Context: crate::Context, + H: HandlerFor, +{ + fn handle_sync( + &self, + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandlerArgsFor, + ) -> Result { + self.0.handle_sync(HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + } + async fn handle_async( + &self, + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandlerArgsFor, + ) -> Result { + self.0 + .handle_async(HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + .await + } + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.0.metadata(method) + } + fn method_from_dots(&self, method: &str) -> Option> { + self.0.method_from_dots(method) } } impl PrintCliResult for NoDisplay where - Context: IntoContext, + Context: crate::Context, H: HandlerTypes, H::Params: FromArgMatches + CommandFactory + Serialize, { @@ -204,10 +269,10 @@ where type Err = H::Err; } -impl Handler for CustomDisplay +impl HandlerFor for CustomDisplay where - Context: IntoContext, - H: Handler, + Context: crate::Context, + H: HandlerFor, P: Send + Sync + Clone + 'static, { fn handle_sync( @@ -252,23 +317,16 @@ where }) .await } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.handler.metadata(method, ctx_ty) + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.handler.metadata(method) } - fn contexts(&self) -> Option> { - self.handler.contexts() - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.handler.method_from_dots(method, ctx_ty) + fn method_from_dots(&self, method: &str) -> Option> { + self.handler.method_from_dots(method) } } impl PrintCliResult for CustomDisplay where - Context: IntoContext, + Context: crate::Context, H: HandlerTypes, P: PrintCliResult< Context, @@ -307,7 +365,7 @@ where } } -pub struct CustomDisplayFn { +pub struct CustomDisplayFn { _phantom: PhantomData, print: F, handler: H, @@ -339,11 +397,11 @@ where type Err = H::Err; } -impl Handler for CustomDisplayFn +impl HandlerFor for CustomDisplayFn where - Context: IntoContext, + Context: crate::Context, C: 'static, - H: Handler, + H: HandlerFor, F: Send + Sync + Clone + 'static, { fn handle_sync( @@ -388,23 +446,16 @@ where }) .await } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.handler.metadata(method, ctx_ty) + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.handler.metadata(method) } - fn contexts(&self) -> Option> { - self.handler.contexts() - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.handler.method_from_dots(method, ctx_ty) + fn method_from_dots(&self, method: &str) -> Option> { + self.handler.method_from_dots(method) } } impl PrintCliResult for CustomDisplayFn where - Context: IntoContext, + Context: crate::Context, H: HandlerTypes, F: Fn(HandlerArgsFor, H::Ok) -> Result<(), H::Err> + Send + Sync + Clone + 'static, { @@ -434,11 +485,11 @@ where } } -pub struct RemoteCaller { - _phantom: PhantomData, +pub struct RemoteCaller { + _phantom: PhantomData<(Context, RemoteContext)>, handler: H, } -impl Clone for RemoteCaller { +impl Clone for RemoteCaller { fn clone(&self) -> Self { Self { _phantom: PhantomData::new(), @@ -446,113 +497,170 @@ impl Clone for RemoteCaller { } } } -impl Debug for RemoteCaller { +impl Debug for RemoteCaller { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("RemoteCaller").field(&self.handler).finish() } } -impl HandlerTypes for RemoteCaller -where - H: HandlerTypes, -{ - type Params = H::Params; - type InheritedParams = H::InheritedParams; - type Ok = H::Ok; - type Err = H::Err; -} -impl Handler> - for RemoteCaller +// impl HandlerTypes for RemoteCaller +// where +// H: HandlerTypes, +// { +// type Params = H::Params; +// type InheritedParams = H::InheritedParams; +// type Ok = H::Ok; +// type Err = H::Err; +// } +// impl HandlerFor +// for RemoteCaller +// where +// Context: CallRemote, +// RemoteContext: crate::Context, +// H: HandlerFor, +// H::Params: Serialize, +// H::InheritedParams: Serialize, +// H::Ok: DeserializeOwned, +// H::Err: From, +// { +// async fn handle_async( +// &self, +// HandlerArgs { +// context, +// parent_method, +// method, +// params, +// inherited_params, +// raw_params, +// }: HandlerArgsFor, +// ) -> Result { +// self.handler +// .handle_async(HandlerArgs { +// context, +// parent_method, +// method, +// params, +// inherited_params, +// raw_params, +// }) +// .await +// } +// fn metadata( +// &self, +// method: VecDeque<&'static str>, +// ctx_ty: TypeId, +// ) -> OrdMap<&'static str, Value> { +// self.handler.metadata(method, ctx_ty) +// } +// fn contexts(&self) -> Option> { +// Context::type_ids() +// } +// fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { +// self.handler.method_from_dots(method, ctx_ty) +// } +// } +// impl HandlerFor for RemoteCaller +// where +// Context: CallRemote, +// RemoteContext: crate::Context, +// H: HandlerFor, +// H::Params: Serialize, +// H::InheritedParams: Serialize, +// H::Ok: DeserializeOwned, +// H::Err: From, +// { +// async fn handle_async( +// &self, +// HandlerArgs { +// context, +// parent_method, +// method, +// params, +// inherited_params, +// raw_params, +// }: HandlerArgsFor, +// ) -> Result { +// let full_method = parent_method.into_iter().chain(method).collect::>(); +// match context +// .call_remote(&full_method.join("."), raw_params, Empty {}) +// .await +// { +// Ok(a) => imbl_value::from_value(a) +// .map_err(internal_error) +// .map_err(Self::Err::from), +// Err(e) => Err(Self::Err::from(e)), +// } +// } +// fn metadata( +// &self, +// method: VecDeque<&'static str>, +// ctx_ty: TypeId, +// ) -> OrdMap<&'static str, Value> { +// self.handler.metadata(method, ctx_ty) +// } +// fn contexts(&self) -> Option> { +// Context::type_ids() +// } +// fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { +// self.handler.method_from_dots(method, ctx_ty) +// } +// } +// impl PrintCliResult for RemoteCaller +// where +// Context: crate::Context, +// H: PrintCliResult, +// { +// fn print( +// &self, +// HandlerArgs { +// context, +// parent_method, +// method, +// params, +// inherited_params, +// raw_params, +// }: HandlerArgsFor, +// result: Self::Ok, +// ) -> Result<(), Self::Err> { +// self.handler.print( +// HandlerArgs { +// context, +// parent_method, +// method, +// params, +// inherited_params, +// raw_params, +// }, +// result, +// ) +// } +// } +impl Handler + for WithContext> where - Context: CallRemote, - RemoteContext: IntoContext, - H: Handler, - H::Params: Serialize, - H::InheritedParams: Serialize, - H::Ok: DeserializeOwned, + Context: crate::Context, + LocalContext: crate::Context + CallRemote, + RemoteContext: crate::Context, + H: HandlerFor + CliBindings, + H::Ok: Serialize + DeserializeOwned, H::Err: From, + H::Params: Serialize + DeserializeOwned, + H::InheritedParams: Serialize + OrEmpty, + RpcError: From, + Inherited: Send + Sync + 'static, { - async fn handle_async( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, Self>, - ) -> Result { - match context { - EitherContext::C1(context) => { - let full_method = parent_method.into_iter().chain(method).collect::>(); - match context - .call_remote(&full_method.join("."), raw_params, Empty {}) - .await - { - Ok(a) => imbl_value::from_value(a) - .map_err(internal_error) - .map_err(Self::Err::from), - Err(e) => Err(Self::Err::from(e)), - } - } - EitherContext::C2(context) => { - self.handler - .handle_async(HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }) - .await - } + type H = H; + fn handler_for(self) -> Option> { + if TypeId::of::() == TypeId::of::() { + DynHandler::new(self.handler.handler.no_cli()) + } else if TypeId::of::() == TypeId::of::() { + DynHandler::new(CallRemoteHandler::::new( + self.handler.handler, + )) + } else { + None } } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.handler.metadata(method, ctx_ty) - } - fn contexts(&self) -> Option> { - Context::type_ids() - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.handler.method_from_dots(method, ctx_ty) - } -} -impl PrintCliResult for RemoteCaller -where - Context: IntoContext, - H: PrintCliResult, -{ - fn print( - &self, - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }: HandlerArgsFor, - result: Self::Ok, - ) -> Result<(), Self::Err> { - self.handler.print( - HandlerArgs { - context, - parent_method, - method, - params, - inherited_params, - raw_params, - }, - result, - ) - } } pub struct InheritanceHandler { @@ -593,13 +701,13 @@ where type Err = H::Err; } -impl Handler +impl HandlerFor for InheritanceHandler where - Context: IntoContext, + Context: crate::Context, Params: Send + Sync + 'static, InheritedParams: Send + Sync + 'static, - H: Handler, + H: HandlerFor, F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { fn handle_sync( @@ -644,39 +752,31 @@ where }) .await } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.handler.metadata(method, ctx_ty) + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.handler.metadata(method) } - fn contexts(&self) -> Option> { - self.handler.contexts() - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.handler.method_from_dots(method, ctx_ty) + fn method_from_dots(&self, method: &str) -> Option> { + self.handler.method_from_dots(method) } } impl CliBindings for InheritanceHandler where - Context: IntoContext, + Context: crate::Context, Params: Send + Sync + 'static, InheritedParams: Send + Sync + 'static, H: CliBindings, F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + 'static, { - fn cli_command(&self, ctx_ty: TypeId) -> clap::Command { - self.handler.cli_command(ctx_ty) + fn cli_command(&self) -> clap::Command { + self.handler.cli_command() } fn cli_parse( &self, matches: &clap::ArgMatches, - ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.handler.cli_parse(matches, ctx_ty) + self.handler.cli_parse(matches) } fn cli_display( &self, diff --git a/src/handler/from_fn.rs b/src/handler/from_fn.rs index 253132a..1311805 100644 --- a/src/handler/from_fn.rs +++ b/src/handler/from_fn.rs @@ -1,4 +1,3 @@ -use std::any::TypeId; use std::collections::VecDeque; use std::fmt::Display; @@ -11,8 +10,7 @@ use serde::Serialize; use crate::util::PhantomData; use crate::{ - CliBindings, Empty, Handler, HandlerArgs, HandlerArgsFor, HandlerTypes, IntoContext, - PrintCliResult, + CliBindings, Empty, HandlerArgs, HandlerArgsFor, HandlerFor, HandlerTypes, PrintCliResult, }; pub struct FromFn { @@ -46,7 +44,7 @@ impl std::fmt::Debug for FromFn { } impl PrintCliResult for FromFn where - Context: IntoContext, + Context: crate::Context, Self: HandlerTypes, ::Ok: Display, { @@ -56,18 +54,17 @@ where } impl CliBindings for FromFn where - Context: IntoContext, + Context: crate::Context, Self: HandlerTypes, Self::Params: CommandFactory + FromArgMatches + Serialize, Self: PrintCliResult, { - fn cli_command(&self, _: TypeId) -> clap::Command { + fn cli_command(&self) -> clap::Command { Self::Params::command() } fn cli_parse( &self, matches: &clap::ArgMatches, - _: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { Self::Params::from_arg_matches(matches).and_then(|a| { Ok(( @@ -154,7 +151,7 @@ impl std::fmt::Debug for FromFnAsync { } impl PrintCliResult for FromFnAsync where - Context: IntoContext, + Context: crate::Context, Self: HandlerTypes, ::Ok: Display, { @@ -164,18 +161,17 @@ where } impl CliBindings for FromFnAsync where - Context: IntoContext, + Context: crate::Context, Self: HandlerTypes, Self::Params: CommandFactory + FromArgMatches + Serialize, Self: PrintCliResult, { - fn cli_command(&self, _: TypeId) -> clap::Command { + fn cli_command(&self) -> clap::Command { Self::Params::command() } fn cli_parse( &self, matches: &clap::ArgMatches, - _: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { Self::Params::from_arg_matches(matches).and_then(|a| { Ok(( @@ -232,7 +228,7 @@ where + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, - Context: IntoContext, + Context: crate::Context, Params: Send + Sync, InheritedParams: Send + Sync, { @@ -242,7 +238,7 @@ where type Err = E; } -impl Handler +impl HandlerFor for FromFn> where F: Fn(HandlerArgs) -> Result @@ -252,7 +248,7 @@ where + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, - Context: IntoContext, + Context: crate::Context, Params: Send + Sync + 'static, InheritedParams: Send + Sync + 'static, { @@ -272,7 +268,7 @@ where self.handle_async_with_sync(handle_args).await } } - fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.metadata.clone() } } @@ -283,7 +279,7 @@ where Fut: Future> + Send + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, - Context: IntoContext, + Context: crate::Context, Params: Send + Sync, InheritedParams: Send + Sync, { @@ -293,14 +289,14 @@ where type Err = E; } -impl Handler +impl HandlerFor for FromFnAsync> where F: Fn(HandlerArgs) -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, - Context: IntoContext, + Context: crate::Context, Params: Send + Sync + 'static, InheritedParams: Send + Sync + 'static, { @@ -310,7 +306,7 @@ where ) -> Result { (self.function)(handle_args).await } - fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.metadata.clone() } } @@ -327,9 +323,9 @@ where type Err = E; } -impl Handler for FromFn +impl HandlerFor for FromFn where - Context: IntoContext, + Context: crate::Context, F: Fn() -> Result + Send + Sync + Clone + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, @@ -347,7 +343,7 @@ where self.handle_async_with_sync(handle_args).await } } - fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.metadata.clone() } } @@ -364,9 +360,9 @@ where type Err = E; } -impl Handler for FromFnAsync +impl HandlerFor for FromFnAsync where - Context: IntoContext, + Context: crate::Context, F: Fn() -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, T: Send + Sync + 'static, @@ -375,14 +371,14 @@ where async fn handle_async(&self, _: HandlerArgsFor) -> Result { (self.function)().await } - fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.metadata.clone() } } impl HandlerTypes for FromFn where - Context: IntoContext, + Context: crate::Context, F: Fn(Context) -> Result + Send + Sync + Clone + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, @@ -393,9 +389,9 @@ where type Err = E; } -impl Handler for FromFn +impl HandlerFor for FromFn where - Context: IntoContext, + Context: crate::Context, F: Fn(Context) -> Result + Send + Sync + Clone + 'static, T: Send + Sync + 'static, E: Send + Sync + 'static, @@ -416,13 +412,13 @@ where self.handle_async_with_sync(handle_args).await } } - fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.metadata.clone() } } impl HandlerTypes for FromFnAsync where - Context: IntoContext, + Context: crate::Context, F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, T: Send + Sync + 'static, @@ -434,9 +430,9 @@ where type Err = E; } -impl Handler for FromFnAsync +impl HandlerFor for FromFnAsync where - Context: IntoContext, + Context: crate::Context, F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, T: Send + Sync + 'static, @@ -448,14 +444,14 @@ where ) -> Result { (self.function)(handle_args.context).await } - fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.metadata.clone() } } impl HandlerTypes for FromFn where - Context: IntoContext, + Context: crate::Context, F: Fn(Context, Params) -> Result + Send + Sync + Clone + 'static, Params: DeserializeOwned + Send + Sync + 'static, T: Send + Sync + 'static, @@ -467,9 +463,9 @@ where type Err = E; } -impl Handler for FromFn +impl HandlerFor for FromFn where - Context: IntoContext, + Context: crate::Context, F: Fn(Context, Params) -> Result + Send + Sync + Clone + 'static, Params: DeserializeOwned + Send + Sync + 'static, T: Send + Sync + 'static, @@ -494,13 +490,13 @@ where self.handle_async_with_sync(handle_args).await } } - fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.metadata.clone() } } impl HandlerTypes for FromFnAsync where - Context: IntoContext, + Context: crate::Context, F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, Params: DeserializeOwned + Send + Sync + 'static, @@ -513,10 +509,10 @@ where type Err = E; } -impl Handler +impl HandlerFor for FromFnAsync where - Context: IntoContext, + Context: crate::Context, F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, Params: DeserializeOwned + Send + Sync + 'static, @@ -532,7 +528,7 @@ where } = handle_args; (self.function)(context, params).await } - fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.metadata.clone() } } @@ -540,7 +536,7 @@ where impl HandlerTypes for FromFn where - Context: IntoContext, + Context: crate::Context, F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, Params: DeserializeOwned + Send + Sync + 'static, InheritedParams: Send + Sync + 'static, @@ -553,10 +549,10 @@ where type Err = E; } -impl Handler +impl HandlerFor for FromFn where - Context: IntoContext, + Context: crate::Context, F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, Params: DeserializeOwned + Send + Sync + 'static, InheritedParams: Send + Sync + 'static, @@ -585,14 +581,14 @@ where self.handle_async_with_sync(handle_args).await } } - fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.metadata.clone() } } impl HandlerTypes for FromFnAsync where - Context: IntoContext, + Context: crate::Context, F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, Params: DeserializeOwned + Send + Sync + 'static, @@ -606,10 +602,10 @@ where type Err = E; } -impl Handler +impl HandlerFor for FromFnAsync where - Context: IntoContext, + Context: crate::Context, F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, Fut: Future> + Send + 'static, Params: DeserializeOwned + Send + Sync + 'static, @@ -629,7 +625,7 @@ where } = handle_args; (self.function)(context, params, inherited_params).await } - fn metadata(&self, _: VecDeque<&'static str>, _: TypeId) -> OrdMap<&'static str, Value> { + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { self.metadata.clone() } } diff --git a/src/handler/mod.rs b/src/handler/mod.rs index c7bb9a5..d47aaa4 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -6,13 +6,12 @@ use std::sync::Arc; use clap::{ArgMatches, Command, Parser}; use futures::Future; -use imbl_value::imbl::{OrdMap, OrdSet}; +use imbl_value::imbl::OrdMap; use imbl_value::Value; use serde::de::DeserializeOwned; use serde::{Deserialize, Serialize}; use yajrc::RpcError; -use crate::context::{AnyContext, IntoContext}; use crate::util::{internal_error, invalid_params, Flat}; pub mod adapters; @@ -23,19 +22,18 @@ pub use adapters::*; pub use from_fn::*; pub use parent::*; -pub(crate) struct HandleAnyArgs { - pub(crate) context: AnyContext, +pub(crate) struct HandleAnyArgs { + pub(crate) context: Context, pub(crate) parent_method: VecDeque<&'static str>, pub(crate) method: VecDeque<&'static str>, pub(crate) params: Value, pub(crate) inherited: Inherited, } -impl HandleAnyArgs { - fn downcast( - self, - ) -> Result, imbl_value::Error> +impl HandleAnyArgs { + fn downcast(self) -> Result, imbl_value::Error> where - H: HandlerTypes, + H: HandlerTypes, + H::InheritedParams: OrEmpty, H::Params: DeserializeOwned, { let Self { @@ -46,84 +44,77 @@ impl HandleAnyArgs { inherited, } = self; Ok(HandlerArgs { - context: Context::downcast(context).map_err(|_| imbl_value::Error { - kind: imbl_value::ErrorKind::Deserialization, - source: serde::ser::Error::custom("context does not match expected"), - })?, + context, parent_method, method, params: imbl_value::from_value(params.clone())?, - inherited_params: inherited, + inherited_params: OrEmpty::from_t(inherited), raw_params: params, }) } } #[async_trait::async_trait] -pub(crate) trait HandleAny: Send + Sync { +pub(crate) trait HandleAny: Send + Sync { type Inherited: Send; - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result; + fn handle_sync( + &self, + handle_args: HandleAnyArgs, + ) -> Result; async fn handle_async( &self, - handle_args: HandleAnyArgs, + handle_args: HandleAnyArgs, ) -> Result; - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value>; - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option>; - fn cli(&self) -> Option<&dyn CliBindingsAny>; + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value>; + fn method_from_dots(&self, method: &str) -> Option>; + fn cli(&self) -> Option<&dyn CliBindingsAny>; } #[async_trait::async_trait] -impl HandleAny for Arc { +impl> HandleAny for Arc { type Inherited = T::Inherited; - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + fn handle_sync( + &self, + handle_args: HandleAnyArgs, + ) -> Result { self.deref().handle_sync(handle_args) } async fn handle_async( &self, - handle_args: HandleAnyArgs, + handle_args: HandleAnyArgs, ) -> Result { self.deref().handle_async(handle_args).await } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.deref().metadata(method, ctx_ty) + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.deref().metadata(method) } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.deref().method_from_dots(method, ctx_ty) + fn method_from_dots(&self, method: &str) -> Option> { + self.deref().method_from_dots(method) } - fn cli(&self) -> Option<&dyn CliBindingsAny> { + fn cli(&self) -> Option<&dyn CliBindingsAny> { self.deref().cli() } } -pub(crate) trait CliBindingsAny { +pub(crate) trait CliBindingsAny { type Inherited; - fn cli_command(&self, ctx_ty: TypeId) -> Command; + fn cli_command(&self) -> Command; fn cli_parse( &self, matches: &ArgMatches, - ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; fn cli_display( &self, - handle_args: HandleAnyArgs, + handle_args: HandleAnyArgs, result: Value, ) -> Result<(), RpcError>; } -pub trait CliBindings: HandlerTypes { +pub trait CliBindings: HandlerTypes { const NO_CLI: bool = false; - fn cli_command(&self, ctx_ty: TypeId) -> Command; + fn cli_command(&self) -> Command; fn cli_parse( &self, matches: &ArgMatches, - ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; fn cli_display( &self, @@ -132,7 +123,7 @@ pub trait CliBindings: HandlerTypes { ) -> Result<(), Self::Err>; } -pub trait PrintCliResult: HandlerTypes { +pub trait PrintCliResult: HandlerTypes { fn print( &self, handle_args: HandlerArgsFor, @@ -141,53 +132,56 @@ pub trait PrintCliResult: HandlerTypes { } #[allow(private_interfaces)] -pub struct DynHandler(Arc>); -impl DynHandler { - pub fn iter + CliBindings>( - h: H, - ) -> Option, Self)>> { - iter_from_ctx_and_handler(ctx, handler) +pub struct DynHandler(Arc>); +impl DynHandler { + pub fn new(handler: H) -> Option + where + C: crate::Context, + WithContext: Handler, + { + WithContext::::new(handler).handler_for::() } } -impl Clone for DynHandler { +impl Clone for DynHandler { fn clone(&self) -> Self { Self(self.0.clone()) } } #[async_trait::async_trait] -impl HandleAny for DynHandler { +impl HandleAny + for DynHandler +{ type Inherited = Inherited; - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + fn handle_sync( + &self, + handle_args: HandleAnyArgs, + ) -> Result { self.0.handle_sync(handle_args) } async fn handle_async( &self, - handle_args: HandleAnyArgs, + handle_args: HandleAnyArgs, ) -> Result { self.0.handle_async(handle_args).await } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.0.metadata(method, ctx_ty) + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.0.metadata(method) } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.0.method_from_dots(method, ctx_ty) + fn method_from_dots(&self, method: &str) -> Option> { + self.0.method_from_dots(method) } - fn cli(&self) -> Option<&dyn CliBindingsAny> { + fn cli(&self) -> Option<&dyn CliBindingsAny> { self.0.cli() } } #[allow(type_alias_bounds)] -pub type HandlerArgsFor = +pub type HandlerArgsFor = HandlerArgs; #[derive(Debug, Clone)] pub struct HandlerArgs< - Context: IntoContext, + Context: crate::Context, Params: Send + Sync = Empty, InheritedParams: Send + Sync = Empty, > { @@ -206,7 +200,9 @@ pub trait HandlerTypes { type Err: Send + Sync; } -pub trait Handler: HandlerTypes + Clone + Send + Sync + 'static { +pub trait HandlerFor: + HandlerTypes + Clone + Send + Sync + 'static +{ fn handle_sync( &self, handle_args: HandlerArgsFor, @@ -241,18 +237,11 @@ pub trait Handler: HandlerTypes + Clone + Send + Sync + 's } } #[allow(unused_variables)] - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { OrdMap::new() } - fn contexts(&self) -> Option> { - Context::type_ids() - } #[allow(unused_variables)] - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + fn method_from_dots(&self, method: &str) -> Option> { if method.is_empty() { Some(VecDeque::new()) } else { @@ -261,11 +250,53 @@ pub trait Handler: HandlerTypes + Clone + Send + Sync + 's } } -pub(crate) struct AnyHandler { +pub trait Handler { + type H: HandlerTypes; + fn handler_for(self) -> Option>; +} + +pub struct WithContext { _phantom: PhantomData, handler: H, } -impl AnyHandler { +impl WithContext { + pub fn new(handler: H) -> Self { + Self { + _phantom: PhantomData, + handler, + } + } +} + +impl Handler for WithContext +where + Context: crate::Context, + H: HandlerFor + CliBindings, + H::Ok: Serialize + DeserializeOwned, + H::Params: DeserializeOwned, + H::InheritedParams: OrEmpty, + RpcError: From, + Inherited: Send + Sync + 'static, +{ + type H = H; + fn handler_for(self) -> Option> { + if TypeId::of::() == TypeId::of::() { + Some(unsafe { + std::mem::transmute::, DynHandler>( + DynHandler(Arc::new(AnyHandler::new(self.handler))), + ) + }) + } else { + None + } + } +} + +pub(crate) struct AnyHandler { + _phantom: PhantomData<(Context, Inherited)>, + handler: H, +} +impl AnyHandler { pub(crate) fn new(handler: H) -> Self { Self { _phantom: PhantomData, @@ -273,7 +304,7 @@ impl AnyHandler { } } } -impl std::fmt::Debug for AnyHandler { +impl std::fmt::Debug for AnyHandler { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("AnyHandler") .field("handler", &self.handler) @@ -282,46 +313,47 @@ impl std::fmt::Debug for AnyHandler { } #[async_trait::async_trait] -impl HandleAny for AnyHandler +impl HandleAny for AnyHandler where - Context: IntoContext, - H: Handler + CliBindings, + Context: crate::Context, + H: HandlerFor + CliBindings, H::Params: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, + H::InheritedParams: OrEmpty, RpcError: From, + Inherited: Send + Sync, { - type Inherited = H::InheritedParams; - fn handle_sync(&self, handle_args: HandleAnyArgs) -> Result { + type Inherited = Inherited; + fn handle_sync( + &self, + handle_args: HandleAnyArgs, + ) -> Result { imbl_value::to_value( &self .handler - .handle_sync(handle_args.downcast::<_, H>().map_err(invalid_params)?)?, + .handle_sync(handle_args.downcast::().map_err(invalid_params)?)?, ) .map_err(internal_error) } async fn handle_async( &self, - handle_args: HandleAnyArgs, + handle_args: HandleAnyArgs, ) -> Result { imbl_value::to_value( &self .handler - .handle_async(handle_args.downcast::<_, H>().map_err(invalid_params)?) + .handle_async(handle_args.downcast::().map_err(invalid_params)?) .await?, ) .map_err(internal_error) } - fn metadata( - &self, - method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { - self.handler.metadata(method, ctx_ty) + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.handler.metadata(method) } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { - self.handler.method_from_dots(method, ctx_ty) + fn method_from_dots(&self, method: &str) -> Option> { + self.handler.method_from_dots(method) } - fn cli(&self) -> Option<&dyn CliBindingsAny> { + fn cli(&self) -> Option<&dyn CliBindingsAny> { if H::NO_CLI { None } else { @@ -330,33 +362,34 @@ where } } -impl CliBindingsAny for AnyHandler +impl CliBindingsAny for AnyHandler where - Context: IntoContext, + Context: crate::Context, H: CliBindings, H::Params: DeserializeOwned, H::Ok: Serialize + DeserializeOwned, RpcError: From, + H::InheritedParams: OrEmpty, + Inherited: Send + Sync, { - type Inherited = H::InheritedParams; - fn cli_command(&self, ctx_ty: TypeId) -> Command { - self.handler.cli_command(ctx_ty) + type Inherited = Inherited; + fn cli_command(&self) -> Command { + self.handler.cli_command() } fn cli_parse( &self, matches: &ArgMatches, - ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - self.handler.cli_parse(matches, ctx_ty) + self.handler.cli_parse(matches) } fn cli_display( &self, - handle_args: HandleAnyArgs, + handle_args: HandleAnyArgs, result: Value, ) -> Result<(), RpcError> { self.handler .cli_display( - handle_args.downcast::<_, H>().map_err(invalid_params)?, + handle_args.downcast::().map_err(invalid_params)?, imbl_value::from_value(result).map_err(internal_error)?, ) .map_err(RpcError::from) @@ -375,7 +408,7 @@ impl OrEmpty for T { } } impl OrEmpty> for Empty { - fn from_t(t: Flat) -> Self { + fn from_t(_: Flat) -> Self { Empty {} } } diff --git a/src/handler/parent.rs b/src/handler/parent.rs index cd8ff91..217bce4 100644 --- a/src/handler/parent.rs +++ b/src/handler/parent.rs @@ -1,42 +1,17 @@ -use std::any::TypeId; use std::collections::VecDeque; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; -use imbl_value::imbl::{OrdMap, OrdSet}; +use imbl_value::imbl::OrdMap; use imbl_value::Value; use serde::Serialize; use yajrc::RpcError; use crate::util::{combine, Flat, PhantomData}; use crate::{ - AnyContext, CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, - HandlerArgsFor, HandlerTypes, IntoContext, + CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, HandlerArgsFor, + HandlerFor, HandlerTypes, WithContext, }; -pub(crate) fn iter_from_ctx_and_handler( - ctx: Option>, - handler: DynHandler, -) -> impl IntoIterator, DynHandler)> { - if let Some(ctx) = ctx { - itertools::Either::Left(ctx.into_iter().map(Some)) - } else { - itertools::Either::Right(std::iter::once(None)) - } - .map(move |ctx| (ctx, handler.clone())) -} - -pub(crate) fn intersect_type_ids( - a: Option>, - b: Option>, -) -> Option> { - match (a, b) { - (None, None) => None, - (Some(a), None) => Some(a), - (None, Some(b)) => Some(b), - (Some(a), Some(b)) => Some(a.intersection(b)), - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub(crate) struct Name(pub(crate) Option<&'static str>); impl<'a> std::borrow::Borrow> for Name { @@ -45,47 +20,30 @@ impl<'a> std::borrow::Borrow> for Name { } } -pub(crate) struct SubcommandMap( - pub(crate) OrdMap, DynHandler>>, +pub(crate) struct SubcommandMap( + pub(crate) OrdMap>, ); -impl Clone for SubcommandMap { +impl Clone for SubcommandMap { fn clone(&self) -> Self { Self(self.0.clone()) } } -impl SubcommandMap { - fn insert( - &mut self, - name: Option<&'static str>, - handlers: impl IntoIterator, DynHandler)>, - ) { - let mut for_name = self.0.remove(&name).unwrap_or_default(); - for (ctx_ty, handler) in handlers { - for_name.insert(ctx_ty, handler); - } - self.0.insert(Name(name), for_name); +impl SubcommandMap { + fn insert(&mut self, name: Option<&'static str>, handler: DynHandler) { + self.0.insert(Name(name), handler); } - - fn get<'a>( - &'a self, - ctx_ty: TypeId, - name: Option<&str>, - ) -> Option<(Name, &'a DynHandler)> { - if let Some((name, for_name)) = self.0.get_key_value(&name) { - if let Some(for_ctx) = for_name.get(&Some(ctx_ty)) { - Some((*name, for_ctx)) - } else { - for_name.get(&None).map(|h| (*name, h)) - } + fn get<'a>(&'a self, name: Option<&str>) -> Option<(Name, &'a DynHandler)> { + if let Some((name, handler)) = self.0.get_key_value(&name) { + Some((*name, handler)) } else { None } } } -pub struct ParentHandler { +pub struct ParentHandler { _phantom: PhantomData, - pub(crate) subcommands: SubcommandMap>, + pub(crate) subcommands: SubcommandMap>, metadata: OrdMap<&'static str, Value>, } impl ParentHandler { @@ -120,33 +78,25 @@ impl std::fmt::Debug } } -impl +impl ParentHandler { - fn get_contexts(&self) -> Option> { - let mut set = OrdSet::new(); - for ctx_ty in self.subcommands.0.values().flat_map(|c| c.keys()) { - set.insert((*ctx_ty)?); - } - Some(set) - } - #[allow(private_bounds)] - pub fn subcommand(mut self, name: &'static str, handler: H) -> Self + pub fn subcommand(mut self, name: &'static str, handler: H) -> Self where - H: Handler> + CliBindings, + WithContext: Handler>, { - if let Some(h) = DynHandler::iter(handler) { + if let Some(h) = DynHandler::new(handler) { self.subcommands.insert(name.into(), h); } self } - #[allow(private_bounds)] - pub fn root_handler(mut self, handler: H) -> Self + pub fn root_handler(mut self, handler: H) -> Self where - H: Handler> - + CliBindings, + WithContext: Handler>, + as Handler>>::H: + HandlerTypes, { - if let Some((c, h)) = DynHandler::iter(handler) { + if let Some(h) = DynHandler::new(handler) { self.subcommands.insert(None, h); } self @@ -165,10 +115,10 @@ where type Err = RpcError; } -impl Handler +impl HandlerFor for ParentHandler where - Context: IntoContext, + Context: crate::Context, Params: Send + Sync + 'static, InheritedParams: Serialize + Send + Sync + 'static, { @@ -187,9 +137,9 @@ where if let Some(cmd) = cmd { parent_method.push_back(cmd); } - if let Some((_, sub_handler)) = &self.subcommands.get(context.inner_type_id(), cmd) { + if let Some((_, sub_handler)) = &self.subcommands.get(cmd) { sub_handler.handle_sync(HandleAnyArgs { - context: context.upcast(), + context, parent_method, method, params: raw_params, @@ -214,10 +164,10 @@ where if let Some(cmd) = cmd { parent_method.push_back(cmd); } - if let Some((_, sub_handler)) = self.subcommands.get(context.inner_type_id(), cmd) { + if let Some((_, sub_handler)) = self.subcommands.get(cmd) { sub_handler .handle_async(HandleAnyArgs { - context: context.upcast(), + context, parent_method, method, params: raw_params, @@ -228,22 +178,15 @@ where Err(yajrc::METHOD_NOT_FOUND_ERROR) } } - fn metadata( - &self, - mut method: VecDeque<&'static str>, - ctx_ty: TypeId, - ) -> OrdMap<&'static str, Value> { + fn metadata(&self, mut method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { let metadata = self.metadata.clone(); - if let Some((_, handler)) = self.subcommands.get(ctx_ty, method.pop_front()) { - handler.metadata(method, ctx_ty).union(metadata) + if let Some((_, handler)) = self.subcommands.get(method.pop_front()) { + handler.metadata(method).union(metadata) } else { metadata } } - fn contexts(&self) -> Option> { - self.get_contexts() - } - fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { + fn method_from_dots(&self, method: &str) -> Option> { let (head, tail) = if method.is_empty() { (None, None) } else { @@ -252,13 +195,13 @@ where .map(|(head, tail)| (Some(head), Some(tail))) .unwrap_or((Some(method), None)) }; - let (Name(name), h) = self.subcommands.get(ctx_ty, head)?; + let (Name(name), h) = self.subcommands.get(head)?; let mut res = VecDeque::new(); if let Some(name) = name { res.push_back(name); } if let Some(tail) = tail { - res.append(&mut h.method_from_dots(tail, ctx_ty)?); + res.append(&mut h.method_from_dots(tail)?); } Some(res) } @@ -267,25 +210,16 @@ where impl CliBindings for ParentHandler where - Context: IntoContext, + Context: crate::Context, Params: FromArgMatches + CommandFactory + Serialize + Send + Sync + 'static, InheritedParams: Serialize + Send + Sync + 'static, { - fn cli_command(&self, ctx_ty: TypeId) -> Command { + fn cli_command(&self) -> Command { let mut base = Params::command().subcommand_required(true); - for (name, handlers) in &self.subcommands.0 { - match ( - name, - if let Some(handler) = handlers.get(&Some(ctx_ty)) { - handler.cli() - } else if let Some(handler) = handlers.get(&None) { - handler.cli() - } else { - None - }, - ) { + for (name, handler) in &self.subcommands.0 { + match (name, handler.cli()) { (Name(Some(name)), Some(cli)) => { - base = base.subcommand(cli.cli_command(ctx_ty).name(name)); + base = base.subcommand(cli.cli_command().name(name)); } (Name(None), Some(_)) => { base = base.subcommand_required(false); @@ -298,7 +232,6 @@ where fn cli_parse( &self, matches: &ArgMatches, - ctx_ty: TypeId, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?) .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?; @@ -308,10 +241,10 @@ where }; if let Some((Name(Some(name)), cli)) = self .subcommands - .get(ctx_ty, name) + .get(name) .and_then(|(n, h)| h.cli().map(|c| (n, c))) { - let (mut method, params) = cli.cli_parse(matches, ctx_ty)?; + let (mut method, params) = cli.cli_parse(matches)?; method.push_front(name); Ok(( @@ -341,12 +274,12 @@ where } if let Some((_, cli)) = self .subcommands - .get(context.inner_type_id(), cmd) + .get(cmd) .and_then(|(n, h)| h.cli().map(|c| (n, c))) { cli.cli_display( HandleAnyArgs { - context: context.upcast(), + context, parent_method, method, params: raw_params, diff --git a/src/server/http.rs b/src/server/http.rs index 22cebb1..3bf5698 100644 --- a/src/server/http.rs +++ b/src/server/http.rs @@ -1,5 +1,3 @@ -use std::any::TypeId; - use axum::body::Body; use axum::extract::Request; use axum::handler::Handler; @@ -257,7 +255,7 @@ impl HttpServer { match self .inner .root_handler - .method_from_dots(req.method.as_str(), TypeId::of::()) + .method_from_dots(req.method.as_str()) { Some(a) => a, None => { @@ -267,7 +265,6 @@ impl HttpServer { } } }, - TypeId::of::(), ) .into_iter() .map(|(key, value)| (key.into(), value)) diff --git a/src/server/mod.rs b/src/server/mod.rs index 8172522..3da0c4c 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -1,4 +1,3 @@ -use std::any::TypeId; use std::collections::VecDeque; use std::sync::Arc; @@ -8,7 +7,7 @@ use imbl_value::Value; use yajrc::{RpcError, RpcMethod}; use crate::util::{invalid_request, JobRunner}; -use crate::{AnyHandler, HandleAny, HandleAnyArgs, IntoContext, ParentHandler}; +use crate::{AnyHandler, Empty, HandleAny, HandleAnyArgs, ParentHandler}; pub type GenericRpcMethod = yajrc::GenericRpcMethod; pub type RpcRequest = yajrc::RpcRequest; @@ -23,7 +22,7 @@ pub use socket::*; pub struct Server { make_ctx: Arc BoxFuture<'static, Result> + Send + Sync>, - root_handler: Arc>>, + root_handler: Arc>>, } impl Clone for Server { fn clone(&self) -> Self { @@ -55,14 +54,13 @@ impl Server { let (make_ctx, root_handler, method) = ( self.make_ctx.clone(), self.root_handler.clone(), - self.root_handler - .method_from_dots(method, TypeId::of::()), + self.root_handler.method_from_dots(method), ); async move { root_handler .handle_async(HandleAnyArgs { - context: make_ctx().await?.upcast(), + context: make_ctx().await?, parent_method: VecDeque::new(), method: method.ok_or_else(|| yajrc::METHOD_NOT_FOUND_ERROR)?, params, diff --git a/tests/handler.rs b/tests/handler.rs index 8b9bde2..1a0d9cf 100644 --- a/tests/handler.rs +++ b/tests/handler.rs @@ -7,8 +7,8 @@ use clap::Parser; use futures::future::ready; use imbl_value::Value; use rpc_toolkit::{ - call_remote_socket, from_fn, from_fn_async, AnyContext, CallRemote, CliApp, Context, Empty, - HandlerExt, ParentHandler, Server, + call_remote_socket, from_fn, from_fn_async, CallRemote, CliApp, Context, Empty, HandlerExt, + ParentHandler, Server, }; use serde::{Deserialize, Serialize}; use tokio::runtime::{Handle, Runtime}; @@ -102,7 +102,7 @@ fn make_server() -> Server { Server::new(move || ready(Ok(ctx.clone())), make_api()) } -fn make_api() -> ParentHandler { +fn make_api() -> ParentHandler { async fn a_hello(_: CliContext) -> Result { Ok::<_, RpcError>("Async Subcommand".to_string()) } @@ -118,8 +118,8 @@ fn make_api() -> ParentHandler { struct InheritParams { donde: String, } - ParentHandler::new() - .subcommand( + ParentHandler::::new() + .subcommand::( "echo", from_fn_async( |c: ServerContext, EchoParams { next }: EchoParams| async move { @@ -133,14 +133,14 @@ fn make_api() -> ParentHandler { ) .subcommand( "hello", - from_fn(|_: AnyContext, HelloParams { whom }: HelloParams| { + from_fn(|_: C, HelloParams { whom }: HelloParams| { Ok::<_, RpcError>(format!("Hello {whom}").to_string()) }), ) .subcommand("a_hello", from_fn_async(a_hello)) .subcommand( "dondes", - ParentHandler::::new().subcommand( + ParentHandler::::new().subcommand( "donde", from_fn(|c: CliContext, _: (), donde| { Ok::<_, RpcError>( @@ -157,7 +157,7 @@ fn make_api() -> ParentHandler { ) .subcommand( "fizz", - ParentHandler::::new().root_handler( + ParentHandler::::new().root_handler( from_fn(|c: CliContext, _: Empty, InheritParams { donde }| { Ok::<_, RpcError>( format!( @@ -172,7 +172,7 @@ fn make_api() -> ParentHandler { ) .subcommand( "error", - ParentHandler::::new().root_handler( + ParentHandler::::new().root_handler( from_fn(|_: CliContext, _: Empty, InheritParams { .. }| { Err::(RpcError { code: 1, From 6bb66a4400a6ad64a05e1ffb86415999d793c6c6 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 22:07:20 -0600 Subject: [PATCH 67/79] add cli bindings for display fns --- src/handler/adapters.rs | 222 ++++++++++++++++------------------------ 1 file changed, 90 insertions(+), 132 deletions(-) diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index 28986ed..04f66ec 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -253,6 +253,36 @@ where Ok(()) } } +impl CliBindings for NoDisplay +where + Context: crate::Context, + Self: HandlerTypes, + Self::Params: CommandFactory + FromArgMatches + Serialize, + Self: PrintCliResult, +{ + fn cli_command(&self) -> clap::Command { + Self::Params::command() + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + handle_args: HandlerArgsFor, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.print(handle_args, result) + } +} #[derive(Clone, Debug)] pub struct CustomDisplay { @@ -364,6 +394,36 @@ where ) } } +impl CliBindings for CustomDisplay +where + Context: crate::Context, + Self: HandlerTypes, + Self::Params: CommandFactory + FromArgMatches + Serialize, + Self: PrintCliResult, +{ + fn cli_command(&self) -> clap::Command { + Self::Params::command() + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + handle_args: HandlerArgsFor, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.print(handle_args, result) + } +} pub struct CustomDisplayFn { _phantom: PhantomData, @@ -484,6 +544,36 @@ where ) } } +impl CliBindings for CustomDisplayFn +where + Context: crate::Context, + Self: HandlerTypes, + Self::Params: CommandFactory + FromArgMatches + Serialize, + Self: PrintCliResult, +{ + fn cli_command(&self) -> clap::Command { + Self::Params::command() + } + fn cli_parse( + &self, + matches: &clap::ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + Self::Params::from_arg_matches(matches).and_then(|a| { + Ok(( + VecDeque::new(), + imbl_value::to_value(&a) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?, + )) + }) + } + fn cli_display( + &self, + handle_args: HandlerArgsFor, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.print(handle_args, result) + } +} pub struct RemoteCaller { _phantom: PhantomData<(Context, RemoteContext)>, @@ -503,138 +593,6 @@ impl Debug for RemoteCaller HandlerTypes for RemoteCaller -// where -// H: HandlerTypes, -// { -// type Params = H::Params; -// type InheritedParams = H::InheritedParams; -// type Ok = H::Ok; -// type Err = H::Err; -// } -// impl HandlerFor -// for RemoteCaller -// where -// Context: CallRemote, -// RemoteContext: crate::Context, -// H: HandlerFor, -// H::Params: Serialize, -// H::InheritedParams: Serialize, -// H::Ok: DeserializeOwned, -// H::Err: From, -// { -// async fn handle_async( -// &self, -// HandlerArgs { -// context, -// parent_method, -// method, -// params, -// inherited_params, -// raw_params, -// }: HandlerArgsFor, -// ) -> Result { -// self.handler -// .handle_async(HandlerArgs { -// context, -// parent_method, -// method, -// params, -// inherited_params, -// raw_params, -// }) -// .await -// } -// fn metadata( -// &self, -// method: VecDeque<&'static str>, -// ctx_ty: TypeId, -// ) -> OrdMap<&'static str, Value> { -// self.handler.metadata(method, ctx_ty) -// } -// fn contexts(&self) -> Option> { -// Context::type_ids() -// } -// fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { -// self.handler.method_from_dots(method, ctx_ty) -// } -// } -// impl HandlerFor for RemoteCaller -// where -// Context: CallRemote, -// RemoteContext: crate::Context, -// H: HandlerFor, -// H::Params: Serialize, -// H::InheritedParams: Serialize, -// H::Ok: DeserializeOwned, -// H::Err: From, -// { -// async fn handle_async( -// &self, -// HandlerArgs { -// context, -// parent_method, -// method, -// params, -// inherited_params, -// raw_params, -// }: HandlerArgsFor, -// ) -> Result { -// let full_method = parent_method.into_iter().chain(method).collect::>(); -// match context -// .call_remote(&full_method.join("."), raw_params, Empty {}) -// .await -// { -// Ok(a) => imbl_value::from_value(a) -// .map_err(internal_error) -// .map_err(Self::Err::from), -// Err(e) => Err(Self::Err::from(e)), -// } -// } -// fn metadata( -// &self, -// method: VecDeque<&'static str>, -// ctx_ty: TypeId, -// ) -> OrdMap<&'static str, Value> { -// self.handler.metadata(method, ctx_ty) -// } -// fn contexts(&self) -> Option> { -// Context::type_ids() -// } -// fn method_from_dots(&self, method: &str, ctx_ty: TypeId) -> Option> { -// self.handler.method_from_dots(method, ctx_ty) -// } -// } -// impl PrintCliResult for RemoteCaller -// where -// Context: crate::Context, -// H: PrintCliResult, -// { -// fn print( -// &self, -// HandlerArgs { -// context, -// parent_method, -// method, -// params, -// inherited_params, -// raw_params, -// }: HandlerArgsFor, -// result: Self::Ok, -// ) -> Result<(), Self::Err> { -// self.handler.print( -// HandlerArgs { -// context, -// parent_method, -// method, -// params, -// inherited_params, -// raw_params, -// }, -// result, -// ) -// } -// } impl Handler for WithContext> where From cbbeea049540cc3d2b32409f33a1b7bcbe161479 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 22:43:48 -0600 Subject: [PATCH 68/79] fix custom_display_fn --- src/handler/adapters.rs | 11 ++++------- tests/handler.rs | 1 + 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index 04f66ec..78fb735 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -30,9 +30,9 @@ pub trait HandlerExt: HandlerFor + Sized { fn with_custom_display_fn( self, display: F, - ) -> CustomDisplayFn + ) -> CustomDisplayFn where - F: Fn(HandlerArgsFor, Self::Ok) -> Result<(), Self::Err>; + F: Fn(HandlerArgsFor, Self::Ok) -> Result<(), Self::Err>; fn with_inherited( self, f: F, @@ -64,12 +64,9 @@ impl + Sized> HandlerExt( - self, - display: F, - ) -> CustomDisplayFn + fn with_custom_display_fn(self, display: F) -> CustomDisplayFn where - F: Fn(HandlerArgsFor, Self::Ok) -> Result<(), Self::Err>, + F: Fn(HandlerArgsFor, Self::Ok) -> Result<(), Self::Err>, { CustomDisplayFn { _phantom: PhantomData::new(), diff --git a/tests/handler.rs b/tests/handler.rs index 1a0d9cf..7128ba2 100644 --- a/tests/handler.rs +++ b/tests/handler.rs @@ -129,6 +129,7 @@ fn make_api() -> ParentHandler { )) }, ) + .with_custom_display_fn(|_, a| Ok(println!("{a}"))) .with_call_remote::(), ) .subcommand( From 0603928164ce2862b9bdd6021f371a6151ad8688 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 3 May 2024 23:43:27 -0600 Subject: [PATCH 69/79] remove requirement for type args on subcommand --- src/handler/adapters.rs | 13 ++++++------- tests/handler.rs | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index 78fb735..00a49ca 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -590,13 +590,12 @@ impl Debug for RemoteCaller Handler - for WithContext> +impl Handler + for WithContext> where - Context: crate::Context, - LocalContext: crate::Context + CallRemote, + Context: crate::Context + CallRemote, RemoteContext: crate::Context, - H: HandlerFor + CliBindings, + H: HandlerFor + CliBindings, H::Ok: Serialize + DeserializeOwned, H::Err: From, H::Params: Serialize + DeserializeOwned, @@ -608,8 +607,8 @@ where fn handler_for(self) -> Option> { if TypeId::of::() == TypeId::of::() { DynHandler::new(self.handler.handler.no_cli()) - } else if TypeId::of::() == TypeId::of::() { - DynHandler::new(CallRemoteHandler::::new( + } else if TypeId::of::() == TypeId::of::() { + DynHandler::new(CallRemoteHandler::::new( self.handler.handler, )) } else { diff --git a/tests/handler.rs b/tests/handler.rs index 7128ba2..4e2ce12 100644 --- a/tests/handler.rs +++ b/tests/handler.rs @@ -119,7 +119,7 @@ fn make_api() -> ParentHandler { donde: String, } ParentHandler::::new() - .subcommand::( + .subcommand( "echo", from_fn_async( |c: ServerContext, EchoParams { next }: EchoParams| async move { From 00d3ab7e70002eb72681541f9a3613bcb4524f32 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Sat, 4 May 2024 03:07:11 -0600 Subject: [PATCH 70/79] debug --- src/handler/adapters.rs | 4 ++-- src/handler/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index 00a49ca..0ccbdd5 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -605,9 +605,9 @@ where { type H = H; fn handler_for(self) -> Option> { - if TypeId::of::() == TypeId::of::() { + if dbg!(TypeId::of::()) == dbg!(TypeId::of::()) { DynHandler::new(self.handler.handler.no_cli()) - } else if TypeId::of::() == TypeId::of::() { + } else if dbg!(TypeId::of::()) == dbg!(TypeId::of::()) { DynHandler::new(CallRemoteHandler::::new( self.handler.handler, )) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index d47aaa4..0ec5904 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -280,7 +280,7 @@ where { type H = H; fn handler_for(self) -> Option> { - if TypeId::of::() == TypeId::of::() { + if dbg!(TypeId::of::()) == dbg!(TypeId::of::()) { Some(unsafe { std::mem::transmute::, DynHandler>( DynHandler(Arc::new(AnyHandler::new(self.handler))), From 7f53e38aeeda90e9537512ce7999dc8ca7e9a701 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Sat, 4 May 2024 03:23:24 -0600 Subject: [PATCH 71/79] debug --- src/handler/mod.rs | 6 ++++++ src/handler/parent.rs | 8 +++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 0ec5904..87d3604 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -1,5 +1,6 @@ use std::any::TypeId; use std::collections::VecDeque; +use std::fmt::Debug; use std::marker::PhantomData; use std::ops::Deref; use std::sync::Arc; @@ -147,6 +148,11 @@ impl Clone for DynHandler { Self(self.0.clone()) } } +impl Debug for DynHandler { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DynHandler").finish() + } +} #[async_trait::async_trait] impl HandleAny for DynHandler diff --git a/src/handler/parent.rs b/src/handler/parent.rs index 217bce4..9356b27 100644 --- a/src/handler/parent.rs +++ b/src/handler/parent.rs @@ -1,4 +1,5 @@ use std::collections::VecDeque; +use std::fmt::Debug; use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; use imbl_value::imbl::OrdMap; @@ -28,6 +29,11 @@ impl Clone for SubcommandMap { Self(self.0.clone()) } } +impl Debug for SubcommandMap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_map().entries(self.0.iter()).finish() + } +} impl SubcommandMap { fn insert(&mut self, name: Option<&'static str>, handler: DynHandler) { self.0.insert(Name(name), handler); @@ -73,7 +79,7 @@ impl std::fmt::Debug { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("ParentHandler") - // .field(&self.subcommands) + .field(&self.subcommands) .finish() } } From f5566840bb9af0743612fede4034f5a390cd1eee Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Sat, 4 May 2024 03:36:04 -0600 Subject: [PATCH 72/79] remove dbg --- src/handler/adapters.rs | 4 ++-- src/handler/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index 0ccbdd5..00a49ca 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -605,9 +605,9 @@ where { type H = H; fn handler_for(self) -> Option> { - if dbg!(TypeId::of::()) == dbg!(TypeId::of::()) { + if TypeId::of::() == TypeId::of::() { DynHandler::new(self.handler.handler.no_cli()) - } else if dbg!(TypeId::of::()) == dbg!(TypeId::of::()) { + } else if TypeId::of::() == TypeId::of::() { DynHandler::new(CallRemoteHandler::::new( self.handler.handler, )) diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 87d3604..99de2cf 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -286,7 +286,7 @@ where { type H = H; fn handler_for(self) -> Option> { - if dbg!(TypeId::of::()) == dbg!(TypeId::of::()) { + if TypeId::of::() == TypeId::of::() { Some(unsafe { std::mem::transmute::, DynHandler>( DynHandler(Arc::new(AnyHandler::new(self.handler))), From 5a24903031e72ac75fd23889215361edc7b20842 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 31 May 2024 14:52:52 -0600 Subject: [PATCH 73/79] fix call remote --- src/cli.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/cli.rs b/src/cli.rs index 700a69b..75a1592 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,6 +3,7 @@ use std::ffi::OsString; use clap::{CommandFactory, FromArgMatches}; use futures::Future; +use imbl_value::imbl::OrdMap; use imbl_value::Value; use reqwest::header::{ACCEPT, CONTENT_LENGTH, CONTENT_TYPE}; use reqwest::{Client, Method}; @@ -246,6 +247,12 @@ where Err(e) => Err(Self::Err::from(e)), } } + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.handler.metadata(method) + } + fn method_from_dots(&self, method: &str) -> Option> { + self.handler.method_from_dots(method) + } } impl PrintCliResult for CallRemoteHandler From f608480034942f1f521ab95949ab33fbc51d99a9 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Mon, 24 Jun 2024 17:16:52 -0600 Subject: [PATCH 74/79] handle single-threaded rt --- src/context.rs | 8 +++++--- src/handler/mod.rs | 23 +++++++++++++---------- tests/handler.rs | 11 +++++++---- 3 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/context.rs b/src/context.rs index b1b2054..d7b1462 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,7 +1,9 @@ -use tokio::runtime::Handle; +use std::sync::Arc; + +use tokio::runtime::Runtime; pub trait Context: Send + Sync + 'static { - fn runtime(&self) -> Handle { - Handle::current() + fn runtime(&self) -> Option> { + None } } diff --git a/src/handler/mod.rs b/src/handler/mod.rs index 99de2cf..796deeb 100644 --- a/src/handler/mod.rs +++ b/src/handler/mod.rs @@ -213,10 +213,11 @@ pub trait HandlerFor: &self, handle_args: HandlerArgsFor, ) -> Result { - handle_args - .context - .runtime() - .block_on(self.handle_async(handle_args)) + if let Some(rt) = handle_args.context.runtime() { + rt.block_on(self.handle_async(handle_args)) + } else { + tokio::runtime::Handle::current().block_on(self.handle_async(handle_args)) + } } fn handle_async( &self, @@ -234,12 +235,14 @@ pub trait HandlerFor: ) -> impl Future> + Send + 'a { async move { let s = self.clone(); - handle_args - .context - .runtime() - .spawn_blocking(move || s.handle_sync(handle_args)) - .await - .unwrap() + if let Some(rt) = handle_args.context.runtime() { + rt.spawn_blocking(move || s.handle_sync(handle_args)).await + } else { + tokio::runtime::Handle::current() + .spawn_blocking(move || s.handle_sync(handle_args)) + .await + } + .unwrap() } } #[allow(unused_variables)] diff --git a/tests/handler.rs b/tests/handler.rs index 4e2ce12..9e89d0e 100644 --- a/tests/handler.rs +++ b/tests/handler.rs @@ -48,16 +48,19 @@ impl CliConfig { struct CliContextSeed { host: PathBuf, - rt: OnceCell, + rt: OnceCell>, } #[derive(Clone)] struct CliContext(Arc); impl Context for CliContext { - fn runtime(&self) -> Handle { + fn runtime(&self) -> Option> { if self.0.rt.get().is_none() { - self.0.rt.set(Runtime::new().unwrap()).unwrap(); + let rt = Arc::new(Runtime::new().unwrap()); + self.0.rt.set(rt.clone()).unwrap_or_default(); + Some(rt) + } else { + self.0.rt.get().cloned() } - self.0.rt.get().unwrap().handle().clone() } } From 60a974a29c5e6380f7bbfbc1b4716f6d2b20b189 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Thu, 15 Aug 2024 11:57:56 -0600 Subject: [PATCH 75/79] fix future busywait when job_source closed --- src/util.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/util.rs b/src/util.rs index c7b71b2..9217b32 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,4 +1,5 @@ use std::fmt::{Debug, Display}; +use std::task::Waker; use futures::future::{BoxFuture, FusedFuture}; use futures::stream::FusedStream; @@ -179,12 +180,14 @@ pub fn poll_select_all<'a, T>( } pub struct JobRunner<'a, T> { + wakers: Vec, closed: bool, running: Vec>, } impl<'a, T> JobRunner<'a, T> { pub fn new() -> Self { JobRunner { + wakers: Vec::new(), closed: false, running: Vec::new(), } @@ -196,12 +199,24 @@ impl<'a, T> JobRunner<'a, T> { &mut self, job_source: &mut Src, ) -> Option { + let mut job_source = Some(job_source); loop { + let next_job_fut = async { + if let Some(job_source) = &mut job_source { + job_source.next().await + } else { + futures::future::pending().await + } + }; tokio::select! { - job = job_source.next() => { + job = next_job_fut => { if let Some(job) = job { self.running.push(job.boxed()); + while let Some(waker) = self.wakers.pop() { + waker.wake(); + } } else { + job_source.take(); self.closed = true; if self.running.is_empty() { return None; @@ -221,6 +236,10 @@ impl<'a, T> Stream for JobRunner<'a, T> { mut self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { + if self.running.is_empty() { + self.wakers.push(cx.waker().clone()); + return std::task::Poll::Pending; + } match poll_select_all(&mut self.running, cx) { std::task::Poll::Pending if self.closed && self.running.is_empty() => { std::task::Poll::Ready(None) From 39a872a1294c7d864faca63f072092ce300ffbe5 Mon Sep 17 00:00:00 2001 From: Jade <2364004+Blu-J@users.noreply.github.com> Date: Fri, 20 Sep 2024 12:16:33 -0600 Subject: [PATCH 76/79] Feat: Add with about (#2) * FEat: Add with about * chore: Fix the tests --- src/handler/adapters.rs | 111 +++++++++++++++++++++++++++++++++++++++- tests/handler.rs | 19 +++---- tests/test.rs | 6 +-- 3 files changed, 119 insertions(+), 17 deletions(-) diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index 00a49ca..a54e7e9 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -2,7 +2,10 @@ use std::any::TypeId; use std::collections::VecDeque; use std::fmt::Debug; -use clap::{CommandFactory, FromArgMatches}; +use clap::{ + builder::{IntoResettable, StyledStr}, + CommandFactory, FromArgMatches, +}; use imbl_value::imbl::OrdMap; use imbl_value::Value; use serde::de::DeserializeOwned; @@ -40,6 +43,9 @@ pub trait HandlerExt: HandlerFor + Sized { where F: Fn(Params, InheritedParams) -> Self::InheritedParams; fn with_call_remote(self) -> RemoteCaller; + fn with_about(self, message: M) -> WithAbout + where + M: IntoResettable; } impl + Sized> HandlerExt for T { @@ -93,6 +99,16 @@ impl + Sized> HandlerExt(self, message: M) -> WithAbout + where + M: IntoResettable, + { + WithAbout { + handler: self, + message, + } + } } #[derive(Debug, Clone)] @@ -757,3 +773,96 @@ where ) } } + +#[derive(Debug, Clone)] +pub struct WithAbout { + handler: H, + message: M, +} +impl HandlerTypes for WithAbout +where + H: HandlerTypes, +{ + type Params = H::Params; + type InheritedParams = H::InheritedParams; + type Ok = H::Ok; + type Err = H::Err; +} +impl HandlerFor for WithAbout +where + Context: crate::Context, + H: HandlerFor, + M: Clone + Send + Sync + 'static, +{ + fn handle_sync( + &self, + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandlerArgsFor, + ) -> Result { + self.handler.handle_sync(HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + } + async fn handle_async( + &self, + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandlerArgsFor, + ) -> Result { + self.handler + .handle_async(HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }) + .await + } + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.handler.metadata(method) + } + fn method_from_dots(&self, method: &str) -> Option> { + self.handler.method_from_dots(method) + } +} +impl CliBindings for WithAbout +where + Context: crate::Context, + H: CliBindings, + M: IntoResettable + Clone, +{ + fn cli_command(&self) -> clap::Command { + self.handler.cli_command().about(self.message.clone()) + } + fn cli_parse( + &self, + arg_matches: &clap::ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + self.handler.cli_parse(arg_matches) + } + fn cli_display( + &self, + handler: HandlerArgsFor, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.handler.cli_display(handler, result) + } +} diff --git a/tests/handler.rs b/tests/handler.rs index 9e89d0e..dc41e8f 100644 --- a/tests/handler.rs +++ b/tests/handler.rs @@ -11,7 +11,7 @@ use rpc_toolkit::{ ParentHandler, Server, }; use serde::{Deserialize, Serialize}; -use tokio::runtime::{Handle, Runtime}; +use tokio::runtime::Runtime; use tokio::sync::{Mutex, OnceCell}; use yajrc::RpcError; @@ -133,6 +133,7 @@ fn make_api() -> ParentHandler { }, ) .with_custom_display_fn(|_, a| Ok(println!("{a}"))) + .with_about("Testing") .with_call_remote::(), ) .subcommand( @@ -200,18 +201,10 @@ pub fn internal_error(e: impl Display) -> RpcError { #[test] fn test_cli() { make_cli() - .run( - ["test-cli", "hello", "me"] - .iter() - .map(|s| OsString::from(s)), - ) + .run(["test-cli", "hello", "me"].iter().map(OsString::from)) .unwrap(); make_cli() - .run( - ["test-cli", "fizz", "buzz"] - .iter() - .map(|s| OsString::from(s)), - ) + .run(["test-cli", "fizz", "buzz"].iter().map(OsString::from)) .unwrap(); } @@ -234,7 +227,7 @@ async fn test_server() { "foo", ] .iter() - .map(|s| OsString::from(s)), + .map(OsString::from), ) .unwrap(); make_cli() @@ -246,7 +239,7 @@ async fn test_server() { "bar", ] .iter() - .map(|s| OsString::from(s)), + .map(OsString::from), ) .unwrap(); shutdown.shutdown() diff --git a/tests/test.rs b/tests/test.rs index 5ef6b81..dac9821 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -19,14 +19,14 @@ // impl Context for CliContext { // type Metadata = (); // } -// + // impl CliContextSocket for CliContext { // type Stream = UnixStream; // async fn connect(&self) -> std::io::Result { // UnixStream::connect(&self.0).await // } // } -// + // impl rpc_toolkit::CliContext for CliContext { // async fn call_remote( // &self, @@ -92,7 +92,7 @@ // println!("{}", res); // } // } -// + // impl AsyncCommand for Thing1 { // async fn implementation( // self, From 021379f21c4d11c5a62c07460f4531ce9b555155 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Fri, 1 Nov 2024 15:10:33 -0600 Subject: [PATCH 77/79] better root_handler handling --- Cargo.lock | 879 +++++++++++++++++++++++------------------- src/cli.rs | 4 +- src/handler/parent.rs | 269 ++++++++----- 3 files changed, 657 insertions(+), 495 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 18e6099..0efe443 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,27 +1,27 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "ahash" -version = "0.8.7" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", "getrandom", @@ -32,63 +32,64 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.11" +version = "0.6.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.2" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "anyhow" -version = "1.0.79" +version = "1.0.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca" +checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -97,9 +98,9 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", @@ -108,9 +109,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.77" +version = "0.1.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", @@ -118,16 +119,22 @@ dependencies = [ ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.7.4" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" dependencies = [ "async-trait", "axum-core", @@ -149,7 +156,7 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.1", "tokio", "tower", "tower-layer", @@ -159,9 +166,9 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.4.3" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" dependencies = [ "async-trait", "bytes", @@ -172,7 +179,7 @@ dependencies = [ "mime", "pin-project-lite", "rustversion", - "sync_wrapper", + "sync_wrapper 1.0.1", "tower-layer", "tower-service", "tracing", @@ -180,17 +187,17 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.69" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets", ] [[package]] @@ -201,41 +208,35 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "1.3.2" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] name = "bitmaps" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703642b98a00b3b90513279a8ede3fcfa479c126c5fb46e78f3051522f021403" +checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" [[package]] name = "bumpalo" -version = "3.14.0" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytes" -version = "1.5.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cc" -version = "1.0.83" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" dependencies = [ - "libc", + "shlex", ] [[package]] @@ -246,9 +247,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ "clap_builder", "clap_derive", @@ -256,9 +257,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.18" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" dependencies = [ "anstream", "anstyle", @@ -268,9 +269,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" dependencies = [ "heck", "proc-macro2", @@ -280,15 +281,15 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "core-foundation" @@ -302,21 +303,21 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "either" -version = "1.9.0" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -329,9 +330,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys 0.52.0", @@ -339,9 +340,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.1" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" [[package]] name = "fnv" @@ -375,9 +376,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -390,9 +391,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -400,15 +401,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -417,15 +418,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -434,21 +435,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -464,9 +465,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", @@ -475,21 +476,21 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "h2" -version = "0.4.2" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http", "indexmap", "slab", @@ -500,9 +501,9 @@ dependencies = [ [[package]] name = "half" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "hashbrown" @@ -515,27 +516,27 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.4" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "http" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", @@ -544,9 +545,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", @@ -554,9 +555,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", @@ -567,9 +568,9 @@ dependencies = [ [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[package]] name = "httpdate" @@ -579,9 +580,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "1.1.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", @@ -593,10 +594,28 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", + "smallvec", "tokio", "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + [[package]] name = "hyper-tls" version = "0.6.0" @@ -615,9 +634,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -628,7 +647,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -669,7 +687,7 @@ dependencies = [ [[package]] name = "imbl-value" version = "0.1.0" -source = "git+https://github.com/Start9Labs/imbl-value.git#48dc39a762a3b4f9300d3b9f850cbd394e777ae0" +source = "git+https://github.com/Start9Labs/imbl-value.git#3ce01b17ae5e756fc829ee5e3513a1b19b2a03fc" dependencies = [ "imbl", "serde", @@ -679,40 +697,46 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown 0.14.3", + "hashbrown 0.15.0", ] [[package]] name = "ipnet" -version = "2.9.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "ddc24109865250148c2e0f3d25d4f0f479571723792d3802153c60922a4fb708" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.10" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.67" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] @@ -725,27 +749,27 @@ checksum = "e479e99b287d578ed5f6cd4c92cdf48db219088adb9c5b14f7c155b71dfba792" [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.152" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -753,9 +777,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "matchit" @@ -765,9 +789,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.7.1" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "mime" @@ -777,31 +801,31 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" dependencies = [ - "adler", + "adler2", ] [[package]] name = "mio" -version = "0.8.10" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -813,38 +837,28 @@ dependencies = [ "tempfile", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - [[package]] name = "object" -version = "0.32.2" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" -version = "0.10.63" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ - "bitflags 2.4.2", + "bitflags", "cfg-if", "foreign-types", "libc", @@ -872,18 +886,18 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.2.1+3.2.0" +version = "300.4.0+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fe476c29791a5ca0d1273c697e96085bbabbbea2ef7afd5617e78a4b40332d3" +checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.99" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ "cc", "libc", @@ -894,9 +908,9 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -904,15 +918,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.48.5", + "windows-targets", ] [[package]] @@ -923,18 +937,18 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.3" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.3" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" dependencies = [ "proc-macro2", "quote", @@ -943,9 +957,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.13" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -955,24 +969,24 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.29" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "proc-macro2" -version = "1.0.78" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.35" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" dependencies = [ "proc-macro2", ] @@ -994,18 +1008,18 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 1.3.2", + "bitflags", ] [[package]] name = "reqwest" -version = "0.12.4" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64", "bytes", @@ -1017,6 +1031,7 @@ dependencies = [ "http-body", "http-body-util", "hyper", + "hyper-rustls", "hyper-tls", "hyper-util", "ipnet", @@ -1031,7 +1046,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 1.0.1", "system-configuration", "tokio", "tokio-native-tls", @@ -1040,7 +1055,22 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "windows-registry", +] + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", ] [[package]] @@ -1073,17 +1103,17 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.30" +version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ - "bitflags 2.4.2", + "bitflags", "errno", "libc", "linux-raw-sys", @@ -1091,40 +1121,63 @@ dependencies = [ ] [[package]] -name = "rustls-pemfile" -version = "2.1.2" +name = "rustls" +version = "0.23.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64", "rustls-pki-types", ] [[package]] name = "rustls-pki-types" -version = "1.5.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beb461507cee2c2ff151784c52762cf4d9ff6a61f3e80968600ed24fa837fa54" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" [[package]] name = "ryu" -version = "1.0.16" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1135,11 +1188,11 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "security-framework" -version = "2.9.2" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags", "core-foundation", "core-foundation-sys", "libc", @@ -1148,9 +1201,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.1" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -1158,9 +1211,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.195" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] @@ -1177,9 +1230,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -1188,20 +1241,21 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "serde_path_to_error" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd154a240de39fdebcf5775d2675c204d7c13cf39a4c697be6493c8e734337c" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" dependencies = [ "itoa", "serde", @@ -1220,10 +1274,16 @@ dependencies = [ ] [[package]] -name = "signal-hook-registry" -version = "1.4.1" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" dependencies = [ "libc", ] @@ -1239,31 +1299,43 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "socket2" -version = "0.5.5" +version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] -name = "strsim" -version = "0.10.0" +name = "spin" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.48" +version = "2.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" dependencies = [ "proc-macro2", "quote", @@ -1277,21 +1349,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] -name = "system-configuration" -version = "0.5.1" +name = "sync_wrapper" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" dependencies = [ - "bitflags 1.3.2", + "futures-core", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -1299,31 +1380,31 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" dependencies = [ "proc-macro2", "quote", @@ -1332,9 +1413,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -1347,28 +1428,27 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -1386,10 +1466,21 @@ dependencies = [ ] [[package]] -name = "tokio-stream" -version = "0.1.14" +name = "tokio-rustls" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "397c988d37662c7dda6d2208364a706264bf3d6138b11d436cbac0ad38832842" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +dependencies = [ + "rustls", + "rustls-pki-types", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" dependencies = [ "futures-core", "pin-project-lite", @@ -1398,28 +1489,27 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "tower" -version = "0.4.13" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper 0.1.2", "tokio", "tower-layer", "tower-service", @@ -1428,15 +1518,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" @@ -1466,30 +1556,36 @@ checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] name = "unicode-bidi" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.22" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] -name = "url" -version = "2.5.0" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -1498,9 +1594,9 @@ dependencies = [ [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "vcpkg" @@ -1510,9 +1606,9 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "want" @@ -1531,19 +1627,20 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.90" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.90" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", "log", @@ -1556,9 +1653,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.40" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde2032aeb86bdfaecc8b261eef3cba735cc426c1f3a3416d1e0791be95fc461" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -1568,9 +1665,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.90" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1578,9 +1675,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.90" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -1591,27 +1688,48 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.90" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" -version = "0.3.67" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-registry" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "windows-targets 0.48.5", + "windows-result", + "windows-strings", + "windows-targets", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", ] [[package]] @@ -1620,132 +1738,81 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", ] [[package]] name = "windows-targets" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - -[[package]] -name = "windows-targets" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" -dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.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]] name = "windows_aarch64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_i686_gnu" -version = "0.52.0" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_i686_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.48.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" - -[[package]] -name = "winreg" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a277a57398d4bfa075df44f501a17cfdf8542d224f0d36095a2adc7aee4ef0a5" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "yajrc" @@ -1773,20 +1840,26 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", "syn", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/src/cli.rs b/src/cli.rs index 75a1592..ef9f268 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -43,8 +43,8 @@ impl } pub fn run(self, args: impl IntoIterator) -> Result<(), RpcError> { let mut cmd = Config::command(); - for (name, handler) in &self.root_handler.subcommands.0 { - if let (Name(Some(name)), Some(cli)) = (name, handler.cli()) { + for (name, handler) in &self.root_handler.subcommands.1 { + if let (Name(name), Some(cli)) = (name, handler.cli()) { cmd = cmd.subcommand(cli.cli_command().name(name)); } } diff --git a/src/handler/parent.rs b/src/handler/parent.rs index 9356b27..6238d85 100644 --- a/src/handler/parent.rs +++ b/src/handler/parent.rs @@ -14,32 +14,52 @@ use crate::{ }; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub(crate) struct Name(pub(crate) Option<&'static str>); -impl<'a> std::borrow::Borrow> for Name { - fn borrow(&self) -> &Option<&'a str> { +pub(crate) struct Name(pub(crate) &'static str); +impl<'a> std::borrow::Borrow<&'a str> for Name { + fn borrow(&self) -> &&'a str { &self.0 } } -pub(crate) struct SubcommandMap( - pub(crate) OrdMap>, +pub(crate) struct SubcommandMap( + pub(crate) Option>, + pub(crate) OrdMap>>, ); -impl Clone for SubcommandMap { +impl Clone for SubcommandMap { fn clone(&self) -> Self { - Self(self.0.clone()) + Self(self.0.clone(), self.1.clone()) } } -impl Debug for SubcommandMap { +impl Debug for SubcommandMap { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_map().entries(self.0.iter()).finish() + let mut map = f.debug_map(); + if let Some(root) = &self.0 { + #[derive(Debug)] + struct Root; + map.entry(&Root, root); + } + map.entries(self.1.iter()).finish() } } -impl SubcommandMap { - fn insert(&mut self, name: Option<&'static str>, handler: DynHandler) { - self.0.insert(Name(name), handler); +impl SubcommandMap { + fn set_root(&mut self, handler: DynHandler) { + self.0 = Some(handler); } - fn get<'a>(&'a self, name: Option<&str>) -> Option<(Name, &'a DynHandler)> { - if let Some((name, handler)) = self.0.get_key_value(&name) { + fn get_root<'a>(&'a self) -> Option<&'a DynHandler> { + self.0.as_ref() + } + fn insert( + &mut self, + name: &'static str, + handler: DynHandler>, + ) { + self.1.insert(Name(name), handler); + } + fn get<'a>( + &'a self, + name: &str, + ) -> Option<(Name, &'a DynHandler>)> { + if let Some((name, handler)) = self.1.get_key_value(&name) { Some((*name, handler)) } else { None @@ -49,14 +69,14 @@ impl SubcommandMap { pub struct ParentHandler { _phantom: PhantomData, - pub(crate) subcommands: SubcommandMap>, + pub(crate) subcommands: SubcommandMap, metadata: OrdMap<&'static str, Value>, } impl ParentHandler { pub fn new() -> Self { Self { _phantom: PhantomData::new(), - subcommands: SubcommandMap(OrdMap::new()), + subcommands: SubcommandMap(None, OrdMap::new()), metadata: OrdMap::new(), } } @@ -98,12 +118,11 @@ impl } pub fn root_handler(mut self, handler: H) -> Self where - WithContext: Handler>, - as Handler>>::H: - HandlerTypes, + WithContext: Handler, + as Handler>::H: HandlerTypes, { if let Some(h) = DynHandler::new(handler) { - self.subcommands.insert(None, h); + self.subcommands.set_root(h); } self } @@ -142,17 +161,29 @@ where let cmd = method.pop_front(); if let Some(cmd) = cmd { parent_method.push_back(cmd); - } - if let Some((_, sub_handler)) = &self.subcommands.get(cmd) { - sub_handler.handle_sync(HandleAnyArgs { - context, - parent_method, - method, - params: raw_params, - inherited: Flat(params, inherited_params), - }) + if let Some((_, sub_handler)) = &self.subcommands.get(cmd) { + sub_handler.handle_sync(HandleAnyArgs { + context, + parent_method, + method, + params: raw_params, + inherited: Flat(params, inherited_params), + }) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } } else { - Err(yajrc::METHOD_NOT_FOUND_ERROR) + if let Some(sub_handler) = &self.subcommands.get_root() { + sub_handler.handle_sync(HandleAnyArgs { + context, + parent_method, + method, + params: raw_params, + inherited: inherited_params, + }) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } } } async fn handle_async( @@ -169,27 +200,49 @@ where let cmd = method.pop_front(); if let Some(cmd) = cmd { parent_method.push_back(cmd); - } - if let Some((_, sub_handler)) = self.subcommands.get(cmd) { - sub_handler - .handle_async(HandleAnyArgs { - context, - parent_method, - method, - params: raw_params, - inherited: Flat(params, inherited_params), - }) - .await + if let Some((_, sub_handler)) = &self.subcommands.get(cmd) { + sub_handler + .handle_async(HandleAnyArgs { + context, + parent_method, + method, + params: raw_params, + inherited: Flat(params, inherited_params), + }) + .await + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } } else { - Err(yajrc::METHOD_NOT_FOUND_ERROR) + if let Some(sub_handler) = &self.subcommands.get_root() { + sub_handler + .handle_async(HandleAnyArgs { + context, + parent_method, + method, + params: raw_params, + inherited: inherited_params, + }) + .await + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } } } fn metadata(&self, mut method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { let metadata = self.metadata.clone(); - if let Some((_, handler)) = self.subcommands.get(method.pop_front()) { - handler.metadata(method).union(metadata) + if let Some(cmd) = method.pop_front() { + if let Some((_, handler)) = self.subcommands.get(cmd) { + handler.metadata(method).union(metadata) + } else { + metadata + } } else { - metadata + if let Some(handler) = self.subcommands.get_root() { + handler.metadata(method).union(metadata) + } else { + metadata + } } } fn method_from_dots(&self, method: &str) -> Option> { @@ -201,15 +254,22 @@ where .map(|(head, tail)| (Some(head), Some(tail))) .unwrap_or((Some(method), None)) }; - let (Name(name), h) = self.subcommands.get(head)?; - let mut res = VecDeque::new(); - if let Some(name) = name { + if let Some(head) = head { + let (Name(name), h) = self.subcommands.get(head)?; + let mut res = VecDeque::new(); res.push_back(name); + if let Some(tail) = tail { + res.append(&mut h.method_from_dots(tail)?); + } + Some(res) + } else { + let h = self.subcommands.get_root()?; + let mut res = VecDeque::new(); + if let Some(tail) = tail { + res.append(&mut h.method_from_dots(tail)?); + } + Some(res) } - if let Some(tail) = tail { - res.append(&mut h.method_from_dots(tail)?); - } - Some(res) } } @@ -221,15 +281,16 @@ where InheritedParams: Serialize + Send + Sync + 'static, { fn cli_command(&self) -> Command { - let mut base = Params::command().subcommand_required(true); - for (name, handler) in &self.subcommands.0 { + let mut base = if let Some(cli) = &self.subcommands.0.as_ref().and_then(|h| h.cli()) { + cli.cli_command().subcommand_required(false) + } else { + Params::command().subcommand_required(true) + }; + for (name, handler) in &self.subcommands.1 { match (name, handler.cli()) { - (Name(Some(name)), Some(cli)) => { + (Name(name), Some(cli)) => { base = base.subcommand(cli.cli_command().name(name)); } - (Name(None), Some(_)) => { - base = base.subcommand_required(false); - } _ => (), } } @@ -239,27 +300,40 @@ where &self, matches: &ArgMatches, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?; let (name, matches) = match matches.subcommand() { Some((name, matches)) => (Some(name), matches), None => (None, matches), }; - if let Some((Name(Some(name)), cli)) = self - .subcommands - .get(name) - .and_then(|(n, h)| h.cli().map(|c| (n, c))) - { - let (mut method, params) = cli.cli_parse(matches)?; - method.push_front(name); + if let Some(name) = name { + let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?; + if let Some((Name(name), cli)) = self + .subcommands + .get(name) + .and_then(|(n, h)| h.cli().map(|c| (n, c))) + { + let (mut method, params) = cli.cli_parse(matches)?; + method.push_front(name); - Ok(( - method, - combine(root_params, params) - .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e))?, - )) + Ok(( + method, + combine(root_params, params).map_err(|e| { + clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e) + })?, + )) + } else { + Ok((VecDeque::new(), root_params)) + } } else { - Ok((VecDeque::new(), root_params)) + if let Some(cli) = self.subcommands.get_root().and_then(|h| h.cli()) { + let (method, params) = cli.cli_parse(matches)?; + + Ok((method, params)) + } else { + let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?) + .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?; + Ok((VecDeque::new(), root_params)) + } } } fn cli_display( @@ -277,24 +351,39 @@ where let cmd = method.pop_front(); if let Some(cmd) = cmd { parent_method.push_back(cmd); - } - if let Some((_, cli)) = self - .subcommands - .get(cmd) - .and_then(|(n, h)| h.cli().map(|c| (n, c))) - { - cli.cli_display( - HandleAnyArgs { - context, - parent_method, - method, - params: raw_params, - inherited: Flat(params, inherited_params), - }, - result, - ) + if let Some((_, cli)) = self + .subcommands + .get(cmd) + .and_then(|(n, h)| h.cli().map(|c| (n, c))) + { + cli.cli_display( + HandleAnyArgs { + context, + parent_method, + method, + params: raw_params, + inherited: Flat(params, inherited_params), + }, + result, + ) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } } else { - Err(yajrc::METHOD_NOT_FOUND_ERROR) + if let Some(cli) = self.subcommands.get_root().and_then(|h| h.cli()) { + cli.cli_display( + HandleAnyArgs { + context, + parent_method, + method, + params: raw_params, + inherited: inherited_params, + }, + result, + ) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } } } } From df388073a6bc6984e5a1236f1b20f0c057230023 Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 19 Nov 2024 13:09:01 -0700 Subject: [PATCH 78/79] clean up traits --- src/cli.rs | 4 --- src/handler/adapters.rs | 8 ++--- src/handler/parent.rs | 4 +-- src/metadata.rs | 68 ----------------------------------------- 4 files changed, 5 insertions(+), 79 deletions(-) delete mode 100644 src/metadata.rs diff --git a/src/cli.rs b/src/cli.rs index ef9f268..5890bff 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -199,7 +199,6 @@ impl HandlerTypes where RemoteHandler: HandlerTypes, RemoteHandler::Params: Serialize, - RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, Extra: Send + Sync + 'static, @@ -217,7 +216,6 @@ where RemoteContext: crate::Context, RemoteHandler: HandlerFor, RemoteHandler::Params: Serialize, - RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, Extra: Serialize + Send + Sync + 'static, @@ -260,7 +258,6 @@ where Context: CallRemote, RemoteHandler: PrintCliResult, RemoteHandler::Params: Serialize, - RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, Extra: Send + Sync + 'static, @@ -296,7 +293,6 @@ where Context: crate::Context, RemoteHandler: CliBindings, RemoteHandler::Params: Serialize, - RemoteHandler::InheritedParams: Serialize, RemoteHandler::Ok: DeserializeOwned, RemoteHandler::Err: From, Extra: Send + Sync + 'static, diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs index a54e7e9..007a8fe 100644 --- a/src/handler/adapters.rs +++ b/src/handler/adapters.rs @@ -2,10 +2,8 @@ use std::any::TypeId; use std::collections::VecDeque; use std::fmt::Debug; -use clap::{ - builder::{IntoResettable, StyledStr}, - CommandFactory, FromArgMatches, -}; +use clap::builder::{IntoResettable, StyledStr}; +use clap::{CommandFactory, FromArgMatches}; use imbl_value::imbl::OrdMap; use imbl_value::Value; use serde::de::DeserializeOwned; @@ -615,7 +613,7 @@ where H::Ok: Serialize + DeserializeOwned, H::Err: From, H::Params: Serialize + DeserializeOwned, - H::InheritedParams: Serialize + OrEmpty, + H::InheritedParams: OrEmpty, RpcError: From, Inherited: Send + Sync + 'static, { diff --git a/src/handler/parent.rs b/src/handler/parent.rs index 6238d85..9dc7e12 100644 --- a/src/handler/parent.rs +++ b/src/handler/parent.rs @@ -145,7 +145,7 @@ impl HandlerFor where Context: crate::Context, Params: Send + Sync + 'static, - InheritedParams: Serialize + Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, { fn handle_sync( &self, @@ -278,7 +278,7 @@ impl CliBindings where Context: crate::Context, Params: FromArgMatches + CommandFactory + Serialize + Send + Sync + 'static, - InheritedParams: Serialize + Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, { fn cli_command(&self) -> Command { let mut base = if let Some(cli) = &self.subcommands.0.as_ref().and_then(|h| h.cli()) { diff --git a/src/metadata.rs b/src/metadata.rs deleted file mode 100644 index c300f76..0000000 --- a/src/metadata.rs +++ /dev/null @@ -1,68 +0,0 @@ -macro_rules! getter_for { - ($($name:ident => $t:ty,)*) => { - $( - #[allow(unused_variables)] - fn $name(&self, command: &str, key: &str) -> Option<$t> { - None - } - )* - }; -} - -pub trait Metadata: Copy + Default + Send + Sync + 'static { - fn get(&self, command: &str, key: &str) -> Option { - Ty::from_metadata(self, command, key) - } - getter_for!( - get_bool => bool, - get_u8 => u8, - get_u16 => u16, - get_u32 => u32, - get_u64 => u64, - get_usize => usize, - get_i8 => i8, - get_i16 => i16, - get_i32 => i32, - get_i64 => i64, - get_isize => isize, - get_f32 => f32, - get_f64 => f64, - get_char => char, - get_str => &'static str, - get_bstr => &'static [u8], - ); -} - -macro_rules! impl_primitive_for { - ($($name:ident => $t:ty,)*) => { - $( - impl Primitive for $t { - fn from_metadata(m: &M, command: &str, key: &str) -> Option { - m.$name(command, key) - } - } - )* - }; -} - -pub trait Primitive: Copy { - fn from_metadata(m: &M, command: &str, key: &str) -> Option; -} -impl_primitive_for!( - get_bool => bool, - get_u8 => u8, - get_u16 => u16, - get_u32 => u32, - get_u64 => u64, - get_usize => usize, - get_i8 => i8, - get_i16 => i16, - get_i32 => i32, - get_i64 => i64, - get_isize => isize, - get_f32 => f32, - get_f64 => f64, - get_char => char, - get_str => &'static str, - get_bstr => &'static [u8], -); From 21e35d85fb8f5de0e046c7ab5266236c2b639a4b Mon Sep 17 00:00:00 2001 From: Aiden McClelland Date: Tue, 19 Nov 2024 13:22:00 -0700 Subject: [PATCH 79/79] fix cli parsing for parent commands --- src/handler/parent.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/handler/parent.rs b/src/handler/parent.rs index 9dc7e12..5668780 100644 --- a/src/handler/parent.rs +++ b/src/handler/parent.rs @@ -298,14 +298,14 @@ where } fn cli_parse( &self, - matches: &ArgMatches, + root_matches: &ArgMatches, ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { - let (name, matches) = match matches.subcommand() { + let (name, matches) = match root_matches.subcommand() { Some((name, matches)) => (Some(name), matches), - None => (None, matches), + None => (None, root_matches), }; if let Some(name) = name { - let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?) + let root_params = imbl_value::to_value(&Params::from_arg_matches(root_matches)?) .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?; if let Some((Name(name), cli)) = self .subcommands