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.lock b/Cargo.lock index 0aba0e4..0efe443 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,59 +1,243 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] -name = "anyhow" -version = "1.0.40" +name = "addr2line" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b2cd92db5cbd74e8e5028f7e27dd7aa3090e89e4f2a197cc7c8dfb69c7063b" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ - "hermit-abi", - "libc", - "winapi", + "gimli", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "anstream" +version = "0.6.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125" +dependencies = [ + "anstyle", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13" + +[[package]] +name = "async-stream" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-trait" +version = "0.1.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" +dependencies = [ + "async-trait", + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper 1.0.1", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper 1.0.1", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] [[package]] name = "base64" -version = "0.13.0" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "1.2.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" + +[[package]] +name = "bitmaps" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" [[package]] name = "bumpalo" -version = "3.6.1" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63396b8a4b9de3f4fdfb320ab6080762242f66a8ef174c49d8e19b674db4cdbe" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytes" -version = "1.0.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b700ce4376041dcd0a327fd0097c41095743c4c8af8887265942faf1100bd040" +checksum = "9ac0150caa2ae65ca5bd83f25c7de183dea78d4d366469f148435e2acfbad0da" [[package]] name = "cc" -version = "1.0.67" +version = "1.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +dependencies = [ + "shlex", +] [[package]] name = "cfg-if" @@ -63,33 +247,55 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "3.2.8" +version = "4.5.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190814073e85d238f31ff738fcb0bf6910cedeb73376c87cd69291028966fd83" +checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8" dependencies = [ - "atty", - "bitflags", + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54" +dependencies = [ + "anstream", + "anstyle", "clap_lex", - "indexmap", "strsim", - "termcolor", - "textwrap", +] + +[[package]] +name = "clap_derive" +version = "4.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] name = "clap_lex" -version = "0.2.4" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" -dependencies = [ - "os_str_bytes", -] +checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[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 +303,47 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.2" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "encoding_rs" -version = "0.8.28" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" 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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "fastrand" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" + [[package]] name = "fnv" version = "1.0.7" @@ -133,19 +367,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.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f73fe65f54d1e12b726f517d3e2135ca3125a437b6d998caf1962961f7172d9e" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -158,9 +391,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.21" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3083ce4b914124575708913bca19bfe887522d6e2e6d0952943f5eac4a74010" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -168,15 +401,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.21" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.21" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9420b90cfa29e327d0429f19be13e7ddb68fa1cccb09d65e5706b8c7a749b8a6" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -185,15 +418,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.21" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4045962a5a5e935ee2fdedaa4e08284547402885ab326734432bed5d12966b" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" -version = "0.3.21" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33c1e13800337f4d4d7a316bf45a567dbcb6ffe087f16424852d97e97a91f512" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -202,21 +435,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.21" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.21" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c66a976bf5909d801bbef33416c41372779507e7a6b3a5e25e4749c58f776a" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.21" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b7abd5d659d9b90c8cba917f6ec750a74e2dc23902ef9cd4cc8c8b22e6036a" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -232,26 +465,32 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.2" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi 0.10.2+wasi-snapshot-preview1", + "wasi", ] [[package]] -name = "h2" -version = "0.3.13" +name = "gimli" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37a82c6d637fc9515a4694bbf1cb2457b79d81ce52b3108bdeea58b07dd34a57" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e8ac6999421f49a846c2d4411f337e53497d8ec55d67753beffa43c5d9205" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http", "indexmap", "slab", @@ -262,161 +501,275 @@ dependencies = [ [[package]] name = "half" -version = "1.7.1" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" +checksum = "1b43ede17f21864e81be2fa654110bf1e793774238d86ef8555c37e6519c0403" [[package]] name = "hashbrown" -version = "0.9.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" - -[[package]] -name = "hermit-abi" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" dependencies = [ - "libc", + "ahash", ] [[package]] -name = "http" -version = "0.2.3" +name = "hashbrown" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "http" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" dependencies = [ "bytes", "fnv", - "itoa 0.4.7", + "itoa", ] [[package]] name = "http-body" -version = "0.4.1" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfb77c123b4e2f72a2069aeae0b4b4949cc7e966df277813fc16347e7549737" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.7.1" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "496ce29bb5a52785b44e0f7ca2847ae0bb839c9bd28f69acac9b99d461c0c04c" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" [[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 = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02c929dc5c39e335a03c405292728118860721b10190d98c2a0f0efd5baafbac" +checksum = "bbbff0a806a4728c99295b254c8838933b5b082d75e3cb70c8dab21fdfbcfa9a" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", "http-body", "httparse", "httpdate", - "itoa 1.0.2", + "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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", "tower-service", "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", ] [[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "144006fb58ed787dcae3f54575ff4349755b00ccc99f4b4873860b654be1ed63" +dependencies = [ + "bitmaps", +] + +[[package]] +name = "imbl-value" +version = "0.1.0" +source = "git+https://github.com/Start9Labs/imbl-value.git#3ce01b17ae5e756fc829ee5e3513a1b19b2a03fc" +dependencies = [ + "imbl", + "serde", + "serde_json", + "yasi", +] + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown 0.15.0", ] [[package]] name = "ipnet" -version = "2.3.0" +version = "2.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47be2f14c678be2fdcab04ab1171db51b2762ce6f0a8ee87c8dd4a04ed216135" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] [[package]] name = "itoa" -version = "0.4.7" +version = "1.0.11" 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 = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" -version = "0.3.50" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d99f9e3e84b8f67f846ef5b4cbbc3b1c29f6c759fcbce6f01aa0e73d932a24c" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" dependencies = [ "wasm-bindgen", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "lazy_format" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "e479e99b287d578ed5f6cd4c92cdf48db219088adb9c5b14f7c155b71dfba792" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.126" +version = "0.2.161" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" -version = "0.4.7" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" dependencies = [ "autocfg", "scopeguard", @@ -424,50 +777,55 @@ dependencies = [ [[package]] name = "log" -version = "0.4.14" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" -dependencies = [ - "cfg-if", -] +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] -name = "matches" -version = "0.1.8" +name = "matchit" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.3.4" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[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.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +dependencies = [ + "adler2", +] [[package]] name = "mio" -version = "0.8.4" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "wasi", + "windows-sys 0.52.0", ] [[package]] name = "native-tls" -version = "0.2.10" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd7e2f3618557f980e0b17e8856252eee3c97fa12c54dff0ca290fb6266ca4a9" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -480,57 +838,67 @@ dependencies = [ ] [[package]] -name = "num_cpus" -version = "1.13.0" +name = "object" +version = "0.36.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" dependencies = [ - "hermit-abi", - "libc", + "memchr", ] [[package]] name = "once_cell" -version = "1.7.2" +version = "1.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" [[package]] name = "openssl" -version = "0.10.33" +version = "0.10.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a61075b62a23fef5a29815de7536d940aa35ce96d18ce0cc5076272db678a577" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" dependencies = [ "bitflags", "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", +] + +[[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.4.0+3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f31f0d509d1c1ae9cada2f9539ff8f37933831fd5098879e482aa687d659853" +checksum = "a709e02f2b4aca747929cca5ed248880847c650233cf8b8cdc48f40aaf4898a6" dependencies = [ "cc", ] [[package]] name = "openssl-sys" -version = "0.9.61" +version = "0.9.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "313752393519e876837e09e1fa183ddef0be7735868dced3196f4472d536277f" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" dependencies = [ - "autocfg", "cc", "libc", "openssl-src", @@ -538,17 +906,11 @@ 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" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -556,28 +918,48 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-sys", + "windows-targets", ] [[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" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be57f64e946e500c8ee36ef6331845d40a93055567ec57e8fae13efd33759b95" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c0f5fad0874fc7abcd4d750e76917eaebbecaa2c20bde22e1dbeeba8beb758c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "pin-project-lite" -version = "0.2.6" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0e1f259c92177c30a4c9d177246edd0a3568b25756a977d0632cf8fa37e905" +checksum = "915a1e146535de9163f3987b8944ed8cf49a18bb0056bcebcdcece385cece4ff" [[package]] name = "pin-utils" @@ -587,97 +969,57 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.19" +version = "0.3.31" 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 = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 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.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 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", -] - [[package]] name = "reqwest" -version = "0.11.11" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75aa69a3f06bbcc66ede33af2af253c6f7a86b1ca0033f60c580a27074fbf92" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64", "bytes", @@ -687,19 +1029,25 @@ dependencies = [ "h2", "http", "http-body", + "http-body-util", "hyper", + "hyper-rustls", "hyper-tls", + "hyper-util", "ipnet", "js-sys", - "lazy_static", "log", "mime", "native-tls", + "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile", "serde", "serde_json", "serde_urlencoded", + "sync_wrapper 1.0.1", + "system-configuration", "tokio", "tokio-native-tls", "tower-service", @@ -707,74 +1055,142 @@ 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]] name = "rpc-toolkit" -version = "0.2.2" +version = "0.2.3" dependencies = [ + "async-stream", + "async-trait", + "axum", "clap", "futures", - "hyper", + "http", + "http-body-util", + "imbl-value", + "itertools", + "lazy_format", "lazy_static", "openssl", + "pin-project", "reqwest", - "rpc-toolkit-macro", "serde", "serde_cbor", "serde_json", "thiserror", "tokio", + "tokio-stream", "url", "yajrc", ] [[package]] -name = "rpc-toolkit-macro" -version = "0.2.2" +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "0.38.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ - "proc-macro2", - "rpc-toolkit-macro-internals", - "syn", + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", ] [[package]] -name = "rpc-toolkit-macro-internals" -version = "0.2.2" +name = "rustls" +version = "0.23.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee87ff5d9b36712a58574e12e9f0ea80f915a5b0ac518d322b24a465617925e" dependencies = [ - "proc-macro2", - "quote", - "syn", + "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 = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "schannel" -version = "0.1.19" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" dependencies = [ - "lazy_static", - "winapi", + "windows-sys 0.59.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.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3670b1d2fdf6084d192bc71ead7aabe6c06aa2ea3fbd9cc3ac111fa5c2b1bd84" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ "bitflags", "core-foundation", @@ -785,9 +1201,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.2.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3676258fd3cfe2c9a0ec99ce3038798d847ce3e4bb17746373eb9f0f1ac16339" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" dependencies = [ "core-foundation-sys", "libc", @@ -795,9 +1211,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.139" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0171ebb889e45aa68b44aee0859b3eede84c6f5f5c228e6f140c0b2a0a46cad6" +checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5" dependencies = [ "serde_derive", ] @@ -814,9 +1230,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.139" +version = "1.0.214" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1d3230c1de7932af58ad8ffbe1d784bd55efd5a9d84ac24f69c72d83543dfb" +checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766" dependencies = [ "proc-macro2", "quote", @@ -825,15 +1241,26 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.82" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82c2c1fdcd807d1098552c5b9a36e425e42e9fbd7c6a37a8425f390f781f7fa7" +checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03" dependencies = [ - "itoa 1.0.2", + "itoa", + "memchr", "ryu", "serde", ] +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -841,53 +1268,74 @@ 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" +name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6" +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", ] [[package]] name = "slab" -version = "0.4.2" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" - -[[package]] -name = "smallvec" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" - -[[package]] -name = "socket2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ - "libc", - "winapi", + "autocfg", ] [[package]] -name = "strsim" -version = "0.10.0" +name = "smallvec" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "socket2" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "1.0.98" +version = "2.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" dependencies = [ "proc-macro2", "quote", @@ -895,48 +1343,68 @@ dependencies = [ ] [[package]] -name = "tempfile" -version = "3.2.0" +name = "sync_wrapper" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" + +[[package]] +name = "sync_wrapper" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" +dependencies = [ + "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.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" dependencies = [ "cfg-if", - "libc", - "rand", - "redox_syscall", - "remove_dir_all", - "winapi", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.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.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.31" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" +checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5" dependencies = [ "proc-macro2", "quote", @@ -945,44 +1413,42 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.1.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317cca572a0e89c3ce0ca1f1bdc9369547fe318a683418e42ac8f59d14701023" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 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.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51a52ed6686dd62c320f9b89299e9dfb46f730c7a48e635c19f21d116cb1439" +checksum = "145f3413504347a2be84393cc8a7d2fb4d863b375909ea59f2158261aa258bbb" dependencies = [ + "backtrace", "bytes", "libc", - "memchr", "mio", - "num_cpus", - "once_cell", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "1.8.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -991,130 +1457,168 @@ dependencies = [ [[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", ] [[package]] -name = "tokio-util" -version = "0.7.2" +name = "tokio-rustls" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f988a1a1adc2fb21f9c12aa96441da33a1728193ae0b95d2be22dbd17fcb4e5c" +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", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", +] + +[[package]] +name = "tower" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper 0.1.2", + "tokio", + "tower-layer", + "tower-service", "tracing", ] [[package]] -name = "tower-service" -version = "0.3.1" +name = "tower-layer" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[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", + "log", "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.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -dependencies = [ - "matches", -] +checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-normalization" -version = "0.1.17" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07fbfce1c8a97d547e8b5334978438d9d6ec8c20e38f56d4a4374d181493eaef" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] -name = "url" -version = "2.2.2" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +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", - "matches", "percent-encoding", ] [[package]] -name = "vcpkg" -version = "0.2.11" +name = "utf8parse" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00bca6106a5e23f3eee943593759b7fcddb00554332e856d990c893966879fb" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[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.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[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,23 +1627,24 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasm-bindgen" -version = "0.2.73" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83240549659d187488f91f33c0f8547cbfef0b2088bc470c116d1d260ef623d9" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ "cfg-if", + "once_cell", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.73" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae70622411ca953215ca6d06d3ebeb1e915f0f6613e3b495122878d7ebec7dae" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" dependencies = [ "bumpalo", - "lazy_static", "log", + "once_cell", "proc-macro2", "quote", "syn", @@ -1148,9 +1653,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.23" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81b8b767af23de6ac18bf2168b690bed2902743ddf0fb39252e36f9e2bfc63ea" +checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" dependencies = [ "cfg-if", "js-sys", @@ -1160,9 +1665,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.73" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e734d91443f177bfdb41969de821e15c516931c3c3db3d318fa1b68975d0f6f" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1170,9 +1675,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.73" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53739ff08c8a68b0fdbcd54c372b8ab800b1449ab3c9d706503bc7dd1621b2c" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", @@ -1183,109 +1688,178 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.73" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a543ae66aa233d14bb765ed9af4a33e81b8b58d1584cf1b47ff8cd0b9e4489" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" [[package]] name = "web-sys" -version = "0.3.50" +version = "0.3.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a905d57e488fec8861446d3393670fb50d27a262344013181c2cdf9fff5481be" +checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] -name = "winapi" -version = "0.3.9" +name = "windows-registry" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "windows-result", + "windows-strings", + "windows-targets", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "windows-result" +version = "0.2.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" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" dependencies = [ - "winapi", + "windows-targets", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "windows-strings" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets", +] [[package]] name = "windows-sys" -version = "0.36.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ + "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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", + "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", + "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" +name = "windows_aarch64_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.36.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.36.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" -version = "0.36.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" -version = "0.36.1" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "yajrc" -version = "0.1.0" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce7af47ad983c2f8357333ef87d859e66deb7eef4bf6f9e1ae7b5e99044a48bf" 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.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +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/Cargo.toml b/Cargo.toml index 333618a..19f3831 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,41 @@ -[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" +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.12" } +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 87552e5..0000000 --- a/rpc-toolkit-macro-internals/Cargo.toml +++ /dev/null @@ -1,13 +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"] } 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 2692af0..0000000 --- a/rpc-toolkit-macro-internals/src/command/build.rs +++ /dev/null @@ -1,1287 +0,0 @@ -use std::collections::HashSet; - -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.last().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.is_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 - } - } - } - } - } -} - -pub fn build(args: AttributeArgs, mut item: ItemFn) -> TokenStream { - let mut 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 - .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); - - 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 - } - }; - if opt.common().macro_debug { - panic!("EXPANDED MACRO:\n{}", res); - } - 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 3876d78..0000000 --- a/rpc-toolkit-macro-internals/src/command/mod.rs +++ /dev/null @@ -1,105 +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: bool, - blocking: Option, - is_async: bool, - aliases: Vec, - about: Option, - rename: Option, - exec_ctx: ExecutionContext, - display: Option, - metadata: HashMap, -} - -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, - } - } -} - -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, -} - -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 d2bf990..0000000 --- a/rpc-toolkit-macro-internals/src/command/parse.rs +++ /dev/null @@ -1,999 +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 = true; - } - 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 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, - }; - match attr.parse_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.short.is_some() { - 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", - )); - } - 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::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() { - return Err(Error::new( - rename.span(), - "duplicate argument `rename`", - )); - } - 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("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")); - } - } - 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") => { - if opt.default.is_some() { - return Err(Error::new(p.span(), "duplicate argument `default`")); - } - opt.default = Some(None); - } - _ => { - return Err(Error::new(arg.span(), "unknown argument")); - } - } - } - } - 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::Context(_)) { - 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 c8535d7..0000000 --- a/rpc-toolkit-macro-internals/src/lib.rs +++ /dev/null @@ -1,21 +0,0 @@ -macro_rules! macro_try { - ($x:expr) => { - match $x { - Ok(a) => a, - Err(e) => return e.to_compile_error(), - } - }; -} - -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/.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 83d3682..0000000 --- a/rpc-toolkit-macro/src/lib.rs +++ /dev/null @@ -1,62 +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() -} - -#[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/.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 5305021..0000000 --- a/rpc-toolkit/Cargo.toml +++ /dev/null @@ -1,39 +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 = ["cbor"] - -[dependencies] -clap = "3" -futures = "0.3" -hyper = { version = "0.14", features = [ - "server", - "http1", - "http2", - "tcp", - "stream", - "client", -] } -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" } -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" 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/context.rs b/rpc-toolkit/src/context.rs deleted file mode 100644 index 1cae28e..0000000 --- a/rpc-toolkit/src/context.rs +++ /dev/null @@ -1,41 +0,0 @@ -use lazy_static::lazy_static; -use reqwest::Client; -use url::{Host, Url}; - -lazy_static! { - static ref DEFAULT_CLIENT: Client = Client::new(); -} - -pub trait Context { - 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) - } -} diff --git a/rpc-toolkit/src/lib.rs b/rpc-toolkit/src/lib.rs deleted file mode 100644 index 861eab7..0000000 --- a/rpc-toolkit/src/lib.rs +++ /dev/null @@ -1,58 +0,0 @@ -/// `#[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; -/// `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; - -pub mod command_helpers; -mod context; -mod metadata; -pub mod rpc_server_helpers; diff --git a/rpc-toolkit/src/metadata.rs b/rpc-toolkit/src/metadata.rs deleted file mode 100644 index c300f76..0000000 --- a/rpc-toolkit/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], -); 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/tests/test.rs b/rpc-toolkit/tests/test.rs deleted file mode 100644 index c3d59b1..0000000 --- a/rpc-toolkit/tests/test.rs +++ /dev/null @@ -1,214 +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::{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/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..5890bff --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,333 @@ +use std::collections::VecDeque; +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}; +use serde::de::DeserializeOwned; +use serde::Serialize; +use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}; +use url::Url; +use yajrc::{Id, RpcError}; + +use crate::util::{internal_error, invalid_params, parse_error, without, Flat, PhantomData}; +use crate::{ + AnyHandler, CliBindings, CliBindingsAny, Empty, HandleAny, HandleAnyArgs, HandlerArgs, + HandlerArgsFor, HandlerFor, HandlerTypes, Name, ParentHandler, PrintCliResult, +}; + +type GenericRpcMethod<'a> = yajrc::GenericRpcMethod<&'a str, Value, Value>; +type RpcRequest<'a> = yajrc::RpcRequest>; +type RpcResponse<'a> = yajrc::RpcResponse>; + +pub struct CliApp { + _phantom: PhantomData<(Context, Config)>, + make_ctx: Box Result + Send + Sync>, + root_handler: ParentHandler, +} +impl + CliApp +{ + pub fn new Result + Send + Sync + 'static>( + make_ctx: MakeCtx, + root_handler: ParentHandler, + ) -> Self { + Self { + _phantom: PhantomData::new(), + make_ctx: Box::new(make_ctx), + root_handler, + } + } + pub fn run(self, args: impl IntoIterator) -> Result<(), RpcError> { + let mut cmd = Config::command(); + 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)); + } + } + 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)?; + let res = root_handler.handle_sync(HandleAnyArgs { + context: ctx.clone(), + parent_method: VecDeque::new(), + method: method.clone(), + params: params.clone(), + inherited: crate::Empty {}, + })?; + root_handler.cli_display( + HandleAnyArgs { + context: ctx, + parent_method: VecDeque::new(), + method, + params, + inherited: crate::Empty {}, + }, + res, + )?; + Ok(()) + } +} + +pub trait CallRemote: crate::Context { + fn call_remote( + &self, + method: &str, + params: Value, + extra: Extra, + ) -> impl Future> + Send; +} + +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(&rpc_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")] + 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<(Context, RemoteContext, Extra)>, + handler: RemoteHandler, +} +impl + CallRemoteHandler +{ + pub fn new(handler: RemoteHandler) -> Self { + Self { + _phantom: PhantomData::new(), + handler: handler, + } + } +} +impl Clone + for CallRemoteHandler +{ + fn clone(&self) -> Self { + Self { + _phantom: PhantomData::new(), + handler: self.handler.clone(), + } + } +} +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 + RemoteHandler: HandlerTypes, + RemoteHandler::Params: Serialize, + RemoteHandler::Ok: DeserializeOwned, + RemoteHandler::Err: From, + Extra: Send + Sync + 'static, +{ + type Params = Flat; + type InheritedParams = RemoteHandler::InheritedParams; + type Ok = RemoteHandler::Ok; + type Err = RemoteHandler::Err; +} + +impl HandlerFor + for CallRemoteHandler +where + Context: CallRemote, + RemoteContext: crate::Context, + RemoteHandler: HandlerFor, + RemoteHandler::Params: Serialize, + RemoteHandler::Ok: DeserializeOwned, + RemoteHandler::Err: From, + Extra: Serialize + Send + Sync + 'static, +{ + async fn handle_async( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + let full_method = handle_args + .parent_method + .into_iter() + .chain(handle_args.method) + .collect::>(); + match handle_args + .context + .call_remote( + &full_method.join("."), + without(handle_args.raw_params.clone(), &handle_args.params.1) + .map_err(invalid_params)?, + handle_args.params.1, + ) + .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>) -> 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 +where + Context: CallRemote, + RemoteHandler: PrintCliResult, + RemoteHandler::Params: Serialize, + RemoteHandler::Ok: DeserializeOwned, + RemoteHandler::Err: From, + Extra: Send + Sync + 'static, +{ + 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: params.0, + inherited_params, + raw_params, + }, + result, + ) + } +} +impl CliBindings + for CallRemoteHandler +where + Context: crate::Context, + RemoteHandler: CliBindings, + RemoteHandler::Params: 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/command_helpers.rs b/src/command_helpers.rs new file mode 100644 index 0000000..87f9cda --- /dev/null +++ b/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/src/context.rs b/src/context.rs new file mode 100644 index 0000000..d7b1462 --- /dev/null +++ b/src/context.rs @@ -0,0 +1,9 @@ +use std::sync::Arc; + +use tokio::runtime::Runtime; + +pub trait Context: Send + Sync + 'static { + fn runtime(&self) -> Option> { + None + } +} diff --git a/src/handler/adapters.rs b/src/handler/adapters.rs new file mode 100644 index 0000000..007a8fe --- /dev/null +++ b/src/handler/adapters.rs @@ -0,0 +1,866 @@ +use std::any::TypeId; +use std::collections::VecDeque; +use std::fmt::Debug; + +use clap::builder::{IntoResettable, StyledStr}; +use clap::{CommandFactory, FromArgMatches}; +use imbl_value::imbl::OrdMap; +use imbl_value::Value; +use serde::de::DeserializeOwned; +use serde::Serialize; +use yajrc::RpcError; + +use crate::util::{Flat, PhantomData}; +use crate::{ + CallRemote, CallRemoteHandler, CliBindings, DynHandler, Handler, HandlerArgs, HandlerArgsFor, + HandlerFor, HandlerTypes, OrEmpty, PrintCliResult, WithContext, +}; + +pub trait HandlerExt: HandlerFor + Sized { + fn no_cli(self) -> NoCli; + fn no_display(self) -> NoDisplay; + 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( + self, + display: F, + ) -> CustomDisplayFn + where + F: Fn(HandlerArgsFor, Self::Ok) -> Result<(), Self::Err>; + fn with_inherited( + self, + f: F, + ) -> InheritanceHandler + 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 { + 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< + C, + 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(HandlerArgsFor, Self::Ok) -> Result<(), Self::Err>, + { + CustomDisplayFn { + _phantom: PhantomData::new(), + print: display, + handler: self, + } + } + fn with_inherited( + self, + f: F, + ) -> InheritanceHandler + where + F: Fn(Params, InheritedParams) -> Self::InheritedParams, + { + InheritanceHandler { + _phantom: PhantomData::new(), + handler: self, + inherit: f, + } + } + fn with_call_remote(self) -> RemoteCaller { + RemoteCaller { + _phantom: PhantomData::new(), + handler: self, + } + } + + fn with_about(self, message: M) -> WithAbout + where + M: IntoResettable, + { + WithAbout { + handler: self, + message, + } + } +} + +#[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; +} +impl HandlerFor for NoCli +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 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, + _: &clap::ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + unimplemented!() + } + fn cli_display(&self, _: HandlerArgsFor, _: Self::Ok) -> Result<(), Self::Err> { + unimplemented!() + } +} + +#[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: crate::Context, + H: HandlerTypes, + H::Params: FromArgMatches + CommandFactory + Serialize, +{ + fn print(&self, _: HandlerArgsFor, _: Self::Ok) -> Result<(), Self::Err> { + 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 { + 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; +} + +impl HandlerFor for CustomDisplay +where + Context: crate::Context, + H: HandlerFor, + P: Send + Sync + Clone + '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 PrintCliResult for CustomDisplay +where + Context: crate::Context, + H: HandlerTypes, + P: PrintCliResult< + Context, + Params = H::Params, + InheritedParams = H::InheritedParams, + Ok = H::Ok, + Err = H::Err, + > + Send + + Sync + + Clone + + 'static, +{ + fn print( + &self, + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }: HandlerArgsFor, + result: Self::Ok, + ) -> Result<(), Self::Err> { + self.print.print( + HandlerArgs { + context, + parent_method, + method, + params, + inherited_params, + raw_params, + }, + result, + ) + } +} +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, + print: F, + handler: H, +} +impl Clone for CustomDisplayFn { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData::new(), + 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; +} + +impl HandlerFor for CustomDisplayFn +where + Context: crate::Context, + C: 'static, + H: HandlerFor, + F: Send + Sync + Clone + '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 PrintCliResult for CustomDisplayFn +where + Context: crate::Context, + H: HandlerTypes, + F: Fn(HandlerArgsFor, H::Ok) -> Result<(), H::Err> + Send + Sync + Clone + 'static, +{ + fn print( + &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, + ) + } +} +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)>, + handler: H, +} +impl Clone for RemoteCaller { + fn clone(&self) -> Self { + Self { + _phantom: PhantomData::new(), + handler: self.handler.clone(), + } + } +} +impl Debug for RemoteCaller { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_tuple("RemoteCaller").field(&self.handler).finish() + } +} + +impl Handler + for WithContext> +where + Context: crate::Context + CallRemote, + RemoteContext: crate::Context, + H: HandlerFor + CliBindings, + H::Ok: Serialize + DeserializeOwned, + H::Err: From, + H::Params: Serialize + DeserializeOwned, + H::InheritedParams: OrEmpty, + RpcError: From, + Inherited: Send + Sync + 'static, +{ + 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 + } + } +} + +pub struct InheritanceHandler { + _phantom: PhantomData<(Params, InheritedParams)>, + handler: H, + inherit: F, +} +impl Clone + for InheritanceHandler +{ + fn clone(&self) -> Self { + Self { + _phantom: PhantomData::new(), + 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; +} + +impl HandlerFor + for InheritanceHandler +where + Context: crate::Context, + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + H: HandlerFor, + F: Fn(Params, InheritedParams) -> H::InheritedParams + Send + Sync + Clone + '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: (self.inherit)(inherited_params.0, inherited_params.1), + 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: (self.inherit)(inherited_params.0, inherited_params.1), + 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 InheritanceHandler +where + 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) -> 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, + inherited_params: (self.inherit)(inherited_params.0, inherited_params.1), + raw_params, + }, + result, + ) + } +} + +#[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/src/handler/from_fn.rs b/src/handler/from_fn.rs new file mode 100644 index 0000000..1311805 --- /dev/null +++ b/src/handler/from_fn.rs @@ -0,0 +1,631 @@ +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::{ + CliBindings, Empty, HandlerArgs, HandlerArgsFor, HandlerFor, HandlerTypes, 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::new(), + 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: crate::Context, + Self: HandlerTypes, + ::Ok: Display, +{ + fn print(&self, _: HandlerArgsFor, result: Self::Ok) -> Result<(), Self::Err> { + Ok(println!("{result}")) + } +} +impl CliBindings for FromFn +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, + 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 fn from_fn(function: F) -> FromFn +where + FromFn: HandlerTypes, +{ + FromFn { + function, + _phantom: PhantomData::new(), + blocking: false, + metadata: OrdMap::new(), + } +} + +pub fn from_fn_blocking(function: F) -> FromFn +where + FromFn: HandlerTypes, +{ + FromFn { + function, + _phantom: PhantomData::new(), + blocking: true, + metadata: OrdMap::new(), + } +} + +pub struct FromFnAsync { + _phantom: PhantomData<(Fut, T, E, Args)>, + function: F, + metadata: OrdMap<&'static str, Value>, +} +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 { + Self { + _phantom: PhantomData::new(), + 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: crate::Context, + Self: HandlerTypes, + ::Ok: Display, +{ + fn print(&self, _: HandlerArgsFor, result: Self::Ok) -> Result<(), Self::Err> { + Ok(println!("{result}")) + } +} +impl CliBindings for FromFnAsync +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, + 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 fn from_fn_async(function: F) -> FromFnAsync +where + FromFnAsync: HandlerTypes, +{ + FromFnAsync { + function, + _phantom: PhantomData::new(), + metadata: OrdMap::new(), + } +} + +impl HandlerTypes + for FromFn> +where + F: Fn(HandlerArgs) -> Result + + Send + + Sync + + Clone + + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, + Context: crate::Context, + Params: Send + Sync, + InheritedParams: Send + Sync, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = T; + type Err = E; +} + +impl HandlerFor + for FromFn> +where + F: Fn(HandlerArgs) -> Result + + Send + + Sync + + Clone + + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, + Context: crate::Context, + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, +{ + 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>) -> 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: crate::Context, + Params: Send + Sync, + InheritedParams: Send + Sync, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = T; + type Err = E; +} + +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: crate::Context, + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, +{ + async fn handle_async( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + (self.function)(handle_args).await + } + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} + +impl HandlerTypes for FromFn +where + F: Fn() -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Empty; + type InheritedParams = Empty; + type Ok = T; + type Err = E; +} + +impl HandlerFor for FromFn +where + Context: crate::Context, + F: Fn() -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + fn handle_sync(&self, _: HandlerArgsFor) -> Result { + (self.function)() + } + 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>) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} +impl HandlerTypes for FromFnAsync +where + F: Fn() -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Empty; + type InheritedParams = Empty; + type Ok = T; + type Err = E; +} + +impl HandlerFor for FromFnAsync +where + Context: crate::Context, + F: Fn() -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + async fn handle_async(&self, _: HandlerArgsFor) -> Result { + (self.function)().await + } + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} + +impl HandlerTypes for FromFn +where + Context: crate::Context, + F: Fn(Context) -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Empty; + type InheritedParams = Empty; + type Ok = T; + type Err = E; +} + +impl HandlerFor for FromFn +where + Context: crate::Context, + F: Fn(Context) -> Result + Send + Sync + Clone + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + fn handle_sync( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + (self.function)(handle_args.context) + } + 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>) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} +impl HandlerTypes for FromFnAsync +where + Context: crate::Context, + F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Empty; + type InheritedParams = Empty; + type Ok = T; + type Err = E; +} + +impl HandlerFor for FromFnAsync +where + Context: crate::Context, + F: Fn(Context) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + async fn handle_async( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + (self.function)(handle_args.context).await + } + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} + +impl HandlerTypes for FromFn +where + Context: crate::Context, + 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 = Empty; + type Ok = T; + type Err = E; +} + +impl HandlerFor for FromFn +where + Context: crate::Context, + 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: HandlerArgsFor, + ) -> Result { + let HandlerArgs { + context, params, .. + } = handle_args; + (self.function)(context, params) + } + 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>) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} +impl HandlerTypes for FromFnAsync +where + Context: crate::Context, + F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Params; + type InheritedParams = Empty; + type Ok = T; + type Err = E; +} + +impl HandlerFor + for FromFnAsync +where + Context: crate::Context, + F: Fn(Context, Params) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + async fn handle_async( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + let HandlerArgs { + context, params, .. + } = handle_args; + (self.function)(context, params).await + } + fn metadata(&self, _: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} + +impl HandlerTypes + for FromFn +where + Context: crate::Context, + F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = T; + type Err = E; +} + +impl HandlerFor + for FromFn +where + Context: crate::Context, + F: Fn(Context, Params, InheritedParams) -> Result + Send + Sync + Clone + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + fn handle_sync( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + let HandlerArgs { + context, + params, + inherited_params, + .. + } = handle_args; + (self.function)(context, params, inherited_params) + } + 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>) -> OrdMap<&'static str, Value> { + self.metadata.clone() + } +} +impl HandlerTypes + for FromFnAsync +where + Context: crate::Context, + F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = T; + type Err = E; +} + +impl HandlerFor + for FromFnAsync +where + Context: crate::Context, + F: Fn(Context, Params, InheritedParams) -> Fut + Send + Sync + Clone + 'static, + Fut: Future> + Send + 'static, + Params: DeserializeOwned + Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, + T: Send + Sync + 'static, + E: Send + Sync + 'static, +{ + async fn handle_async( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + let HandlerArgs { + context, + params, + inherited_params, + .. + } = handle_args; + (self.function)(context, params, inherited_params).await + } + 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 new file mode 100644 index 0000000..796deeb --- /dev/null +++ b/src/handler/mod.rs @@ -0,0 +1,426 @@ +use std::any::TypeId; +use std::collections::VecDeque; +use std::fmt::Debug; +use std::marker::PhantomData; +use std::ops::Deref; +use std::sync::Arc; + +use clap::{ArgMatches, Command, 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::util::{internal_error, invalid_params, Flat}; + +pub mod adapters; +pub mod from_fn; +pub mod parent; + +pub use adapters::*; +pub use from_fn::*; +pub use parent::*; + +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> + where + H: HandlerTypes, + H::InheritedParams: OrEmpty, + H::Params: DeserializeOwned, + { + let Self { + context, + parent_method, + method, + params, + inherited, + } = self; + Ok(HandlerArgs { + context, + parent_method, + method, + params: imbl_value::from_value(params.clone())?, + inherited_params: OrEmpty::from_t(inherited), + raw_params: params, + }) + } +} + +#[async_trait::async_trait] +pub(crate) trait HandleAny: Send + Sync { + 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>) -> 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 { + 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 { + self.deref().handle_async(handle_args).await + } + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + self.deref().metadata(method) + } + fn method_from_dots(&self, method: &str) -> Option> { + self.deref().method_from_dots(method) + } + fn cli(&self) -> Option<&dyn CliBindingsAny> { + self.deref().cli() + } +} + +pub(crate) trait CliBindingsAny { + type Inherited; + 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: HandlerTypes { + const NO_CLI: bool = false; + fn cli_command(&self) -> Command; + fn cli_parse( + &self, + matches: &ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error>; + fn cli_display( + &self, + handle_args: HandlerArgsFor, + result: Self::Ok, + ) -> Result<(), Self::Err>; +} + +pub trait PrintCliResult: HandlerTypes { + fn print( + &self, + handle_args: HandlerArgsFor, + result: Self::Ok, + ) -> Result<(), Self::Err>; +} + +#[allow(private_interfaces)] +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 { + fn clone(&self) -> Self { + 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 +{ + type Inherited = Inherited; + fn handle_sync( + &self, + handle_args: HandleAnyArgs, + ) -> Result { + self.0.handle_sync(handle_args) + } + async fn handle_async( + &self, + handle_args: HandleAnyArgs, + ) -> Result { + self.0.handle_async(handle_args).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) + } + fn cli(&self) -> Option<&dyn CliBindingsAny> { + self.0.cli() + } +} + +#[allow(type_alias_bounds)] +pub type HandlerArgsFor = + HandlerArgs; + +#[derive(Debug, Clone)] +pub struct HandlerArgs< + Context: crate::Context, + Params: Send + Sync = Empty, + InheritedParams: Send + Sync = Empty, +> { + pub context: Context, + pub parent_method: VecDeque<&'static str>, + pub method: VecDeque<&'static str>, + pub params: Params, + pub inherited_params: InheritedParams, + pub raw_params: Value, +} + +pub trait HandlerTypes { + type Params: Send + Sync; + type InheritedParams: Send + Sync; + type Ok: Send + Sync; + type Err: Send + Sync; +} + +pub trait HandlerFor: + HandlerTypes + Clone + Send + Sync + 'static +{ + fn handle_sync( + &self, + handle_args: HandlerArgsFor, + ) -> Result { + 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, + handle_args: HandlerArgsFor, + ) -> impl Future> + Send; + fn handle_async_with_sync<'a>( + &'a self, + 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, + ) -> impl Future> + Send + 'a { + async move { + let s = self.clone(); + 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)] + fn metadata(&self, method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { + OrdMap::new() + } + #[allow(unused_variables)] + fn method_from_dots(&self, method: &str) -> Option> { + if method.is_empty() { + Some(VecDeque::new()) + } else { + None + } + } +} + +pub trait Handler { + type H: HandlerTypes; + fn handler_for(self) -> Option>; +} + +pub struct WithContext { + _phantom: PhantomData, + handler: H, +} +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, + handler, + } + } +} +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) + .finish() + } +} + +#[async_trait::async_trait] +impl HandleAny for AnyHandler +where + Context: crate::Context, + H: HandlerFor + CliBindings, + H::Params: DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + H::InheritedParams: OrEmpty, + RpcError: From, + Inherited: Send + Sync, +{ + type Inherited = Inherited; + 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>) -> OrdMap<&'static str, Value> { + self.handler.metadata(method) + } + fn method_from_dots(&self, method: &str) -> Option> { + self.handler.method_from_dots(method) + } + fn cli(&self) -> Option<&dyn CliBindingsAny> { + if H::NO_CLI { + None + } else { + Some(self) + } + } +} + +impl CliBindingsAny for AnyHandler +where + Context: crate::Context, + H: CliBindings, + H::Params: DeserializeOwned, + H::Ok: Serialize + DeserializeOwned, + RpcError: From, + H::InheritedParams: OrEmpty, + Inherited: Send + Sync, +{ + type Inherited = Inherited; + 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 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(_: Flat) -> Self { + Empty {} + } +} + +#[derive(Debug, Clone, Copy, Deserialize, Serialize, Parser)] +pub enum Never {} diff --git a/src/handler/parent.rs b/src/handler/parent.rs new file mode 100644 index 0000000..5668780 --- /dev/null +++ b/src/handler/parent.rs @@ -0,0 +1,389 @@ +use std::collections::VecDeque; +use std::fmt::Debug; + +use clap::{ArgMatches, Command, CommandFactory, FromArgMatches}; +use imbl_value::imbl::OrdMap; +use imbl_value::Value; +use serde::Serialize; +use yajrc::RpcError; + +use crate::util::{combine, Flat, PhantomData}; +use crate::{ + CliBindings, DynHandler, Empty, HandleAny, HandleAnyArgs, Handler, HandlerArgs, HandlerArgsFor, + HandlerFor, HandlerTypes, WithContext, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +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) Option>, + pub(crate) OrdMap>>, +); +impl Clone for SubcommandMap { + fn clone(&self) -> Self { + Self(self.0.clone(), self.1.clone()) + } +} +impl Debug for SubcommandMap { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + 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 set_root(&mut self, handler: DynHandler) { + self.0 = Some(handler); + } + 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 + } + } +} + +pub struct ParentHandler { + _phantom: PhantomData, + pub(crate) subcommands: SubcommandMap, + metadata: OrdMap<&'static str, Value>, +} +impl ParentHandler { + pub fn new() -> Self { + Self { + _phantom: PhantomData::new(), + subcommands: SubcommandMap(None, 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::new(), + 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() + } +} + +impl + ParentHandler +{ + pub fn subcommand(mut self, name: &'static str, handler: H) -> Self + where + WithContext: Handler>, + { + if let Some(h) = DynHandler::new(handler) { + self.subcommands.insert(name.into(), h); + } + self + } + pub fn root_handler(mut self, handler: H) -> Self + where + WithContext: Handler, + as Handler>::H: HandlerTypes, + { + if let Some(h) = DynHandler::new(handler) { + self.subcommands.set_root(h); + } + self + } +} + +impl HandlerTypes + for ParentHandler +where + Params: Send + Sync, + InheritedParams: Send + Sync, +{ + type Params = Params; + type InheritedParams = InheritedParams; + type Ok = Value; + type Err = RpcError; +} + +impl HandlerFor + for ParentHandler +where + Context: crate::Context, + Params: Send + Sync + 'static, + InheritedParams: Send + Sync + 'static, +{ + fn handle_sync( + &self, + HandlerArgs { + context, + mut parent_method, + mut method, + params, + inherited_params, + raw_params, + }: HandlerArgsFor, + ) -> Result { + 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), + }) + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } else { + 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( + &self, + HandlerArgs { + context, + mut parent_method, + mut method, + params, + inherited_params, + raw_params, + }: HandlerArgsFor, + ) -> Result { + 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 + } else { + Err(yajrc::METHOD_NOT_FOUND_ERROR) + } + } else { + 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(cmd) = method.pop_front() { + if let Some((_, handler)) = self.subcommands.get(cmd) { + handler.metadata(method).union(metadata) + } else { + metadata + } + } else { + if let Some(handler) = self.subcommands.get_root() { + handler.metadata(method).union(metadata) + } else { + metadata + } + } + } + fn method_from_dots(&self, method: &str) -> 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)) + }; + 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) + } + } +} + +impl CliBindings + for ParentHandler +where + Context: crate::Context, + Params: FromArgMatches + CommandFactory + 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()) { + cli.cli_command().subcommand_required(false) + } else { + Params::command().subcommand_required(true) + }; + for (name, handler) in &self.subcommands.1 { + match (name, handler.cli()) { + (Name(name), Some(cli)) => { + base = base.subcommand(cli.cli_command().name(name)); + } + _ => (), + } + } + base + } + fn cli_parse( + &self, + root_matches: &ArgMatches, + ) -> Result<(VecDeque<&'static str>, Value), clap::Error> { + let (name, matches) = match root_matches.subcommand() { + Some((name, matches)) => (Some(name), matches), + None => (None, root_matches), + }; + if let Some(name) = name { + 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 + .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) + })?, + )) + } else { + Ok((VecDeque::new(), root_params)) + } + } else { + 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( + &self, + HandlerArgs { + context, + mut parent_method, + mut method, + params, + inherited_params, + raw_params, + }: 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((_, 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 { + 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) + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..062b5c5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,15 @@ +#![cfg_attr(feature = "nightly", feature(const_trait_impl, const_type_id))] + +pub use cli::*; +// pub use command::*; +pub use context::*; +pub use handler::*; +pub use server::*; +pub use {clap, futures, reqwest, serde, serde_json, tokio, url, yajrc}; + +mod cli; +pub mod command_helpers; +mod context; +mod handler; +mod server; +pub mod util; diff --git a/src/server/http.rs b/src/server/http.rs new file mode 100644 index 0000000..3bf5698 --- /dev/null +++ b/src/server/http.rs @@ -0,0 +1,302 @@ +use axum::body::Body; +use axum::extract::Request; +use axum::handler::Handler; +use axum::response::Response; +use futures::future::{join_all, BoxFuture}; +use futures::{Future, FutureExt}; +use http::header::{CONTENT_LENGTH, CONTENT_TYPE}; +use http_body_util::BodyExt; +use imbl_value::imbl::Vector; +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::{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(Body::from(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(Body::from(body)) + .unwrap_or_else(|_| fallback_rpc_error_response()) +} + +pub trait Middleware: Clone + Send + Sync + 'static { + type Metadata: DeserializeOwned + Send + 'static; + #[allow(unused_variables)] + fn process_http_request( + &mut self, + context: &Context, + request: &mut Request, + ) -> impl Future> + Send { + async { Ok(()) } + } + #[allow(unused_variables)] + fn process_rpc_request( + &mut self, + context: &Context, + metadata: Self::Metadata, + request: &mut RpcRequest, + ) -> impl Future> + Send { + async { Ok(()) } + } + #[allow(unused_variables)] + fn process_rpc_response( + &mut self, + context: &Context, + response: &mut RpcResponse, + ) -> impl Future + Send { + async { () } + } + #[allow(unused_variables)] + fn process_http_response( + &mut self, + context: &Context, + response: &mut Response, + ) -> impl Future + Send { + async { () } + } +} + +#[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, + context: &'a Context, + metadata: Value, + request: &'a mut RpcRequest, + ) -> BoxFuture<'a, Result<(), RpcResponse>>; + 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, ()>; +} +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).boxed() + } + 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(), + }, + 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).boxed() + } + fn process_http_response<'a>( + &'a mut self, + context: &'a Context, + response: &'a mut Response, + ) -> BoxFuture<'a, ()> { + >::process_http_response(self, context, response).boxed() + } +} + +struct DynMiddleware(Box>); +impl Clone for DynMiddleware { + fn clone(&self) -> Self { + self.0.dyn_clone() + } +} + +pub struct HttpServer { + inner: Server, + middleware: Vector>, +} +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: Vector::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_back(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(&ctx, &mut mid, rpc_req).await, + ); + for middleware in mid.iter_mut() { + middleware.0.process_http_response(&ctx, &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(&ctx, &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(&ctx, &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, + ctx: &Context, + mid: &mut Vector>, + 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()) + { + Some(a) => a, + None => { + return RpcResponse { + id: req.id, + result: Err(yajrc::METHOD_NOT_FOUND_ERROR), + } + } + }, + ) + .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(ctx, 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(ctx, &mut res).await; + } + res + } + pub fn handle(&self, req: Request) -> BoxFuture<'static, Response> { + let server = self.clone(); + async move { server.process_http_request(req).await }.boxed() + } +} + +impl Handler<(), ()> for HttpServer { + type Future = BoxFuture<'static, Response>; + fn call(self, req: Request, _: ()) -> Self::Future { + self.handle(req) + } +} diff --git a/src/server/mod.rs b/src/server/mod.rs new file mode 100644 index 0000000..3da0c4c --- /dev/null +++ b/src/server/mod.rs @@ -0,0 +1,131 @@ +use std::collections::VecDeque; +use std::sync::Arc; + +use futures::future::{join_all, BoxFuture}; +use futures::{Future, FutureExt, Stream, StreamExt}; +use imbl_value::Value; +use yajrc::{RpcError, RpcMethod}; + +use crate::util::{invalid_request, JobRunner}; +use crate::{AnyHandler, Empty, HandleAny, HandleAnyArgs, ParentHandler}; + +pub type GenericRpcMethod = yajrc::GenericRpcMethod; +pub type RpcRequest = yajrc::RpcRequest; +pub type RpcResponse = yajrc::RpcResponse; +pub type SingleOrBatchRpcRequest = yajrc::SingleOrBatchRpcRequest; + +pub mod http; +pub mod socket; + +pub use http::*; +pub use socket::*; + +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, + Fut: Future> + Send + 'static, + >( + make_ctx: MakeCtx, + root_handler: ParentHandler, + ) -> Self { + Server { + make_ctx: Arc::new(move || make_ctx().boxed()), + root_handler: Arc::new(AnyHandler::new(root_handler)), + } + } + + pub fn handle_command( + &self, + method: &str, + params: Value, + ) -> impl Future> + Send + 'static { + let (make_ctx, root_handler, method) = ( + self.make_ctx.clone(), + self.root_handler.clone(), + self.root_handler.method_from_dots(method), + ); + + async move { + root_handler + .handle_async(HandleAnyArgs { + context: make_ctx().await?, + parent_method: VecDeque::new(), + method: method.ok_or_else(|| yajrc::METHOD_NOT_FOUND_ERROR)?, + params, + inherited: crate::Empty {}, + }) + .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(), params)))(); + async move { + RpcResponse { + id, + result: match handle { + Ok(handle) => handle.await, + Err(e) => Err(e), + }, + } + } + } + + 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) }.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) }.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 { + async_stream::try_stream! { + 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; + } + } + } +} diff --git a/src/server/socket.rs b/src/server/socket.rs new file mode 100644 index 0000000..d51201b --- /dev/null +++ b/src/server/socket.rs @@ -0,0 +1,95 @@ +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::Notify; +use yajrc::RpcError; + +use crate::util::{parse_error, JobRunner, StreamUntil}; +use crate::Server; + +#[derive(Clone)] +pub struct ShutdownHandle(Arc); +impl ShutdownHandle { + pub fn shutdown(self) { + self.0.notify_one(); + } +} + +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(Notify::new()); + (ShutdownHandle(shutdown.clone()), async move { + let mut runner = JobRunner::>::new(); + 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( + 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/src/util.rs b/src/util.rs new file mode 100644 index 0000000..9217b32 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,315 @@ +use std::fmt::{Debug, Display}; +use std::task::Waker; + +use futures::future::{BoxFuture, FusedFuture}; +use futures::stream::FusedStream; +use futures::{Future, FutureExt, Stream, StreamExt}; +use imbl_value::Value; +use serde::de::DeserializeOwned; +use serde::ser::Error; +use serde::{Deserialize, Serialize}; +use yajrc::RpcError; + +pub fn extract(value: &Value) -> Result { + 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 { + let (Value::Object(mut v1), Value::Object(v2)) = (v1, v2) else { + 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(imbl_value::Error { + kind: imbl_value::ErrorKind::Serialization, + source: serde_json::Error::custom(lazy_format::lazy_format!( + "duplicate key: {key}" + )), + }); + } + } + 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 invalid_request(e: imbl_value::Error) -> RpcError { + RpcError { + data: Some(e.to_string().into()), + ..yajrc::INVALID_REQUEST_ERROR + } +} + +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 + 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)) + } +} +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) + } +} +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>, + 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> { + 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(), + } + } + pub async fn next_result< + Src: Stream + Unpin, + Fut: Future + Send + 'a, + >( + &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 = 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; + } + } + } + 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> { + 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) + } + a => a.map(Some), + } + } +} + +#[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() + } +} + +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 {} diff --git a/tests/handler.rs b/tests/handler.rs new file mode 100644 index 0000000..dc41e8f --- /dev/null +++ b/tests/handler.rs @@ -0,0 +1,251 @@ +use std::ffi::OsString; +use std::fmt::Display; +use std::path::{Path, PathBuf}; +use std::sync::Arc; + +use clap::Parser; +use futures::future::ready; +use imbl_value::Value; +use rpc_toolkit::{ + call_remote_socket, from_fn, from_fn_async, CallRemote, CliApp, Context, Empty, HandlerExt, + ParentHandler, Server, +}; +use serde::{Deserialize, Serialize}; +use tokio::runtime::Runtime; +use tokio::sync::{Mutex, OnceCell}; +use yajrc::RpcError; + +#[derive(Parser, Deserialize)] +#[command( + name = "test-cli", + version, + author, + about = "This is a test cli application." +)] +struct CliConfig { + #[arg(long = "host")] + host: Option, + #[arg(short = 'c', long = "config")] + config: Option, +} +impl CliConfig { + fn load_rec(&mut self) -> Result<(), RpcError> { + if let Some(path) = self.config.as_ref() { + 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()?; + 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: PathBuf, + rt: OnceCell>, +} +#[derive(Clone)] +struct CliContext(Arc); +impl Context for CliContext { + fn runtime(&self) -> Option> { + if self.0.rt.get().is_none() { + let rt = Arc::new(Runtime::new().unwrap()); + self.0.rt.set(rt.clone()).unwrap_or_default(); + Some(rt) + } else { + self.0.rt.get().cloned() + } + } +} + +impl CallRemote for CliContext { + async fn call_remote(&self, method: &str, params: Value, _: Empty) -> Result { + call_remote_socket( + tokio::net::UnixStream::connect(&self.0.host).await.unwrap(), + method, + params, + ) + .await + } +} + +fn make_cli() -> CliApp { + CliApp::new( + |mut config: CliConfig| { + config.load_rec()?; + Ok(CliContext(Arc::new(CliContextSeed { + host: config + .host + .unwrap_or_else(|| Path::new("./rpc.sock").to_owned()), + rt: OnceCell::new(), + }))) + }, + make_api(), + ) +} + +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 { + 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, + } + #[derive(Debug, Clone, Deserialize, Serialize, Parser)] + struct InheritParams { + donde: String, + } + ParentHandler::::new() + .subcommand( + "echo", + 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)), + )) + }, + ) + .with_custom_display_fn(|_, a| Ok(println!("{a}"))) + .with_about("Testing") + .with_call_remote::(), + ) + .subcommand( + "hello", + 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( + "donde", + from_fn(|c: CliContext, _: (), donde| { + Ok::<_, RpcError>( + format!( + "Subcommand No Cli: Host {host} Donde = {donde}", + host = c.0.host.display() + ) + .to_string(), + ) + }) + .with_inherited(|InheritParams { donde }, _| donde) + .no_cli(), + ), + ) + .subcommand( + "fizz", + ParentHandler::::new().root_handler( + from_fn(|c: CliContext, _: Empty, InheritParams { donde }| { + Ok::<_, RpcError>( + format!( + "Root Command: Host {host} Donde = {donde}", + host = c.0.host.display(), + ) + .to_string(), + ) + }) + .with_inherited(|a, _| a), + ), + ) + .subcommand( + "error", + ParentHandler::::new().root_handler( + from_fn(|_: CliContext, _: Empty, InheritParams { .. }| { + Err::(RpcError { + code: 1, + message: "This is an example message".into(), + data: None, + }) + }) + .with_inherited(|a, _| a) + .no_cli(), + ), + ) +} + +pub fn internal_error(e: impl Display) -> RpcError { + RpcError { + data: Some(e.to_string().into()), + ..yajrc::INTERNAL_ERROR + } +} + +#[test] +fn test_cli() { + make_cli() + .run(["test-cli", "hello", "me"].iter().map(OsString::from)) + .unwrap(); + make_cli() + .run(["test-cli", "fizz", "buzz"].iter().map(OsString::from)) + .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", + ] + .iter() + .map(OsString::from), + ) + .unwrap(); + make_cli() + .run( + [ + "test-cli", + &format!("--host={}", path.display()), + "echo", + "bar", + ] + .iter() + .map(OsString::from), + ) + .unwrap(); + shutdown.shutdown() + }), + fut + ) + .0 + .unwrap(); +} diff --git a/tests/test.rs b/tests/test.rs new file mode 100644 index 0000000..dac9821 --- /dev/null +++ b/tests/test.rs @@ -0,0 +1,109 @@ +// 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 = (); +// } + +// 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, +// 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); +// } +// } + +// 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()); +// }