diff --git a/clients/rust/Cargo.lock b/clients/rust/Cargo.lock new file mode 100644 index 0000000..78d5aa4 --- /dev/null +++ b/clients/rust/Cargo.lock @@ -0,0 +1,1308 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +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 = "axum" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" +dependencies = [ + "axum-core", + "bytes", + "futures-util", + "http", + "http-body", + "http-body-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "serde_core", + "sync_wrapper", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum-core" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "sync_wrapper", + "tower-layer", + "tower-service", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "h2" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hyper" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "libc", + "pin-project-lite", + "socket2 0.6.3", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.0", + "serde", + "serde_core", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "matchit" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "mxgateway-client" +version = "0.1.0" +dependencies = [ + "prost", + "prost-types", + "serde_json", + "thiserror", + "tokio", + "tonic", + "tonic-build", +] + +[[package]] +name = "mxgw-cli" +version = "0.1.0" +dependencies = [ + "clap", + "mxgateway-client", + "serde_json", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "petgraph" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-build" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be769465445e8c1474e9c5dac2018218498557af32d9ed057325ec9a41ae81bf" +dependencies = [ + "heck", + "itertools", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost", + "prost-types", + "regex", + "syn", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.52.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tonic" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e581ba15a835f4d9ea06c55ab1bd4dce26fc53752c69a04aac00703bfb49ba9" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "prost", + "socket2 0.5.10", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac6f67be712d12f0b41328db3137e0d0757645d8904b4cb7d51cd9c2279e847" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "indexmap", + "pin-project-lite", + "slab", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[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_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_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.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/clients/rust/Cargo.toml b/clients/rust/Cargo.toml new file mode 100644 index 0000000..6256c10 --- /dev/null +++ b/clients/rust/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "mxgateway-client" +version = "0.1.0" +edition = "2021" +publish = false +build = "build.rs" + +[workspace] +members = ["crates/mxgw-cli"] +resolver = "2" + +[workspace.package] +edition = "2021" +version = "0.1.0" +publish = false + +[workspace.dependencies] +clap = { version = "4.5.53", features = ["derive"] } +prost = "0.13.5" +prost-types = "0.13.5" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" +thiserror = "2.0.17" +tokio = { version = "1.48.0", features = ["macros", "rt-multi-thread"] } +tonic = { version = "0.13.1", features = ["transport"] } +tonic-build = "0.13.1" + +[dependencies] +prost = { workspace = true } +prost-types = { workspace = true } +thiserror = { workspace = true } +tonic = { workspace = true } + +[dev-dependencies] +serde_json = { workspace = true } +tokio = { workspace = true } + +[build-dependencies] +tonic-build = { workspace = true } diff --git a/clients/rust/README.md b/clients/rust/README.md new file mode 100644 index 0000000..4c84c4f --- /dev/null +++ b/clients/rust/README.md @@ -0,0 +1,53 @@ +# Rust Client Workspace + +The Rust client workspace contains the MXAccess Gateway client library, a +test CLI, and scaffold tests for generated contract wiring. The library uses +the shared protobuf inputs documented in +`../../docs/client-proto-generation.md` so the Rust bindings compile against +the same public gateway and worker contracts as the server. + +## Layout + +```text +clients/rust/ + Cargo.toml + build.rs + src/ + tests/ + crates/mxgw-cli/ +``` + +`build.rs` reads the `.proto` files from +`../../src/MxGateway.Contracts/Protos` and generates `tonic`/`prost` bindings +into Cargo build output. `src/generated.rs` declares the Rust modules that +include those generated files. `src/generated` remains reserved for checked-in +generator output if the crate later changes to source-tree generation. + +## Build And Test + +Run the Rust workspace checks from `clients/rust`: + +```powershell +cargo fmt --all --check +cargo test --workspace +cargo check --workspace +``` + +The build script uses `protoc` from `PATH` or the Windows path recorded in +`../../docs/toolchain-links.md`. + +## CLI + +The scaffold CLI exposes version information: + +```powershell +cargo run -p mxgw-cli -- version --json +``` + +Additional commands are implemented with the client/session wrapper work. + +## Related Documentation + +- [Client Proto Generation](../../docs/client-proto-generation.md) +- [Rust Client Detailed Design](../../docs/clients-rust-design.md) +- [Rust Style Guide](../../docs/style-guides/RustStyleGuide.md) diff --git a/clients/rust/build.rs b/clients/rust/build.rs new file mode 100644 index 0000000..33d38fb --- /dev/null +++ b/clients/rust/build.rs @@ -0,0 +1,59 @@ +use std::env; +use std::error::Error; +use std::path::{Path, PathBuf}; + +fn main() -> Result<(), Box> { + configure_protoc(); + + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); + let repo_root = manifest_dir + .parent() + .and_then(Path::parent) + .ok_or("clients/rust must live two levels below the repository root")?; + let proto_root = repo_root.join("src/MxGateway.Contracts/Protos"); + let gateway_proto = proto_root.join("mxaccess_gateway.proto"); + let worker_proto = proto_root.join("mxaccess_worker.proto"); + let descriptor_path = PathBuf::from(env::var("OUT_DIR")?).join("mxaccessgw-client-v1.protoset"); + + println!("cargo:rerun-if-changed={}", gateway_proto.display()); + println!("cargo:rerun-if-changed={}", worker_proto.display()); + + tonic_build::configure() + .build_server(false) + .build_client(true) + .file_descriptor_set_path(descriptor_path) + .compile_protos( + &[gateway_proto.as_path(), worker_proto.as_path()], + &[proto_root.as_path()], + )?; + + Ok(()) +} + +fn configure_protoc() { + if env::var_os("PROTOC").is_some() { + return; + } + + for candidate in protoc_candidates() { + if candidate.is_file() { + env::set_var("PROTOC", candidate); + return; + } + } +} + +fn protoc_candidates() -> Vec { + let mut candidates = Vec::new(); + + if cfg!(windows) { + if let Some(local_app_data) = env::var_os("LOCALAPPDATA") { + candidates.push(PathBuf::from(local_app_data).join( + "Microsoft/WinGet/Packages/Google.Protobuf_Microsoft.Winget.Source_8wekyb3d8bbwe/bin/protoc.exe", + )); + } + } + + candidates.push(PathBuf::from("protoc")); + candidates +} diff --git a/clients/rust/crates/mxgw-cli/Cargo.toml b/clients/rust/crates/mxgw-cli/Cargo.toml new file mode 100644 index 0000000..5691ac7 --- /dev/null +++ b/clients/rust/crates/mxgw-cli/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "mxgw-cli" +version.workspace = true +edition.workspace = true +publish.workspace = true + +[[bin]] +name = "mxgw" +path = "src/main.rs" + +[dependencies] +clap = { workspace = true } +mxgateway-client = { path = "../.." } +serde_json = { workspace = true } diff --git a/clients/rust/crates/mxgw-cli/src/main.rs b/clients/rust/crates/mxgw-cli/src/main.rs new file mode 100644 index 0000000..4accfdf --- /dev/null +++ b/clients/rust/crates/mxgw-cli/src/main.rs @@ -0,0 +1,64 @@ +use std::process::ExitCode; + +use clap::{Parser, Subcommand}; +use mxgateway_client::{CLIENT_VERSION, GATEWAY_PROTOCOL_VERSION, WORKER_PROTOCOL_VERSION}; +use serde_json::json; + +#[derive(Debug, Parser)] +#[command(name = "mxgw")] +#[command(about = "MXAccess Gateway Rust test CLI")] +struct Cli { + #[command(subcommand)] + command: Command, +} + +#[derive(Debug, Subcommand)] +enum Command { + Version { + #[arg(long)] + json: bool, + }, +} + +fn main() -> ExitCode { + let cli = Cli::parse(); + run(cli); + ExitCode::SUCCESS +} + +fn run(cli: Cli) { + match cli.command { + Command::Version { json } => print_version(json), + } +} + +fn print_version(use_json: bool) { + if use_json { + println!( + "{}", + json!({ + "clientVersion": CLIENT_VERSION, + "gatewayProtocolVersion": GATEWAY_PROTOCOL_VERSION, + "workerProtocolVersion": WORKER_PROTOCOL_VERSION, + }) + ); + return; + } + + println!("mxgw {CLIENT_VERSION}"); + println!("gateway protocol {GATEWAY_PROTOCOL_VERSION}"); + println!("worker protocol {WORKER_PROTOCOL_VERSION}"); +} + +#[cfg(test)] +mod tests { + use clap::Parser; + + use super::Cli; + + #[test] + fn parses_version_json_command() { + let parsed = Cli::try_parse_from(["mxgw", "version", "--json"]); + assert!(parsed.is_ok()); + } +} diff --git a/clients/rust/src/auth.rs b/clients/rust/src/auth.rs new file mode 100644 index 0000000..6af8123 --- /dev/null +++ b/clients/rust/src/auth.rs @@ -0,0 +1,30 @@ +use std::fmt; + +/// API key wrapper that avoids exposing raw credentials in formatted output. +#[derive(Clone, Eq, PartialEq)] +pub struct ApiKey(String); + +impl ApiKey { + pub fn new(value: impl Into) -> Self { + Self(value.into()) + } + + pub fn expose_secret(&self) -> &str { + &self.0 + } +} + +impl fmt::Debug for ApiKey { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_tuple("ApiKey") + .field(&"") + .finish() + } +} + +impl fmt::Display for ApiKey { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("") + } +} diff --git a/clients/rust/src/client.rs b/clients/rust/src/client.rs new file mode 100644 index 0000000..d09f929 --- /dev/null +++ b/clients/rust/src/client.rs @@ -0,0 +1,30 @@ +use tonic::transport::Channel; + +use crate::error::Error; +use crate::generated::mxaccess_gateway::v1::mx_access_gateway_client::MxAccessGatewayClient; +use crate::options::ClientOptions; + +/// Thin owner for the generated gateway client. +pub struct GatewayClient { + inner: MxAccessGatewayClient, +} + +impl GatewayClient { + pub async fn connect(options: ClientOptions) -> Result { + let endpoint = Channel::from_shared(options.endpoint().to_owned()).map_err(|source| { + Error::InvalidEndpoint { + endpoint: options.endpoint().to_owned(), + detail: source.to_string(), + } + })?; + let channel = endpoint.connect().await?; + + Ok(Self { + inner: MxAccessGatewayClient::new(channel), + }) + } + + pub fn into_inner(self) -> MxAccessGatewayClient { + self.inner + } +} diff --git a/clients/rust/src/error.rs b/clients/rust/src/error.rs new file mode 100644 index 0000000..3aa1f2a --- /dev/null +++ b/clients/rust/src/error.rs @@ -0,0 +1,13 @@ +use thiserror::Error as ThisError; + +#[derive(Debug, ThisError)] +pub enum Error { + #[error("invalid gateway endpoint `{endpoint}`: {detail}")] + InvalidEndpoint { endpoint: String, detail: String }, + + #[error("gateway transport error: {0}")] + Transport(#[from] tonic::transport::Error), + + #[error("gateway status error: {0}")] + Status(#[from] tonic::Status), +} diff --git a/clients/rust/src/generated.rs b/clients/rust/src/generated.rs new file mode 100644 index 0000000..6351f7c --- /dev/null +++ b/clients/rust/src/generated.rs @@ -0,0 +1,15 @@ +pub mod mxaccess_gateway { + pub mod v1 { + #![allow(clippy::large_enum_variant)] + + tonic::include_proto!("mxaccess_gateway.v1"); + } +} + +pub mod mxaccess_worker { + pub mod v1 { + #![allow(clippy::large_enum_variant)] + + tonic::include_proto!("mxaccess_worker.v1"); + } +} diff --git a/clients/rust/src/lib.rs b/clients/rust/src/lib.rs new file mode 100644 index 0000000..aac1c91 --- /dev/null +++ b/clients/rust/src/lib.rs @@ -0,0 +1,21 @@ +//! Rust client scaffold for MXAccess Gateway. +//! +//! The crate compiles generated `tonic` bindings from the shared gateway +//! protobuf contracts and exposes a small handwritten surface for future client +//! implementation work. + +pub mod auth; +pub mod client; +pub mod error; +pub mod generated; +pub mod options; +pub mod session; +pub mod value; +pub mod version; + +pub use auth::ApiKey; +pub use client::GatewayClient; +pub use error::Error; +pub use options::ClientOptions; +pub use session::Session; +pub use version::{CLIENT_VERSION, GATEWAY_PROTOCOL_VERSION, WORKER_PROTOCOL_VERSION}; diff --git a/clients/rust/src/options.rs b/clients/rust/src/options.rs new file mode 100644 index 0000000..38013ff --- /dev/null +++ b/clients/rust/src/options.rs @@ -0,0 +1,54 @@ +use std::fmt; + +use crate::auth::ApiKey; + +#[derive(Clone)] +pub struct ClientOptions { + endpoint: String, + api_key: Option, + plaintext: bool, +} + +impl ClientOptions { + pub fn new(endpoint: impl Into) -> Self { + Self { + endpoint: endpoint.into(), + api_key: None, + plaintext: true, + } + } + + pub fn with_api_key(mut self, api_key: ApiKey) -> Self { + self.api_key = Some(api_key); + self + } + + pub fn endpoint(&self) -> &str { + &self.endpoint + } + + pub fn api_key(&self) -> Option<&ApiKey> { + self.api_key.as_ref() + } + + pub fn plaintext(&self) -> bool { + self.plaintext + } +} + +impl Default for ClientOptions { + fn default() -> Self { + Self::new("http://127.0.0.1:5000") + } +} + +impl fmt::Debug for ClientOptions { + fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter + .debug_struct("ClientOptions") + .field("endpoint", &self.endpoint) + .field("api_key", &self.api_key.as_ref().map(|_| "")) + .field("plaintext", &self.plaintext) + .finish() + } +} diff --git a/clients/rust/src/session.rs b/clients/rust/src/session.rs new file mode 100644 index 0000000..a421480 --- /dev/null +++ b/clients/rust/src/session.rs @@ -0,0 +1,15 @@ +/// Session identifier returned by the gateway. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Session { + id: String, +} + +impl Session { + pub fn new(id: impl Into) -> Self { + Self { id: id.into() } + } + + pub fn id(&self) -> &str { + &self.id + } +} diff --git a/clients/rust/src/value.rs b/clients/rust/src/value.rs new file mode 100644 index 0000000..754e64d --- /dev/null +++ b/clients/rust/src/value.rs @@ -0,0 +1,9 @@ +use crate::generated::mxaccess_gateway::v1::MxValue; + +pub fn int32_value(value: i32) -> MxValue { + MxValue { + data_type: crate::generated::mxaccess_gateway::v1::MxDataType::Integer as i32, + kind: Some(crate::generated::mxaccess_gateway::v1::mx_value::Kind::Int32Value(value)), + ..MxValue::default() + } +} diff --git a/clients/rust/src/version.rs b/clients/rust/src/version.rs new file mode 100644 index 0000000..aee4d39 --- /dev/null +++ b/clients/rust/src/version.rs @@ -0,0 +1,3 @@ +pub const CLIENT_VERSION: &str = "0.1.0-dev"; +pub const GATEWAY_PROTOCOL_VERSION: u32 = 1; +pub const WORKER_PROTOCOL_VERSION: u32 = 1; diff --git a/clients/rust/tests/proto_fixtures.rs b/clients/rust/tests/proto_fixtures.rs new file mode 100644 index 0000000..8b2a16c --- /dev/null +++ b/clients/rust/tests/proto_fixtures.rs @@ -0,0 +1,144 @@ +use std::fs; +use std::path::PathBuf; + +use mxgateway_client::generated::mxaccess_gateway::v1::{ + mx_command, mx_value, MxCommand, MxCommandKind, MxCommandRequest, MxDataType, MxEvent, + MxEventFamily, MxValue, OpenSessionReply, ProtocolStatusCode, RegisterCommand, +}; +use mxgateway_client::{GATEWAY_PROTOCOL_VERSION, WORKER_PROTOCOL_VERSION}; +use serde_json::Value; + +#[test] +fn generated_golden_fixtures_are_available() { + for fixture_name in [ + "open-session-reply.ok.json", + "register-command-request.json", + "on-data-change-event.json", + ] { + let fixture = read_fixture(fixture_name); + assert!( + fixture.is_object(), + "{fixture_name} must remain a protobuf JSON object" + ); + } +} + +#[test] +fn open_session_fixture_matches_protocol_versions() { + let fixture = read_fixture("open-session-reply.ok.json"); + let reply = OpenSessionReply { + session_id: string_field(&fixture, "sessionId"), + backend_name: string_field(&fixture, "backendName"), + worker_process_id: i32_field(&fixture, "workerProcessId"), + worker_protocol_version: u32_field(&fixture, "workerProtocolVersion"), + gateway_protocol_version: u32_field(&fixture, "gatewayProtocolVersion"), + protocol_status: Some( + mxgateway_client::generated::mxaccess_gateway::v1::ProtocolStatus { + code: ProtocolStatusCode::Ok as i32, + message: string_field(&fixture["protocolStatus"], "message"), + }, + ), + ..OpenSessionReply::default() + }; + + assert_eq!(reply.gateway_protocol_version, GATEWAY_PROTOCOL_VERSION); + assert_eq!(reply.worker_protocol_version, WORKER_PROTOCOL_VERSION); +} + +#[test] +fn register_fixture_can_build_generated_request() { + let fixture = read_fixture("register-command-request.json"); + let command = &fixture["command"]; + let request = MxCommandRequest { + session_id: string_field(&fixture, "sessionId"), + client_correlation_id: string_field(&fixture, "clientCorrelationId"), + command: Some(MxCommand { + kind: MxCommandKind::Register as i32, + payload: Some(mx_command::Payload::Register(RegisterCommand { + client_name: string_field(&command["register"], "clientName"), + })), + }), + }; + + assert_eq!(request.session_id, "session-fixture"); + assert_eq!( + request.command.unwrap().kind, + MxCommandKind::Register as i32 + ); +} + +#[test] +fn on_data_change_fixture_can_build_generated_event() { + let fixture = read_fixture("on-data-change-event.json"); + let event = MxEvent { + family: MxEventFamily::OnDataChange as i32, + session_id: string_field(&fixture, "sessionId"), + server_handle: i32_field(&fixture, "serverHandle"), + item_handle: i32_field(&fixture, "itemHandle"), + value: Some(MxValue { + data_type: MxDataType::Integer as i32, + variant_type: string_field(&fixture["value"], "variantType"), + kind: Some(mx_value::Kind::Int32Value(i32_field( + &fixture["value"], + "int32Value", + ))), + ..MxValue::default() + }), + quality: i32_field(&fixture, "quality"), + worker_sequence: u64_field(&fixture, "workerSequence"), + ..MxEvent::default() + }; + + assert_eq!(event.family, MxEventFamily::OnDataChange as i32); + assert_eq!(event.value.unwrap().data_type, MxDataType::Integer as i32); +} + +fn read_fixture(name: &str) -> Value { + let path = fixture_root().join(name); + let data = fs::read_to_string(&path).unwrap_or_else(|error| { + panic!("failed to read fixture {}: {error}", path.display()); + }); + + serde_json::from_str(&data).unwrap_or_else(|error| { + panic!("failed to parse fixture {}: {error}", path.display()); + }) +} + +fn fixture_root() -> PathBuf { + PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../proto/fixtures/golden") +} + +fn string_field(value: &Value, name: &str) -> String { + value[name] + .as_str() + .unwrap_or_else(|| panic!("missing string field {name}")) + .to_owned() +} + +fn i32_field(value: &Value, name: &str) -> i32 { + value[name] + .as_i64() + .unwrap_or_else(|| panic!("missing i32 field {name}")) + .try_into() + .unwrap_or_else(|_| panic!("field {name} does not fit in i32")) +} + +fn u32_field(value: &Value, name: &str) -> u32 { + value[name] + .as_u64() + .unwrap_or_else(|| panic!("missing u32 field {name}")) + .try_into() + .unwrap_or_else(|_| panic!("field {name} does not fit in u32")) +} + +fn u64_field(value: &Value, name: &str) -> u64 { + if let Some(number) = value[name].as_u64() { + return number; + } + + value[name] + .as_str() + .unwrap_or_else(|| panic!("missing u64 field {name}")) + .parse() + .unwrap_or_else(|_| panic!("field {name} does not parse as u64")) +} diff --git a/docs/client-proto-generation.md b/docs/client-proto-generation.md index c9276c4..0345a91 100644 --- a/docs/client-proto-generation.md +++ b/docs/client-proto-generation.md @@ -111,10 +111,21 @@ The script maps both proto files into the internal Go package the source `.proto` files do not carry Go-specific `go_package` options. This keeps language-specific packaging outside the public contract files. -Rust clients should use `tonic-build` or the selected protobuf generator from -the Rust client build script, with generated modules placed under -`clients/rust/src/generated` or included from the build output according to the -client crate design. +Rust clients use `tonic-build` from `clients/rust/build.rs`. The build script +reads the shared `.proto` files and emits generated `tonic`/`prost` modules +into Cargo build output. `clients/rust/src/generated.rs` contains the module +declarations that include those generated files. `clients/rust/src/generated` +remains reserved for checked-in generator output if the crate later changes to +source-tree generation, and handwritten wrapper code stays outside that +directory. + +Run the Rust workspace checks from `clients/rust`: + +```powershell +cargo fmt --all --check +cargo test --workspace +cargo check --workspace +``` Python clients should use `grpc_tools.protoc` and write generated modules under `clients/python/src/mxgateway/generated` so imports stay separate from