From 2453d758bef7cb0996e358b222a274ed02e0bc77 Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Wed, 27 Sep 2023 10:34:34 +0200 Subject: [PATCH 01/14] Add rust lib to kuksa_databroker --- Cargo.lock | 925 ++++++++++------------- Cargo.toml | 3 + kuksa_databroker/lib/Cargo.toml | 37 + kuksa_databroker/lib/common.rs | 207 +++++ kuksa_databroker/lib/kuksa/Cargo.toml | 38 + kuksa_databroker/lib/kuksa/src/client.rs | 305 ++++++++ kuksa_databroker/lib/sdv/Cargo.toml | 38 + kuksa_databroker/lib/sdv/src/client.rs | 116 +++ 8 files changed, 1134 insertions(+), 535 deletions(-) create mode 100644 kuksa_databroker/lib/Cargo.toml create mode 100644 kuksa_databroker/lib/common.rs create mode 100644 kuksa_databroker/lib/kuksa/Cargo.toml create mode 100644 kuksa_databroker/lib/kuksa/src/client.rs create mode 100644 kuksa_databroker/lib/sdv/Cargo.toml create mode 100644 kuksa_databroker/lib/sdv/src/client.rs diff --git a/Cargo.lock b/Cargo.lock index ae4a08754..11d75fd7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "addr2line" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4fa78e18c64fce05e902adecd7a5eed15a5e0a3439f7b0e169f0252214865e3" +checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" dependencies = [ "gimli", ] @@ -17,23 +17,11 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "getrandom 0.2.10", - "once_cell", - "version_check", -] - [[package]] name = "aho-corasick" -version = "1.0.2" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" dependencies = [ "memchr", ] @@ -49,24 +37,23 @@ dependencies = [ [[package]] name = "anstream" -version = "0.3.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" +checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", - "is-terminal", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" +checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" [[package]] name = "anstyle-parse" @@ -88,9 +75,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -version = "1.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" +checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -98,9 +85,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.72" +version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" [[package]] name = "arc-swap" @@ -139,18 +126,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] name = "async-trait" -version = "0.1.72" +version = "0.1.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" +checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] @@ -181,9 +168,9 @@ dependencies = [ [[package]] name = "axum" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a1de45611fdb535bfde7b7de4fd54f4fd2b17b1737c0a59b69bf9b92074b8c" +checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", "axum-core", @@ -226,9 +213,9 @@ dependencies = [ [[package]] name = "backtrace" -version = "0.3.68" +version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4319208da049c43661739c5fade2ba182f09d1dc2299b32298d3a31692b17e12" +checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" dependencies = [ "addr2line", "cc", @@ -247,9 +234,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -version = "0.21.2" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "bitflags" @@ -259,9 +246,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" [[package]] name = "blake2b_simd" @@ -276,12 +263,12 @@ dependencies = [ [[package]] name = "bstr" -version = "1.6.0" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6798148dccfbff0fae41c7574d2fa8f1ef3492fba0face179de5d8d447d67b05" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" dependencies = [ "memchr", - "regex-automata 0.3.3", + "regex-automata 0.3.8", "serde", ] @@ -296,9 +283,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.13.0" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" +checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" [[package]] name = "bytecount" @@ -308,15 +295,18 @@ checksum = "2c676a478f63e9fa2dd5368a42f28bba0d6c560b775f38583c8bbaa7fcd67c9c" [[package]] name = "bytes" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" +checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" [[package]] name = "cc" -version = "1.0.79" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] [[package]] name = "cfg-if" @@ -326,20 +316,19 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.3.19" +version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" +checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" dependencies = [ "clap_builder", "clap_derive", - "once_cell", ] [[package]] name = "clap_builder" -version = "4.3.19" +version = "4.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" +checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" dependencies = [ "anstream", "anstyle", @@ -350,21 +339,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.3.12" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] name = "clap_lex" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "clru" @@ -378,6 +367,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "common" +version = "0.1.0" +dependencies = [ + "databroker-proto", + "http", + "tokio", + "tokio-stream", + "tonic", +] + [[package]] name = "console" version = "0.15.7" @@ -508,12 +508,15 @@ version = "0.4.0" dependencies = [ "ansi_term", "clap", + "common", "databroker-proto", "http", + "kuksa", "linefeed", "prost", "prost-types", "regex", + "sdv", "tokio", "tokio-stream", "tonic", @@ -542,6 +545,12 @@ dependencies = [ "tonic-build", ] +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + [[package]] name = "derive_more" version = "0.99.17" @@ -608,11 +617,17 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[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.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" dependencies = [ "errno-dragonfly", "libc", @@ -629,6 +644,15 @@ dependencies = [ "libc", ] +[[package]] +name = "faster-hex" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a" +dependencies = [ + "serde", +] + [[package]] name = "fastrand" version = "2.0.0" @@ -637,13 +661,13 @@ checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "filetime" -version = "0.2.21" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cbc844cecaee9d4443931972e1289c8ff485cb4cc2767cb03ca139ed6885153" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.2.16", + "redox_syscall 0.3.5", "windows-sys 0.48.0", ] @@ -655,9 +679,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -version = "1.0.26" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" dependencies = [ "crc32fast", "miniz_oxide", @@ -734,7 +758,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] @@ -808,42 +832,38 @@ dependencies = [ [[package]] name = "gimli" -version = "0.27.3" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" [[package]] name = "gix" -version = "0.48.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1e74cea676de7f53a79f3c0365812b11f6814b81e671b8ee4abae6ca09c7881" +checksum = "06a8c9f9452078f474fecd2880de84819b8c77224ab62273275b646bf785f906" dependencies = [ "gix-actor", - "gix-attributes", "gix-commitgraph", "gix-config", - "gix-credentials", "gix-date", "gix-diff", "gix-discover", - "gix-features 0.31.1", - "gix-fs 0.3.0", + "gix-features", + "gix-fs", "gix-glob", "gix-hash", "gix-hashtable", - "gix-ignore", "gix-index", "gix-lock", - "gix-mailmap", - "gix-negotiate", + "gix-macros", "gix-object", "gix-odb", "gix-pack", "gix-path", - "gix-prompt", "gix-ref", "gix-refspec", "gix-revision", + "gix-revwalk", "gix-sec", "gix-tempfile", "gix-trace", @@ -851,9 +871,8 @@ dependencies = [ "gix-url", "gix-utils", "gix-validate", - "gix-worktree", - "log", "once_cell", + "parking_lot", "signal-hook", "smallvec", "thiserror", @@ -862,40 +881,23 @@ dependencies = [ [[package]] name = "gix-actor" -version = "0.23.0" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1969b77b9ee4cc1755c841987ec6f7622aaca95e952bcafb76973ae59d1b8716" +checksum = "8e8c6778cc03bca978b2575a03e04e5ba6f430a9dd9b0f1259f0a8a9a5e5cc66" dependencies = [ "bstr", "btoi", "gix-date", "itoa", - "nom", "thiserror", -] - -[[package]] -name = "gix-attributes" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3772b0129dcd1fc73e985bbd08a1482d082097d2915cb1ee31ce8092b8e4434" -dependencies = [ - "bstr", - "gix-glob", - "gix-path", - "gix-quote", - "kstring", - "log", - "smallvec", - "thiserror", - "unicode-bom", + "winnow", ] [[package]] name = "gix-bitmap" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa8bbde7551a9e3e783a2871f53bbb0f50aac7a77db5680c8709f69e8ce724f" +checksum = "0ccab4bc576844ddb51b78d81b4a42d73e6229660fa614dfc3d3999c874d1959" dependencies = [ "thiserror", ] @@ -909,24 +911,15 @@ dependencies = [ "thiserror", ] -[[package]] -name = "gix-command" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2783ad148fb16bf9cfd46423706ba552a62a4d4a18fda5dd07648eb0228862dd" -dependencies = [ - "bstr", -] - [[package]] name = "gix-commitgraph" -version = "0.17.1" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed42baa50075d41c1a0931074ce1a97c5797c7c6fe7591d9f1f2dcd448532c26" +checksum = "4676ede3a7d37e7028e2889830349a6aca22efc1d2f2dd9fa3351c1a8ddb0c6a" dependencies = [ "bstr", "gix-chunk", - "gix-features 0.31.1", + "gix-features", "gix-hash", "memmap2", "thiserror", @@ -934,60 +927,43 @@ dependencies = [ [[package]] name = "gix-config" -version = "0.25.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "817688c7005a716d9363e267913526adea402dabd947f4ba63842d10cc5132af" +checksum = "1108c4ac88248dd25cc8ab0d0dae796e619fb72d92f88e30e00b29d61bb93cc4" dependencies = [ "bstr", "gix-config-value", - "gix-features 0.31.1", + "gix-features", "gix-glob", "gix-path", "gix-ref", "gix-sec", - "log", "memchr", - "nom", "once_cell", "smallvec", "thiserror", "unicode-bom", + "winnow", ] [[package]] name = "gix-config-value" -version = "0.12.5" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e874f41437441c02991dcea76990b9058fadfc54b02ab4dd06ab2218af43897" +checksum = "ea7505b97f4d8e7933e29735a568ba2f86d8de466669d9f0e8321384f9972f47" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "bstr", "gix-path", "libc", "thiserror", ] -[[package]] -name = "gix-credentials" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a75565e0e6e7f80cfa4eb1b05cc448c6846ddd48dcf413a28875fbc11ee9af" -dependencies = [ - "bstr", - "gix-command", - "gix-config-value", - "gix-path", - "gix-prompt", - "gix-sec", - "gix-url", - "thiserror", -] - [[package]] name = "gix-date" -version = "0.7.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56b0312dba1ad003d9b8c502bed52fbcf106f8de3a9a26bfa7b45642a6f94b72" +checksum = "fc7df669639582dc7c02737642f76890b03b5544e141caba68a7d6b4eb551e0d" dependencies = [ "bstr", "itoa", @@ -997,21 +973,20 @@ dependencies = [ [[package]] name = "gix-diff" -version = "0.32.0" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aaf5d9b9b521b284ebe53ee69eee33341835ec70edc314f36b2100ea81396121" +checksum = "b45e342d148373bd9070d557e6fb1280aeae29a3e05e32506682d027278501eb" dependencies = [ "gix-hash", "gix-object", - "imara-diff", "thiserror", ] [[package]] name = "gix-discover" -version = "0.21.1" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "272aad20dc63dedba76615373dd8885fb5aebe4795e5b5b0aa2a24e63c82085c" +checksum = "da4cacda5ee9dd1b38b0e2506834e40e66c08cf050ef55c344334c76745f277b" dependencies = [ "bstr", "dunce", @@ -1024,9 +999,9 @@ dependencies = [ [[package]] name = "gix-features" -version = "0.31.1" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06142d8cff5d17509399b04052b64d2f9b3a311d5cff0b1a32b220f62cd0d595" +checksum = "f414c99e1a7abc69b21f3225a6539d203b0513f1d1d448607c4ea81cdcf9ee59" dependencies = [ "crc32fast", "flate2", @@ -1040,92 +1015,61 @@ dependencies = [ "walkdir", ] -[[package]] -name = "gix-features" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "882695cccf38da4c3cc7ee687bdb412cf25e37932d7f8f2c306112ea712449f1" -dependencies = [ - "gix-hash", - "gix-trace", - "libc", -] - -[[package]] -name = "gix-fs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb15956bc0256594c62a2399fcf6958a02a11724217eddfdc2b49b21b6292496" -dependencies = [ - "gix-features 0.31.1", -] - [[package]] name = "gix-fs" -version = "0.4.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d5b6e9d34a2c61ea4a02bbca94c409ab6dbbca1348cbb67298cd7fed8758761" +checksum = "404795da3d4c660c9ab6c3b2ad76d459636d1e1e4b37b0c7ff68eee898c298d4" dependencies = [ - "gix-features 0.32.1", + "gix-features", ] [[package]] name = "gix-glob" -version = "0.9.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c18bdff83143d61e7d60da6183b87542a870d026b2a2d0b30170b8e9c0cd321a" +checksum = "e3ac79c444193b0660fe0c0925d338bd338bd643e32138784dccfb12c628b892" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "bstr", - "gix-features 0.31.1", + "gix-features", "gix-path", ] [[package]] name = "gix-hash" -version = "0.11.4" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b422ff2ad9a0628baaad6da468cf05385bf3f5ab495ad5a33cce99b9f41092f" +checksum = "2ccf425543779cddaa4a7c62aba3fa9d90ea135b160be0a72dd93c063121ad4a" dependencies = [ - "hex", + "faster-hex", "thiserror", ] [[package]] name = "gix-hashtable" -version = "0.2.4" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385f4ce6ecf3692d313ca3aa9bd3b3d8490de53368d6d94bedff3af8b6d9c58d" +checksum = "409268480841ad008e81c17ca5a293393fbf9f2b6c2f85b8ab9de1f0c5176a16" dependencies = [ "gix-hash", "hashbrown 0.14.0", "parking_lot", ] -[[package]] -name = "gix-ignore" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca801f2d0535210f77b33e2c067d565aedecacc82f1b3dbce26da1388ebc4634" -dependencies = [ - "bstr", - "gix-glob", - "gix-path", - "unicode-bom", -] - [[package]] name = "gix-index" -version = "0.20.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68099abdf6ee50ae3c897e8b05de96871cbe54d52a37cdf559101f911b883562" +checksum = "0e9599fc30b3d6aad231687a403f85dfa36ae37ccf1b68ee1f621ad5b7fc7a0d" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "bstr", "btoi", "filetime", "gix-bitmap", - "gix-features 0.31.1", + "gix-features", + "gix-fs", "gix-hash", "gix-lock", "gix-object", @@ -1138,9 +1082,9 @@ dependencies = [ [[package]] name = "gix-lock" -version = "7.0.2" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e82ec23c8a281f91044bf3ed126063b91b59f9c9340bf0ae746f385cc85a6fa" +checksum = "1568c3d90594c60d52670f325f5db88c2d572e85c8dd45fabc23d91cadb0fd52" dependencies = [ "gix-tempfile", "gix-utils", @@ -1148,62 +1092,44 @@ dependencies = [ ] [[package]] -name = "gix-mailmap" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1787e3c37fc43b1f7c0e3be6196c6837b3ba5f869190dfeaa444b816f0a7f34b" -dependencies = [ - "bstr", - "gix-actor", - "gix-date", - "thiserror", -] - -[[package]] -name = "gix-negotiate" -version = "0.4.0" +name = "gix-macros" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7bce64d4452dd609f44d04b14b29da2e0ad2c45fcdf4ce1472a5f5f8ec21c2" +checksum = "9d8acb5ee668d55f0f2d19a320a3f9ef67a6999ad483e11135abcc2464ed18b6" dependencies = [ - "bitflags 2.3.3", - "gix-commitgraph", - "gix-date", - "gix-hash", - "gix-object", - "gix-revwalk", - "smallvec", - "thiserror", + "proc-macro2", + "quote", + "syn 2.0.37", ] [[package]] name = "gix-object" -version = "0.32.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a953f3d7ffad16734aa3ab1d05807972c80e339d1bd9dde03e0198716b99e2a6" +checksum = "3e5528d5b2c984044d547e696e44a8c45fa122e83cd8c2ac1da69bd474336be8" dependencies = [ "bstr", "btoi", "gix-actor", "gix-date", - "gix-features 0.31.1", + "gix-features", "gix-hash", "gix-validate", - "hex", "itoa", - "nom", "smallvec", "thiserror", + "winnow", ] [[package]] name = "gix-odb" -version = "0.49.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6418cff00ecc2713b58c8e04bff30dda808fbba1a080e7248b299d069894a01" +checksum = "d0446eca295459deb3d6dd6ed7d44a631479f1b7381d8087166605c7a9f717c6" dependencies = [ "arc-swap", "gix-date", - "gix-features 0.31.1", + "gix-features", "gix-hash", "gix-object", "gix-pack", @@ -1216,20 +1142,18 @@ dependencies = [ [[package]] name = "gix-pack" -version = "0.39.1" +version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "414935138d90043ea5898de7a93f02c2558e52652492719470e203ef26a8fd0a" +checksum = "be19ee650300d7cbac5829b637685ec44a8d921a7c2eaff8a245d8f2f008870c" dependencies = [ "clru", "gix-chunk", - "gix-diff", - "gix-features 0.31.1", + "gix-features", "gix-hash", "gix-hashtable", "gix-object", "gix-path", "gix-tempfile", - "gix-traverse", "memmap2", "parking_lot", "smallvec", @@ -1238,9 +1162,9 @@ dependencies = [ [[package]] name = "gix-path" -version = "0.8.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18609c8cbec8508ea97c64938c33cd305b75dfc04a78d0c3b78b8b3fd618a77c" +checksum = "6a1d370115171e3ae03c5c6d4f7d096f2981a40ddccb98dfd704c773530ba73b" dependencies = [ "bstr", "gix-trace", @@ -1249,24 +1173,11 @@ dependencies = [ "thiserror", ] -[[package]] -name = "gix-prompt" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f755e8eb83ee9a06642a8fbd3009b033db2b5bd774f3aaf3de0b07f9b6ebdc5" -dependencies = [ - "gix-command", - "gix-config-value", - "parking_lot", - "rustix 0.38.4", - "thiserror", -] - [[package]] name = "gix-quote" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfd80d3d0c733508df9449b1d3795da36083807e31d851d7d61d29af13bd4b0a" +checksum = "475c86a97dd0127ba4465fbb239abac9ea10e68301470c9791a6dd5351cdc905" dependencies = [ "bstr", "btoi", @@ -1275,14 +1186,14 @@ dependencies = [ [[package]] name = "gix-ref" -version = "0.32.1" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39453f4e5f23cddc2e6e4cca2ba20adfdbec29379e3ca829714dfe98ae068ccd" +checksum = "3cccbfa8d5cd9b86465f27a521e0c017de54b92d9fd37c143e49c658a2f04f3a" dependencies = [ "gix-actor", "gix-date", - "gix-features 0.31.1", - "gix-fs 0.3.0", + "gix-features", + "gix-fs", "gix-hash", "gix-lock", "gix-object", @@ -1290,15 +1201,15 @@ dependencies = [ "gix-tempfile", "gix-validate", "memmap2", - "nom", "thiserror", + "winnow", ] [[package]] name = "gix-refspec" -version = "0.13.0" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e76ff1f82fba295a121e31ab02f69642994e532c45c0c899aa393f4b740302" +checksum = "678ba30d95baa5462df9875628ed40655d5f5b8aba7028de86ed57f36e762c6c" dependencies = [ "bstr", "gix-hash", @@ -1310,9 +1221,9 @@ dependencies = [ [[package]] name = "gix-revision" -version = "0.17.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "237428a7d3978e8572964e1e45d984027c2acc94df47e594baa6c4b0da7c9922" +checksum = "b3e80a5992ae446fe1745dd26523b86084e3f1b6b3e35377fe09b4f35ac8f151" dependencies = [ "bstr", "gix-date", @@ -1320,14 +1231,15 @@ dependencies = [ "gix-hashtable", "gix-object", "gix-revwalk", + "gix-trace", "thiserror", ] [[package]] name = "gix-revwalk" -version = "0.3.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "028d50fcaf8326a8f79a359490d9ca9fb4e2b51ac9ac86503560d0bcc888d2eb" +checksum = "b806349bc1f668e09035800e07ac8045da4e39a8925a245d93142c4802224ec1" dependencies = [ "gix-commitgraph", "gix-date", @@ -1340,11 +1252,11 @@ dependencies = [ [[package]] name = "gix-sec" -version = "0.8.4" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9615cbd6b456898aeb942cd75e5810c382fbfc48dbbff2fa23ebd2d33dcbe9c7" +checksum = "92b9542ac025a8c02ed5d17b3fc031a111a384e859d0be3532ec4d58c40a0f28" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "gix-path", "libc", "windows", @@ -1352,11 +1264,11 @@ dependencies = [ [[package]] name = "gix-tempfile" -version = "7.0.2" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa28d567848cec8fdd77d36ad4f5f78ecfaba7d78f647d4f63c8ae1a2cec7243" +checksum = "2762b91ff95e27ff3ea95758c0d4efacd7435a1be3629622928b8276de0f72a8" dependencies = [ - "gix-fs 0.4.1", + "gix-fs", "libc", "once_cell", "parking_lot", @@ -1373,9 +1285,9 @@ checksum = "96b6d623a1152c3facb79067d6e2ecdae48130030cf27d6eb21109f13bd7b836" [[package]] name = "gix-traverse" -version = "0.29.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3cdfd54598db4fae57d5ae6f52958422b2d13382d2745796bfe5c8015ffa86e" +checksum = "3ec6358f8373fb018af8fc96c9d2ec6a5b66999e2377dc40b7801351fec409ed" dependencies = [ "gix-commitgraph", "gix-date", @@ -1389,12 +1301,12 @@ dependencies = [ [[package]] name = "gix-url" -version = "0.20.1" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beaede6dbc83f408b19adfd95bb52f1dbf01fb8862c3faf6c6243e2e67fcdfa1" +checksum = "1c79d595b99a6c7ab274f3c991735a0c0f5a816a3da460f513c48edf1c7bf2cc" dependencies = [ "bstr", - "gix-features 0.31.1", + "gix-features", "gix-path", "home", "thiserror", @@ -1412,40 +1324,19 @@ dependencies = [ [[package]] name = "gix-validate" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba9b3737b2cef3dcd014633485f0034b0f1a931ee54aeb7d8f87f177f3c89040" -dependencies = [ - "bstr", - "thiserror", -] - -[[package]] -name = "gix-worktree" -version = "0.21.1" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1363b9aa66b9e14412ac04e1f759827203f491729d92172535a8ce6cde02efa" +checksum = "e05cab2b03a45b866156e052aa38619f4ece4adcb2f79978bfc249bc3b21b8c5" dependencies = [ "bstr", - "filetime", - "gix-attributes", - "gix-features 0.31.1", - "gix-fs 0.3.0", - "gix-glob", - "gix-hash", - "gix-ignore", - "gix-index", - "gix-object", - "gix-path", - "io-close", "thiserror", ] [[package]] name = "globset" -version = "0.4.12" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aca8bbd8e0707c1887a8bbb7e6b40e228f251ff5d62c8220a4a7a53c73aff006" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" dependencies = [ "aho-corasick", "bstr", @@ -1467,9 +1358,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.20" +version = "0.3.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97ec8491ebaf99c8eaa73058b045fe58073cd6be7f596ac993ced0b0a0c01049" +checksum = "91fc23aa11be92976ef4729127f1a74adf36d8436f7816b185d18df956790833" dependencies = [ "bytes", "fnv", @@ -1477,7 +1368,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -1522,15 +1413,9 @@ dependencies = [ [[package]] name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "hex" -version = "0.4.3" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" [[package]] name = "home" @@ -1571,9 +1456,9 @@ checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" [[package]] name = "httpdate" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "humantime" @@ -1598,7 +1483,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2", + "socket2 0.4.9", "tokio", "tower-service", "tracing", @@ -1645,23 +1530,23 @@ dependencies = [ ] [[package]] -name = "imara-diff" -version = "0.1.5" +name = "indexmap" +version = "1.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e98c1d0ad70fc91b8b9654b1f33db55e59579d3b3de2bffdced0fdb810570cb8" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ - "ahash", + "autocfg", "hashbrown 0.12.3", ] [[package]] name = "indexmap" -version = "1.9.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" dependencies = [ - "autocfg", - "hashbrown 0.12.3", + "equivalent", + "hashbrown 0.14.0", ] [[package]] @@ -1672,41 +1557,9 @@ checksum = "a257582fdcde896fd96463bf2d40eefea0580021c0712a0e2b028b60b47a837a" [[package]] name = "inventory" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a53088c87cf71c9d4f3372a2cb9eea1e7b8a0b1bf8b7f7d23fe5b76dbb07e63b" - -[[package]] -name = "io-close" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadcf447f06744f8ce713d2d6239bb5bde2c357a452397a9ed90c625da390bc" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.2", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi 0.3.2", - "rustix 0.38.4", - "windows-sys 0.48.0", -] +checksum = "e1be380c410bf0595e94992a648ea89db4dd3f3354ba54af206fd2a68cf5ac8e" [[package]] name = "itertools" @@ -1725,9 +1578,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "jemalloc-sys" -version = "0.5.3+5.3.0-patched" +version = "0.5.4+5.3.0-patched" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9bd5d616ea7ed58b571b2e209a65759664d7fb021a0819d7a790afc67e47ca1" +checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2" dependencies = [ "cc", "libc", @@ -1735,9 +1588,9 @@ dependencies = [ [[package]] name = "jemallocator" -version = "0.5.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16c2514137880c52b0b4822b563fadd38257c1f380858addb74a400889696ea6" +checksum = "a0de374a9f8e63150e6f5e8a60cc14c668226d7a347d8aee1a45766e3c4dd3bc" dependencies = [ "jemalloc-sys", "libc", @@ -1758,7 +1611,7 @@ version = "8.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", "pem", "ring", "serde", @@ -1767,12 +1620,15 @@ dependencies = [ ] [[package]] -name = "kstring" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" +name = "kuksa" +version = "0.1.0" dependencies = [ - "static_assertions", + "common", + "databroker-proto", + "http", + "tokio", + "tokio-stream", + "tonic", ] [[package]] @@ -1783,9 +1639,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.148" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" [[package]] name = "linefeed" @@ -1806,15 +1662,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.3" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" +checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" [[package]] name = "lock_api" @@ -1828,9 +1678,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.19" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "matchers" @@ -1843,15 +1693,15 @@ dependencies = [ [[package]] name = "matchit" -version = "0.7.0" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b87248edafb776e59e6ee64a79086f65890d3510f2c656c000bf2a7e8a0aea40" +checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" [[package]] name = "memmap2" @@ -1900,7 +1750,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c624fa1b7aab6bd2aff6e9b18565cc0363b6d45cbcd7465c9ed5e3740ebf097" dependencies = [ - "bitflags 2.3.3", + "bitflags 2.4.0", "libc", "nix", "smallstr", @@ -1918,14 +1768,13 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "nix" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "static_assertions", ] [[package]] @@ -1940,9 +1789,9 @@ dependencies = [ [[package]] name = "nom_locate" -version = "4.1.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e299bf5ea7b212e811e71174c5d1a5d065c4c0ad0c8691ecb1f97e3e66025e" +checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" dependencies = [ "bytecount", "memchr", @@ -1961,9 +1810,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" dependencies = [ "autocfg", "num-integer", @@ -1995,7 +1844,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi 0.3.2", + "hermit-abi 0.3.3", "libc", ] @@ -2010,9 +1859,9 @@ dependencies = [ [[package]] name = "object" -version = "0.31.1" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bda667d9f2b5051b8833f59f3bf748b28ef54f850f4fcb389a252aa383866d1" +checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" dependencies = [ "memchr", ] @@ -2049,7 +1898,7 @@ dependencies = [ "libc", "redox_syscall 0.3.5", "smallvec", - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -2096,12 +1945,12 @@ checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "petgraph" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dd7d28ee937e54fe3080c91faa1c3a46c06de6252988a7f4592ba2310ef22a4" +checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.0.0", ] [[package]] @@ -2144,29 +1993,29 @@ dependencies = [ [[package]] name = "pin-project" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030ad2bc4db10a8944cb0d837f158bdfec4d4a4873ab701a95046770d11f8842" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] name = "pin-project-lite" -version = "0.2.10" +version = "0.2.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c40d25201921e5ff0c862a505c6557ea88568a4e3ace775ab55e93f2f4f9d57" +checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" [[package]] name = "pin-utils" @@ -2192,18 +2041,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" dependencies = [ "unicode-ident", ] [[package]] name = "prodash" -version = "25.0.1" +version = "26.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c236e70b7f9b9ea00d33c69f63ec1ae6e9ae96118923cd37bd4e9c7396f0b107" +checksum = "794b5bf8e2d19b53dcdcec3e4bba628e20f5b6062503ba89281fa7037dd7bbcf" [[package]] name = "prost" @@ -2270,9 +2119,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.32" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -2355,14 +2204,14 @@ dependencies = [ [[package]] name = "regex" -version = "1.9.1" +version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" +checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.3.3", - "regex-syntax 0.7.4", + "regex-automata 0.3.8", + "regex-syntax 0.7.5", ] [[package]] @@ -2376,13 +2225,13 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.3.3" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39354c10dd07468c2e73926b23bb9c2caca74c5501e38a35da70406f1d923310" +checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.7.4", + "regex-syntax 0.7.5", ] [[package]] @@ -2393,9 +2242,9 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" [[package]] name = "regex-syntax" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "ring" @@ -2432,36 +2281,22 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.37.23" +version = "0.38.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d69718bf81c6127a49dc64e44a742e8bb9213c0ff8869a22c308f84c1d4ab06" +checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.0", "errno", - "io-lifetimes", "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", -] - -[[package]] -name = "rustix" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" -dependencies = [ - "bitflags 2.3.3", - "errno", - "libc", - "linux-raw-sys 0.4.3", + "linux-raw-sys", "windows-sys 0.48.0", ] [[package]] name = "rustls" -version = "0.21.5" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79ea77c539259495ce8ca47f53e66ae0330a8819f67e23ac96ca02f50e7b7d36" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ "log", "ring", @@ -2475,14 +2310,14 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ - "base64 0.21.2", + "base64 0.21.4", ] [[package]] name = "rustls-webpki" -version = "0.101.4" +version = "0.101.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d93931baf2d282fff8d3a532bbfd7653f734643161b87e3e01e59a04439bf0d" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" dependencies = [ "ring", "untrusted", @@ -2525,6 +2360,18 @@ dependencies = [ "untrusted", ] +[[package]] +name = "sdv" +version = "0.1.0" +dependencies = [ + "common", + "databroker-proto", + "http", + "tokio", + "tokio-stream", + "tonic", +] + [[package]] name = "sealed" version = "0.3.0" @@ -2551,29 +2398,29 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.176" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76dc28c9523c5d70816e393136b86d48909cfb27cecaa902d338c19ed47164dc" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.176" +version = "1.0.188" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e7b8c5dc823e3b90651ff1d3808419cd14e5ad76de04feaf37da114e7a306f" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] name = "serde_json" -version = "1.0.104" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" +checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" dependencies = [ "itoa", "ryu", @@ -2628,15 +2475,15 @@ dependencies = [ [[package]] name = "siphasher" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de" +checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "slab" -version = "0.4.8" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] @@ -2652,9 +2499,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" [[package]] name = "smart-default" @@ -2669,9 +2516,9 @@ dependencies = [ [[package]] name = "smawk" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" @@ -2683,6 +2530,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "socket2" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" +dependencies = [ + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "spin" version = "0.5.2" @@ -2698,12 +2555,6 @@ dependencies = [ "log", ] -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - [[package]] name = "strsim" version = "0.10.0" @@ -2723,9 +2574,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.27" +version = "2.0.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" +checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" dependencies = [ "proc-macro2", "quote", @@ -2773,24 +2624,24 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.7.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.3.5", - "rustix 0.38.4", + "rustix", "windows-sys 0.48.0", ] [[package]] name = "terminal_size" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e6bf6f19e9f8ed8d4048dc22981458ebcf406d67e94cd422e5ecd73d63b3237" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" dependencies = [ - "rustix 0.37.23", + "rustix", "windows-sys 0.48.0", ] @@ -2820,22 +2671,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.47" +version = "1.0.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] @@ -2850,10 +2701,11 @@ dependencies = [ [[package]] name = "time" -version = "0.3.23" +version = "0.3.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e399c068f43a5d116fedaf73b203fa4f9c519f17e2b34f63221d3792f81446" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" dependencies = [ + "deranged", "itoa", "libc", "num_threads", @@ -2864,15 +2716,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "time-macros" -version = "0.2.10" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96ba15a897f3c86766b757e5ac7221554c6750054d74d5b28844fce5fb36a6c4" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" dependencies = [ "time-core", ] @@ -2894,11 +2746,10 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.29.1" +version = "1.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "532826ff75199d5833b9d2c5fe410f29235e25704ee5f0ef599fb51c21f4a4da" +checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" dependencies = [ - "autocfg", "backtrace", "bytes", "libc", @@ -2906,7 +2757,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "signal-hook-registry", - "socket2", + "socket2 0.5.4", "tokio-macros", "windows-sys 0.48.0", ] @@ -2929,7 +2780,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] @@ -2956,9 +2807,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.8" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "806fe8c2c87eccc8b3267cbae29ed3ab2d0bd37fca70ab622e46aaa9375ddb7d" +checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" dependencies = [ "bytes", "futures-core", @@ -2977,7 +2828,7 @@ dependencies = [ "async-stream", "async-trait", "axum", - "base64 0.21.2", + "base64 0.21.4", "bytes", "futures-core", "futures-util", @@ -3020,7 +2871,7 @@ checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" dependencies = [ "futures-core", "futures-util", - "indexmap", + "indexmap 1.9.3", "pin-project", "pin-project-lite", "rand", @@ -3064,7 +2915,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", ] [[package]] @@ -3123,9 +2974,9 @@ checksum = "98e90c70c9f0d4d1ee6d0a7d04aa06cb9bbd53d8cfbdd62a0269a7c2eb640552" [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-linebreak" @@ -3150,9 +3001,9 @@ checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" [[package]] name = "unicode-width" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" [[package]] name = "untrusted" @@ -3162,9 +3013,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.4.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" dependencies = [ "form_urlencoded", "idna", @@ -3179,9 +3030,9 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "vergen" -version = "8.2.4" +version = "8.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbc5ad0d9d26b2c49a5ab7da76c3e79d3ee37e7821799f8223fcb8f2f391a2e7" +checksum = "85e7dc29b3c54a2ea67ef4f953d5ec0c4085035c0ae2d325be1c0d2144bd9f16" dependencies = [ "anyhow", "gix", @@ -3189,17 +3040,11 @@ dependencies = [ "time", ] -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - [[package]] name = "walkdir" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" dependencies = [ "same-file", "winapi-util", @@ -3247,7 +3092,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", "wasm-bindgen-shared", ] @@ -3269,7 +3114,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.27", + "syn 2.0.37", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3292,13 +3137,14 @@ dependencies = [ [[package]] name = "which" -version = "4.4.0" +version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", - "libc", + "home", "once_cell", + "rustix", ] [[package]] @@ -3319,9 +3165,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -3338,7 +3184,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -3356,7 +3202,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets 0.48.1", + "windows-targets 0.48.5", ] [[package]] @@ -3376,17 +3222,17 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.48.1" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", ] [[package]] @@ -3397,9 +3243,9 @@ checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_msvc" @@ -3409,9 +3255,9 @@ checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_i686_gnu" @@ -3421,9 +3267,9 @@ checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" [[package]] name = "windows_i686_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_msvc" @@ -3433,9 +3279,9 @@ checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" [[package]] name = "windows_i686_msvc" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_x86_64_gnu" @@ -3445,9 +3291,9 @@ checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" [[package]] name = "windows_x86_64_gnu" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnullvm" @@ -3457,9 +3303,9 @@ checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" [[package]] name = "windows_x86_64_gnullvm" -version = "0.48.0" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_msvc" @@ -3469,6 +3315,15 @@ checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] name = "windows_x86_64_msvc" -version = "0.48.0" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml index 51db3f1e5..0365a4766 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,9 @@ members = [ "kuksa_databroker/databroker-proto", "kuksa_databroker/databroker-cli", "kuksa_databroker/databroker-examples", + "kuksa_databroker/lib", + "kuksa_databroker/lib/kuksa", + "kuksa_databroker/lib/sdv", ] [workspace.dependencies] diff --git a/kuksa_databroker/lib/Cargo.toml b/kuksa_databroker/lib/Cargo.toml new file mode 100644 index 000000000..fa6c8e1c5 --- /dev/null +++ b/kuksa_databroker/lib/Cargo.toml @@ -0,0 +1,37 @@ +#******************************************************************************** +# Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License 2.0 which is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +#*******************************************************************************/ + +[package] +name = "common" +version = "0.1.0" +authors = ["Robert Bosch GmbH"] +edition = "2018" +license = "Apache-2.0" + +[dependencies] +databroker-proto = { workspace = true } +tonic = { workspace = true, features = ["transport", "channel"] } +tokio = { workspace = true, features = [ + "macros", +] } +tokio-stream = { workspace = true, features = ["sync"] } +http = "0.2.8" + +[lib] +name = "common" +crate-type = ["lib"] +path = "common.rs" + +[features] +default = ["tls"] +tls = ["tonic/tls"] \ No newline at end of file diff --git a/kuksa_databroker/lib/common.rs b/kuksa_databroker/lib/common.rs new file mode 100644 index 000000000..4bcc3e47c --- /dev/null +++ b/kuksa_databroker/lib/common.rs @@ -0,0 +1,207 @@ +use std::convert::TryFrom; + +use http::Uri; +use tokio_stream::wrappers::BroadcastStream; +use tonic::transport::Channel; + +#[derive(Debug)] +pub struct Client { + uri: Uri, + token: Option, + #[cfg(feature = "tls")] + tls_config: Option, + channel: Option, + connection_state_subs: Option>, +} + +#[derive(Clone)] +pub enum ConnectionState { + Connected, + Disconnected, +} + +#[derive(Debug)] +pub enum ClientError { + Connection(String), + Status(tonic::Status), +} + +impl std::error::Error for ClientError {} +impl std::fmt::Display for ClientError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ClientError::Connection(con) => f.pad(con), + ClientError::Status(status) => f.pad(&format!("{status}")), + } + } +} + +#[derive(Debug)] +pub enum TokenError { + MalformedTokenError(String), +} + +impl std::error::Error for TokenError {} +impl std::fmt::Display for TokenError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TokenError::MalformedTokenError(msg) => f.pad(msg), + } + } +} + +pub fn to_uri(uri: impl AsRef) -> Result { + let uri = uri + .as_ref() + .parse::() + .map_err(|err| format!("{err}"))?; + let mut parts = uri.into_parts(); + + if parts.scheme.is_none() { + parts.scheme = Some("http".parse().expect("http should be valid scheme")); + } + + match &parts.authority { + Some(_authority) => { + // match (authority.port_u16(), port) { + // (Some(uri_port), Some(port)) => { + // if uri_port != port { + // parts.authority = format!("{}:{}", authority.host(), port) + // .parse::() + // .map_err(|err| format!("{}", err)) + // .ok(); + // } + // } + // (_, _) => {} + // } + } + None => return Err("No server uri specified".to_owned()), + } + parts.path_and_query = Some("".parse().expect("uri path should be empty string")); + tonic::transport::Uri::from_parts(parts).map_err(|err| format!("{err}")) +} + +impl Client { + pub fn new(uri: Uri) -> Self { + Client { + uri, + token: None, + #[cfg(feature = "tls")] + tls_config: None, + channel: None, + connection_state_subs: None, + } + } + + pub fn get_uri(&self) -> String { + self.uri.to_string() + } + + #[cfg(feature = "tls")] + pub fn set_tls_config(&mut self, tls_config: tonic::transport::ClientTlsConfig) { + self.tls_config = Some(tls_config); + } + + pub fn set_access_token(&mut self, token: impl AsRef) -> Result<(), TokenError> { + match tonic::metadata::AsciiMetadataValue::try_from(&format!("Bearer {}", token.as_ref())) { + Ok(token) => { + self.token = Some(token); + Ok(()) + } + Err(err) => Err(TokenError::MalformedTokenError(format!("{err}"))), + } + } + + pub fn is_connected(&self) -> bool { + self.channel.is_some() + } + + pub fn subscribe_to_connection_state(&mut self) -> BroadcastStream { + match &self.connection_state_subs { + Some(stream) => BroadcastStream::new(stream.subscribe()), + None => { + let (tx, rx1) = tokio::sync::broadcast::channel(1); + self.connection_state_subs = Some(tx); + BroadcastStream::new(rx1) + } + } + } + + async fn try_create_channel(&mut self) -> Result<&Channel, ClientError> { + #[cfg(feature = "tls")] + let mut builder = tonic::transport::Channel::builder(self.uri.clone()); + #[cfg(not(feature = "tls"))] + let builder = tonic::transport::Channel::builder(self.uri.clone()); + + #[cfg(feature = "tls")] + if let Some(tls_config) = &self.tls_config { + match builder.tls_config(tls_config.clone()) { + Ok(new_builder) => { + builder = new_builder; + } + Err(err) => { + return Err(ClientError::Connection(format!( + "Failed to configure TLS: {err}" + ))); + } + } + } + + match builder.connect().await { + Ok(channel) => { + if let Some(subs) = &self.connection_state_subs { + subs.send(ConnectionState::Connected).map_err(|err| { + ClientError::Connection(format!( + "Failed to notify connection state change: {err}" + )) + })?; + } + self.channel = Some(channel); + Ok(self.channel.as_ref().expect("Channel should exist")) + } + Err(err) => { + if let Some(subs) = &self.connection_state_subs { + subs.send(ConnectionState::Disconnected).unwrap_or_default(); + } + Err(ClientError::Connection(format!( + "Failed to connect to {}: {}", + self.uri, err + ))) + } + } + } + + pub async fn try_connect(&mut self) -> Result<(), ClientError> { + self.try_create_channel().await?; + Ok(()) + } + + pub async fn try_connect_to(&mut self, uri: tonic::transport::Uri) -> Result<(), ClientError> { + self.uri = uri; + self.try_create_channel().await?; + Ok(()) + } + + pub async fn get_channel(&mut self) -> Result<&Channel, ClientError> { + if self.channel.is_none() { + self.try_create_channel().await + } else { + match &self.channel { + Some(channel) => Ok(channel), + None => unreachable!(), + } + } + } + + pub fn get_auth_interceptor( + &mut self, + ) -> impl FnMut(tonic::Request<()>) -> Result, tonic::Status> + '_ { + move |mut req: tonic::Request<()>| { + if let Some(token) = &self.token { + // debug!("Inserting auth token: {:?}", token); + req.metadata_mut().insert("authorization", token.clone()); + } + Ok(req) + } + } +} \ No newline at end of file diff --git a/kuksa_databroker/lib/kuksa/Cargo.toml b/kuksa_databroker/lib/kuksa/Cargo.toml new file mode 100644 index 000000000..18c57c3f7 --- /dev/null +++ b/kuksa_databroker/lib/kuksa/Cargo.toml @@ -0,0 +1,38 @@ +#******************************************************************************** +# Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License 2.0 which is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +#*******************************************************************************/ + +[package] +name = "kuksa" +version = "0.1.0" +authors = ["Robert Bosch GmbH"] +edition = "2018" +license = "Apache-2.0" + +[dependencies] +common = { path = "../."} +databroker-proto = { workspace = true } +tonic = { workspace = true, features = ["transport", "channel"] } +tokio = { workspace = true, features = [ + "macros", +] } +tokio-stream = { workspace = true, features = ["sync"] } +http = "0.2.8" + +[lib] +name = "kuksa" +crate-type = ["lib"] +path = "src/client.rs" + +[features] +default = ["tls"] +tls = ["tonic/tls"] \ No newline at end of file diff --git a/kuksa_databroker/lib/kuksa/src/client.rs b/kuksa_databroker/lib/kuksa/src/client.rs new file mode 100644 index 000000000..0f420f2e7 --- /dev/null +++ b/kuksa_databroker/lib/kuksa/src/client.rs @@ -0,0 +1,305 @@ +/******************************************************************************** +* Copyright (c) 2023 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License 2.0 which is available at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + +use std::collections::HashMap; + +use databroker_proto::kuksa::val::{self as proto, v1::DataEntry}; + +use common::{Client, ClientError,}; + +pub struct KuksaClient{ + pub basic_client: Client +} + +impl KuksaClient{ + pub fn new(basic_client: Client) -> Self{ + KuksaClient { basic_client } + } + + pub async fn get_metadata( + &mut self, + paths: Vec<&str>, + ) -> Result, ClientError> { + let mut client = proto::v1::val_client::ValClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + + let mut metadata_result = Vec::new(); + + for path in paths { + let get_request = proto::v1::GetRequest { + entries: vec![proto::v1::EntryRequest { + path: path.to_string(), + view: proto::v1::View::Metadata.into(), + fields: vec![proto::v1::Field::Metadata.into()], + }], + }; + + match client.get(get_request).await { + Ok(response) => { + let message = response.into_inner(); + let entries = message.entries; + metadata_result = entries; + } + Err(err) => return Err(ClientError::Status(err)), + } + } + + Ok(metadata_result) + } + + + pub async fn get_current_values( + &mut self, + paths: Vec>, + ) -> Result, ClientError>{ + let mut client = proto::v1::val_client::ValClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + + let mut get_result = Vec::new(); + + for path in paths { + let get_request = proto::v1::GetRequest { + entries: vec![proto::v1::EntryRequest { + path: path.as_ref().to_string(), + view: proto::v1::View::CurrentValue.into(), + fields: vec![proto::v1::Field::Value.into()], + }], + }; + + match client.get(get_request).await { + Ok(response) => { + let message = response.into_inner(); + get_result = message.entries; + } + Err(err) => return Err(ClientError::Status(err)), + } + } + + Ok(get_result) + } + + pub async fn get_target_values( + &mut self, + paths: Vec>, + ) -> Result, ClientError>{ + let mut client = proto::v1::val_client::ValClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + + let mut get_result = Vec::new(); + + for path in paths { + let get_request = proto::v1::GetRequest { + entries: vec![proto::v1::EntryRequest { + path: path.as_ref().to_string(), + view: proto::v1::View::TargetValue.into(), + fields: vec![proto::v1::Field::ActuatorTarget.into()], + }], + }; + + match client.get(get_request).await { + Ok(response) => { + let message = response.into_inner(); + get_result = message.entries; + } + Err(err) => return Err(ClientError::Status(err)), + } + } + + Ok(get_result) + } + + pub async fn set_current_values( + &mut self, + datapoints: HashMap, + ) -> Result, ClientError> { + let mut client = proto::v1::val_client::ValClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + + let mut set_result = Vec::new(); + + for (path, datapoint) in datapoints{ + let set_request = proto::v1::SetRequest { + updates: vec![proto::v1::EntryUpdate { + entry: Some(proto::v1::DataEntry { + path: path.clone(), + value: Some(datapoint), + actuator_target: None, + metadata: None, + }), + fields: vec![proto::v1::Field::Value.into(), proto::v1::Field::Path.into()], + }], + }; + match client.set(set_request).await { + Ok(response) => { + set_result.push(response.into_inner()); + } + Err(err) => return Err(ClientError::Status(err)), + } + } + + Ok(set_result) + } + + pub async fn set_target_values( + &mut self, + datapoints: HashMap, + ) -> Result, ClientError> { + let mut client = proto::v1::val_client::ValClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + + let mut set_result = Vec::new(); + + for (path, datapoint) in datapoints{ + let set_request = proto::v1::SetRequest { + updates: vec![proto::v1::EntryUpdate { + entry: Some(proto::v1::DataEntry { + path: path.clone(), + value: None, + actuator_target: Some(datapoint), + metadata: None, + }), + fields: vec![proto::v1::Field::ActuatorTarget.into(), proto::v1::Field::Path.into()], + }], + }; + match client.set(set_request).await { + Ok(response) => { + set_result.push(response.into_inner()); + } + Err(err) => return Err(ClientError::Status(err)), + } + } + + Ok(set_result) + } + + pub async fn set_metadata( + &mut self, + metadatas: HashMap, + ) -> Result, ClientError> { + let mut client = proto::v1::val_client::ValClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + + let mut set_result = Vec::new(); + + for (path, metadata) in metadatas{ + let set_request = proto::v1::SetRequest { + updates: vec![proto::v1::EntryUpdate { + entry: Some(proto::v1::DataEntry { + path: path.clone(), + value: None, + actuator_target: None, + metadata: Some(metadata), + }), + fields: vec![proto::v1::Field::Metadata.into(), proto::v1::Field::Path.into()], + }], + }; + match client.set(set_request).await { + Ok(response) => { + set_result.push(response.into_inner()); + } + Err(err) => return Err(ClientError::Status(err)), + } + } + + Ok(set_result) + } + + pub async fn subscribe_current_values( + &mut self, + paths: Vec<&str>, + ) -> Result, ClientError> { + let mut client = proto::v1::val_client::ValClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + + let mut entries = Vec::new(); + for path in paths{ + entries.push( + proto::v1::SubscribeEntry{path: path.to_string(), view: proto::v1::View::CurrentValue.into() ,fields: vec![proto::v1::Field::Value.into()] } + ) + } + + let req = proto::v1::SubscribeRequest{entries: entries}; + + match client.subscribe(req).await { + Ok(response) => Ok(response.into_inner()), + Err(err) => Err(ClientError::Status(err)), + } + } + + //masking subscribe curent values with subscribe + pub async fn subscribe(&mut self, + paths: Vec<&str>, + ) -> Result, ClientError> { + return self.subscribe_current_values(paths).await; + } + + pub async fn subscribe_target_values( + &mut self, + paths: Vec<&str>, + ) -> Result, ClientError> { + let mut client = proto::v1::val_client::ValClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + let mut entries = Vec::new(); + for path in paths{ + entries.push( + proto::v1::SubscribeEntry{path: path.to_string(), view: proto::v1::View::TargetValue.into() ,fields: vec![proto::v1::Field::ActuatorTarget.into()] } + ) + } + + let req = proto::v1::SubscribeRequest{entries: entries}; + + match client.subscribe(req).await { + Ok(response) => Ok(response.into_inner()), + Err(err) => Err(ClientError::Status(err)), + } + } + + pub async fn subscribe_metadata( + &mut self, + paths: Vec, + ) -> Result, ClientError> { + let mut client = proto::v1::val_client::ValClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + let mut entries = Vec::new(); + for path in paths{ + entries.push( + proto::v1::SubscribeEntry{path: path.to_string(), view: proto::v1::View::Metadata.into() ,fields: vec![proto::v1::Field::Metadata.into()] } + ) + } + + let req = proto::v1::SubscribeRequest{entries: entries}; + + match client.subscribe(req).await { + Ok(response) => Ok(response.into_inner()), + Err(err) => Err(ClientError::Status(err)), + } + } +} \ No newline at end of file diff --git a/kuksa_databroker/lib/sdv/Cargo.toml b/kuksa_databroker/lib/sdv/Cargo.toml new file mode 100644 index 000000000..bfa8dc713 --- /dev/null +++ b/kuksa_databroker/lib/sdv/Cargo.toml @@ -0,0 +1,38 @@ +#******************************************************************************** +# Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License 2.0 which is available at +# http://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +#*******************************************************************************/ + +[package] +name = "sdv" +version = "0.1.0" +authors = ["Robert Bosch GmbH"] +edition = "2018" +license = "Apache-2.0" + +[dependencies] +common = { path = "../."} +databroker-proto = { workspace = true } +tonic = { workspace = true, features = ["transport", "channel"] } +tokio = { workspace = true, features = [ + "macros", +] } +tokio-stream = { workspace = true, features = ["sync"] } +http = "0.2.8" + +[lib] +name = "sdv" +crate-type = ["lib"] +path = "src/client.rs" + +[features] +default = ["tls"] +tls = ["tonic/tls"] \ No newline at end of file diff --git a/kuksa_databroker/lib/sdv/src/client.rs b/kuksa_databroker/lib/sdv/src/client.rs new file mode 100644 index 000000000..4f9a72ec2 --- /dev/null +++ b/kuksa_databroker/lib/sdv/src/client.rs @@ -0,0 +1,116 @@ +/******************************************************************************** +* Copyright (c) 2023 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License 2.0 which is available at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + +use std::collections::HashMap; + +use databroker_proto::sdv::databroker as proto; +use common::{Client, ClientError}; + +pub struct SDVClient{ + pub basic_client: Client, +} + +impl SDVClient{ + pub fn new(basic_client: Client) -> Self{ + SDVClient { basic_client } + } + + pub async fn get_metadata( + &mut self, + paths: Vec, + ) -> Result, ClientError> { + let mut client = proto::v1::broker_client::BrokerClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + // Empty vec == all property metadata + let args = tonic::Request::new(proto::v1::GetMetadataRequest { names: paths }); + match client.get_metadata(args).await { + Ok(response) => { + let message = response.into_inner(); + Ok(message.list) + } + Err(err) => Err(ClientError::Status(err)), + } + } + + pub async fn get_datapoints( + &mut self, + paths: Vec>, + ) -> Result< + HashMap, + ClientError, + > { + let mut client = proto::v1::broker_client::BrokerClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + let args = tonic::Request::new(proto::v1::GetDatapointsRequest { + datapoints: paths.iter().map(|path| path.as_ref().into()).collect(), + }); + match client.get_datapoints(args).await { + Ok(response) => { + let message = response.into_inner(); + Ok(message.datapoints) + } + Err(err) => Err(ClientError::Status(err)), + } + } + + pub async fn set_datapoints( + &mut self, + datapoints: HashMap, + ) -> Result { + let args = tonic::Request::new(proto::v1::SetDatapointsRequest { datapoints }); + let mut client = proto::v1::broker_client::BrokerClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + match client.set_datapoints(args).await { + Ok(response) => Ok(response.into_inner()), + Err(err) => Err(ClientError::Status(err)), + } + } + + pub async fn subscribe( + &mut self, + query: String, + ) -> Result, ClientError> { + let mut client = proto::v1::broker_client::BrokerClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + let args = tonic::Request::new(proto::v1::SubscribeRequest { query }); + + match client.subscribe(args).await { + Ok(response) => Ok(response.into_inner()), + Err(err) => Err(ClientError::Status(err)), + } + } + + pub async fn update_datapoints( + &mut self, + datapoints: HashMap, + ) -> Result { + let mut client = proto::v1::collector_client::CollectorClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + + let request = tonic::Request::new(proto::v1::UpdateDatapointsRequest { datapoints }); + match client.update_datapoints(request).await { + Ok(response) => Ok(response.into_inner()), + Err(err) => Err(ClientError::Status(err)), + } + } +} From ef1a960744a823035ce1e54c1c4e0ae3a8d67337 Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Wed, 27 Sep 2023 10:35:28 +0200 Subject: [PATCH 02/14] Make databroker-cli plugable --- kuksa_databroker/databroker-cli/Cargo.toml | 7 +- kuksa_databroker/databroker-cli/src/client.rs | 278 --- kuksa_databroker/databroker-cli/src/main.rs | 1692 +++++++++++------ 3 files changed, 1168 insertions(+), 809 deletions(-) delete mode 100644 kuksa_databroker/databroker-cli/src/client.rs diff --git a/kuksa_databroker/databroker-cli/Cargo.toml b/kuksa_databroker/databroker-cli/Cargo.toml index 91d1ebc88..5ba13dd54 100644 --- a/kuksa_databroker/databroker-cli/Cargo.toml +++ b/kuksa_databroker/databroker-cli/Cargo.toml @@ -19,6 +19,9 @@ edition = "2018" license = "Apache-2.0" [dependencies] +common = { path = "../lib"} +kuksa = { path = "../lib/kuksa", optional = true} +sdv = { path = "../lib/sdv", optional = true} databroker-proto = { workspace = true } tonic = { workspace = true, features = ["transport", "channel", "prost"] } prost = { workspace = true } @@ -41,5 +44,7 @@ regex = "1.6.0" http = "0.2.8" [features] -default = ["tls"] +default = ["tls", "feature_sdv"] +feature_kuksa = ["kuksa"] +feature_sdv = ["sdv"] tls = ["tonic/tls"] diff --git a/kuksa_databroker/databroker-cli/src/client.rs b/kuksa_databroker/databroker-cli/src/client.rs deleted file mode 100644 index 1d4854c74..000000000 --- a/kuksa_databroker/databroker-cli/src/client.rs +++ /dev/null @@ -1,278 +0,0 @@ -/******************************************************************************** -* Copyright (c) 2023 Contributors to the Eclipse Foundation -* -* See the NOTICE file(s) distributed with this work for additional -* information regarding copyright ownership. -* -* This program and the accompanying materials are made available under the -* terms of the Apache License 2.0 which is available at -* http://www.apache.org/licenses/LICENSE-2.0 -* -* SPDX-License-Identifier: Apache-2.0 -********************************************************************************/ - -use std::{collections::HashMap, convert::TryFrom}; - -use databroker_proto::sdv::databroker as proto; -use http::Uri; -use tokio_stream::wrappers::BroadcastStream; -use tonic::transport::Channel; - -pub struct Client { - uri: Uri, - token: Option, - #[cfg(feature = "tls")] - tls_config: Option, - channel: Option, - connection_state_subs: Option>, -} - -#[derive(Clone)] -pub enum ConnectionState { - Connected, - Disconnected, -} - -#[derive(Debug)] -pub enum ClientError { - Connection(String), - Status(tonic::Status), -} - -impl std::error::Error for ClientError {} -impl std::fmt::Display for ClientError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - ClientError::Connection(con) => f.pad(con), - ClientError::Status(status) => f.pad(&format!("{status}")), - } - } -} - -#[derive(Debug)] -pub enum TokenError { - MalformedTokenError(String), -} - -impl std::error::Error for TokenError {} -impl std::fmt::Display for TokenError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - TokenError::MalformedTokenError(msg) => f.pad(msg), - } - } -} - -impl Client { - pub fn new(uri: Uri) -> Self { - Client { - uri, - token: None, - #[cfg(feature = "tls")] - tls_config: None, - channel: None, - connection_state_subs: None, - } - } - - pub fn get_uri(&self) -> String { - self.uri.to_string() - } - - #[cfg(feature = "tls")] - pub fn set_tls_config(&mut self, tls_config: tonic::transport::ClientTlsConfig) { - self.tls_config = Some(tls_config); - } - - pub fn set_access_token(&mut self, token: impl AsRef) -> Result<(), TokenError> { - match tonic::metadata::AsciiMetadataValue::try_from(&format!("Bearer {}", token.as_ref())) { - Ok(token) => { - self.token = Some(token); - Ok(()) - } - Err(err) => Err(TokenError::MalformedTokenError(format!("{err}"))), - } - } - - pub fn is_connected(&self) -> bool { - self.channel.is_some() - } - - pub fn subscribe_to_connection_state(&mut self) -> BroadcastStream { - match &self.connection_state_subs { - Some(stream) => BroadcastStream::new(stream.subscribe()), - None => { - let (tx, rx1) = tokio::sync::broadcast::channel(1); - self.connection_state_subs = Some(tx); - BroadcastStream::new(rx1) - } - } - } - - async fn try_create_channel(&mut self) -> Result<&Channel, ClientError> { - #[cfg(feature = "tls")] - let mut builder = tonic::transport::Channel::builder(self.uri.clone()); - #[cfg(not(feature = "tls"))] - let builder = tonic::transport::Channel::builder(self.uri.clone()); - - #[cfg(feature = "tls")] - if let Some(tls_config) = &self.tls_config { - match builder.tls_config(tls_config.clone()) { - Ok(new_builder) => { - builder = new_builder; - } - Err(err) => { - return Err(ClientError::Connection(format!( - "Failed to configure TLS: {err}" - ))); - } - } - } - - match builder.connect().await { - Ok(channel) => { - if let Some(subs) = &self.connection_state_subs { - subs.send(ConnectionState::Connected).map_err(|err| { - ClientError::Connection(format!( - "Failed to notify connection state change: {err}" - )) - })?; - } - self.channel = Some(channel); - Ok(self.channel.as_ref().expect("Channel should exist")) - } - Err(err) => { - if let Some(subs) = &self.connection_state_subs { - subs.send(ConnectionState::Disconnected).unwrap_or_default(); - } - Err(ClientError::Connection(format!( - "Failed to connect to {}: {}", - self.uri, err - ))) - } - } - } - - pub async fn try_connect(&mut self) -> Result<(), ClientError> { - self.try_create_channel().await?; - Ok(()) - } - - pub async fn try_connect_to(&mut self, uri: tonic::transport::Uri) -> Result<(), ClientError> { - self.uri = uri; - self.try_create_channel().await?; - Ok(()) - } - - pub async fn get_channel(&mut self) -> Result<&Channel, ClientError> { - if self.channel.is_none() { - self.try_create_channel().await - } else { - match &self.channel { - Some(channel) => Ok(channel), - None => unreachable!(), - } - } - } - - pub async fn get_metadata( - &mut self, - paths: Vec, - ) -> Result, ClientError> { - let mut client = proto::v1::broker_client::BrokerClient::with_interceptor( - self.get_channel().await?.clone(), - self.get_auth_interceptor(), - ); - // Empty vec == all property metadata - let args = tonic::Request::new(proto::v1::GetMetadataRequest { names: paths }); - match client.get_metadata(args).await { - Ok(response) => { - let message = response.into_inner(); - Ok(message.list) - } - Err(err) => Err(ClientError::Status(err)), - } - } - - pub async fn get_datapoints( - &mut self, - paths: Vec>, - ) -> Result< - HashMap, - ClientError, - > { - let mut client = proto::v1::broker_client::BrokerClient::with_interceptor( - self.get_channel().await?.clone(), - self.get_auth_interceptor(), - ); - let args = tonic::Request::new(proto::v1::GetDatapointsRequest { - datapoints: paths.iter().map(|path| path.as_ref().into()).collect(), - }); - match client.get_datapoints(args).await { - Ok(response) => { - let message = response.into_inner(); - Ok(message.datapoints) - } - Err(err) => Err(ClientError::Status(err)), - } - } - - pub async fn set_datapoints( - &mut self, - datapoints: HashMap, - ) -> Result { - let args = tonic::Request::new(proto::v1::SetDatapointsRequest { datapoints }); - let mut client = proto::v1::broker_client::BrokerClient::with_interceptor( - self.get_channel().await?.clone(), - self.get_auth_interceptor(), - ); - match client.set_datapoints(args).await { - Ok(response) => Ok(response.into_inner()), - Err(err) => Err(ClientError::Status(err)), - } - } - - pub async fn subscribe( - &mut self, - query: String, - ) -> Result, ClientError> { - let mut client = proto::v1::broker_client::BrokerClient::with_interceptor( - self.get_channel().await?.clone(), - self.get_auth_interceptor(), - ); - let args = tonic::Request::new(proto::v1::SubscribeRequest { query }); - - match client.subscribe(args).await { - Ok(response) => Ok(response.into_inner()), - Err(err) => Err(ClientError::Status(err)), - } - } - - pub async fn update_datapoints( - &mut self, - datapoints: HashMap, - ) -> Result { - let mut client = proto::v1::collector_client::CollectorClient::with_interceptor( - self.get_channel().await?.clone(), - self.get_auth_interceptor(), - ); - - let request = tonic::Request::new(proto::v1::UpdateDatapointsRequest { datapoints }); - match client.update_datapoints(request).await { - Ok(response) => Ok(response.into_inner()), - Err(err) => Err(ClientError::Status(err)), - } - } - - fn get_auth_interceptor( - &mut self, - ) -> impl FnMut(tonic::Request<()>) -> Result, tonic::Status> + '_ { - move |mut req: tonic::Request<()>| { - if let Some(token) = &self.token { - // debug!("Inserting auth token: {:?}", token); - req.metadata_mut().insert("authorization", token.clone()); - } - Ok(req) - } - } -} diff --git a/kuksa_databroker/databroker-cli/src/main.rs b/kuksa_databroker/databroker-cli/src/main.rs index 36b2a3d02..743790fdc 100644 --- a/kuksa_databroker/databroker-cli/src/main.rs +++ b/kuksa_databroker/databroker-cli/src/main.rs @@ -11,8 +11,6 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -use client::{ClientError, ConnectionState}; -use databroker_proto::sdv::databroker as proto; use http::uri::Uri; use prost_types::Timestamp; use tokio_stream::StreamExt; @@ -31,15 +29,13 @@ use linefeed::complete::{Completer, Completion, Suffix}; use linefeed::terminal::Terminal; use linefeed::{Command, DefaultTerminal, Function, Interface, Prompter, ReadResult}; -mod client; - const TIMEOUT: Duration = Duration::from_millis(500); const CLI_COMMANDS: &[(&str, &str, &str)] = &[ ("connect", "[URI]", "Connect to server"), ("get", " [[PATH] ...]", "Get signal value(s)"), ("set", " ", "Set actuator signal"), - ("subscribe", "", "Subscribe to signals with QUERY"), + ("subscribe", "", "Subscribe to signals with QUERY, if you use kuksa feature comma separated list"), ("feed", " ", "Publish signal value"), ( "metadata", @@ -56,6 +52,18 @@ const CLI_COMMANDS: &[(&str, &str, &str)] = &[ ("quit", "", "Quit"), ]; +#[cfg(feature = "feature_kuksa")] +mod root { + pub use databroker_proto::kuksa::val as proto; + pub use kuksa::*; +} + +#[cfg(feature = "feature_sdv")] +mod root { + pub use databroker_proto::sdv::databroker as proto; + pub use sdv::*; +} + #[derive(Debug, Parser)] #[clap(author, version, about, long_about = None)] struct Cli { @@ -91,7 +99,18 @@ enum Commands { #[tokio::main] async fn main() -> Result<(), Box> { - let mut properties = Vec::::new(); + let mut properties = Vec::new(); + #[cfg(feature = "feature_sdv")] + { + println!("Using sdv"); + properties = Vec::::new(); + } + #[cfg(feature = "feature_kuksa")] + { + println!("Using kuksa"); + properties = Vec::::new(); + } + let mut subscription_nbr = 1; let completer = CliCompleter::new(); @@ -106,10 +125,19 @@ async fn main() -> Result<(), Box> { let cli = Cli::parse(); - let mut client = client::Client::new(to_uri(cli.server)?); + let mut basic_client = common::Client::new(to_uri(cli.server)?); + let mut client; + #[cfg(feature = "feature_sdv")]{ + client = root::SDVClient::new(basic_client); + } + + #[cfg(feature = "feature_kuksa")]{ + client = root::KuksaClient::new(basic_client); + } + if let Some(token_filename) = cli.token_file { let token = std::fs::read_to_string(token_filename)?; - client.set_access_token(token)?; + client.basic_client.set_access_token(token)?; } #[cfg(feature = "tls")] @@ -119,20 +147,20 @@ async fn main() -> Result<(), Box> { let tls_config = tonic::transport::ClientTlsConfig::new().ca_certificate(ca_cert); - client.set_tls_config(tls_config); + client.basic_client.set_tls_config(tls_config); } - let mut connection_state_subscription = client.subscribe_to_connection_state(); + let mut connection_state_subscription = client.basic_client.subscribe_to_connection_state(); let interface_ref = interface.clone(); tokio::spawn(async move { while let Some(state) = connection_state_subscription.next().await { match state { Ok(state) => match state { - ConnectionState::Connected => { + common::ConnectionState::Connected => { set_connected_prompt(&interface_ref); } - ConnectionState::Disconnected => { + common::ConnectionState::Disconnected => { set_disconnected_prompt(&interface_ref); } }, @@ -149,14 +177,33 @@ async fn main() -> Result<(), Box> { match cli.command { Some(Commands::Get { paths }) => { - match client.get_datapoints(paths).await { - Ok(datapoints) => { - for (name, datapoint) in datapoints { - println!("{}: {}", name, DisplayDatapoint(datapoint),); + #[cfg(feature = "feature_sdv")]{ + match client.get_datapoints(paths).await { + Ok(datapoints) => { + for (name, datapoint) in datapoints { + println!("{}: {}", name, DisplayDatapoint(datapoint),); + } + } + Err(err) => { + eprintln!("{err}"); } } - Err(err) => { - eprintln!("{err}"); + } + #[cfg(feature = "feature_kuksa")]{ + match client.get_current_values(paths).await { + Ok(data_entries) => { + for entry in data_entries { + if let Some(val) = entry.value { + println!("{}: {}", entry.path, DisplayDatapoint(val),); + } + else{ + println!("{}: NotAvailable", entry.path); + } + } + } + Err(err) => { + eprintln!("{err}"); + } } } return Ok(()); @@ -169,19 +216,23 @@ async fn main() -> Result<(), Box> { }; print_logo(version); - match client.try_connect().await { + match client.basic_client.try_connect().await { Ok(()) => { - print_info(format!("Successfully connected to {}", client.get_uri()))?; - match client.get_metadata(vec![]).await { + print_info(format!("Successfully connected to {}", client.basic_client.get_uri()))?; + let mut pattern = vec![]; + #[cfg(feature = "feature_kuksa")]{ + pattern = vec!["**"]; + } + match client.get_metadata(pattern).await { Ok(metadata) => { interface .set_completer(Arc::new(CliCompleter::from_metadata(&metadata))); properties = metadata; } - Err(ClientError::Status(status)) => { + Err(common::ClientError::Status(status)) => { print_resp_err("metadata", &status)?; } - Err(ClientError::Connection(msg)) => { + Err(common::ClientError::Connection(msg)) => { print_error("metadata", msg)?; } } @@ -209,26 +260,49 @@ async fn main() -> Result<(), Box> { "get" => { interface.add_history_unique(line.clone()); - if args.is_empty() { - print_usage(cmd); - continue; - } - let paths = args - .split_whitespace() - .map(|path| path.to_owned()) - .collect(); - match client.get_datapoints(paths).await { - Ok(datapoints) => { - print_resp_ok(cmd)?; - for (name, datapoint) in datapoints { - println!("{}: {}", name, DisplayDatapoint(datapoint),); - } + if args.is_empty() { + print_usage(cmd); + continue; } - Err(ClientError::Status(err)) => { - print_resp_err(cmd, &err)?; + let paths = args + .split_whitespace() + .map(|path| path.to_owned()) + .collect(); + #[cfg(feature = "feature_sdv")]{ + match client.get_datapoints(paths).await { + Ok(datapoints) => { + print_resp_ok(cmd)?; + for (name, datapoint) in datapoints { + println!("{}: {}", name, DisplayDatapoint(datapoint),); + } + } + Err(common::ClientError::Status(err)) => { + print_resp_err(cmd, &err)?; + } + Err(common::ClientError::Connection(msg)) => { + print_error(cmd, msg)?; + } } - Err(ClientError::Connection(msg)) => { - print_error(cmd, msg)?; + } + #[cfg(feature = "feature_kuksa")]{ + match client.get_current_values(paths).await { + Ok(data_entries) => { + print_resp_ok(cmd)?; + for entry in data_entries { + if let Some(val) = entry.value{ + println!("{}: {}", entry.path, DisplayDatapoint(val),); + } + else{ + println!("{}: NotAvailable", entry.path); + } + } + } + Err(common::ClientError::Status(err)) => { + print_resp_err(cmd, &err)?; + } + Err(common::ClientError::Connection(msg)) => { + print_error(cmd, msg)?; + } } } } @@ -240,7 +314,7 @@ async fn main() -> Result<(), Box> { continue; } - match client.set_access_token(args) { + match client.basic_client.set_access_token(args) { Ok(()) => { print_info("Access token set.")?; match client.get_metadata(vec![]).await { @@ -250,10 +324,10 @@ async fn main() -> Result<(), Box> { )); properties = metadata; } - Err(ClientError::Status(status)) => { + Err(common::ClientError::Status(status)) => { print_resp_err("metadata", &status)?; } - Err(ClientError::Connection(msg)) => { + Err(common::ClientError::Connection(msg)) => { print_error("metadata", msg)?; } } @@ -271,7 +345,7 @@ async fn main() -> Result<(), Box> { let token_filename = args.trim(); match std::fs::read_to_string(token_filename) { - Ok(token) => match client.set_access_token(token) { + Ok(token) => match client.basic_client.set_access_token(token) { Ok(()) => { print_info("Access token set.")?; match client.get_metadata(vec![]).await { @@ -281,10 +355,10 @@ async fn main() -> Result<(), Box> { )); properties = metadata; } - Err(ClientError::Status(status)) => { + Err(common::ClientError::Status(status)) => { print_resp_err("metadata", &status)?; } - Err(ClientError::Connection(msg)) => { + Err(common::ClientError::Connection(msg)) => { print_error("metadata", msg)?; } } @@ -310,86 +384,170 @@ async fn main() -> Result<(), Box> { print_usage(cmd); continue; } - - let datapoint_metadata = { - let mut datapoint_metadata = None; - for metadata in properties.iter() { - if metadata.name == path { - datapoint_metadata = Some(metadata) + + #[cfg(feature = "feature_sdv")]{ + let datapoint_metadata = { + let mut datapoint_metadata = None; + for metadata in properties.iter() { + if metadata.name == path { + datapoint_metadata = Some(metadata) + } } - } - datapoint_metadata - }; - - if datapoint_metadata.is_none() { - print_info(format!( - "No metadata available for {path}. Needed to determine data type for serialization." - ))?; - continue; - } + datapoint_metadata + }; - if let Some(metadata) = datapoint_metadata { - let data_value = try_into_data_value( - value, - proto::v1::DataType::from_i32(metadata.data_type).unwrap(), - ); - if data_value.is_err() { - println!( - "Could not parse \"{value}\" as {:?}", - proto::v1::DataType::from_i32(metadata.data_type).unwrap() - ); + if datapoint_metadata.is_none() { + print_info(format!( + "No metadata available for {path}. Needed to determine data type for serialization." + ))?; continue; } - if metadata.entry_type != proto::v1::EntryType::Actuator.into() { - print_error( - cmd, - format!("{} is not an actuator.", metadata.name), - )?; - print_info( - "If you want to provide the signal value, use `feed`.", - )?; - continue; - } + if let Some(metadata) = datapoint_metadata { + let data_value = try_into_data_value( + value, + root::proto::v1::DataType::from_i32(metadata.data_type).unwrap(), + ); + if data_value.is_err() { + println!( + "Could not parse \"{value}\" as {:?}", + root::proto::v1::DataType::from_i32(metadata.data_type).unwrap() + ); + continue; + } - let ts = Timestamp::from(SystemTime::now()); - let datapoints = HashMap::from([( - metadata.name.clone(), - proto::v1::Datapoint { - timestamp: Some(ts), - value: Some(data_value.unwrap()), - }, - )]); - - match client.set_datapoints(datapoints).await { - Ok(message) => { - if message.errors.is_empty() { - print_resp_ok(cmd)?; - } else { - for (id, error) in message.errors { - match proto::v1::DatapointError::from_i32(error) { - Some(error) => { - print_resp_ok(cmd)?; - println!( - "Error setting {}: {}", - id, - Color::Red.paint(format!("{error:?}")), - ); + if metadata.entry_type != root::proto::v1::EntryType::Actuator.into() { + print_error( + cmd, + format!("{} is not an actuator.", metadata.name), + )?; + print_info( + "If you want to provide the signal value, use `feed`.", + )?; + continue; + } + + let ts = Timestamp::from(SystemTime::now()); + let datapoints = HashMap::from([( + metadata.name.clone(), + root::proto::v1::Datapoint { + timestamp: Some(ts), + value: Some(data_value.unwrap()), + }, + )]); + + match client.set_datapoints(datapoints).await { + Ok(message) => { + if message.errors.is_empty() { + print_resp_ok(cmd)?; + } else { + for (id, error) in message.errors { + match root::proto::v1::DatapointError::from_i32(error) { + Some(error) => { + print_resp_ok(cmd)?; + println!( + "Error setting {}: {}", + id, + Color::Red.paint(format!("{error:?}")), + ); + } + None => print_resp_ok_fmt( + cmd, + format_args!("Error setting id {id}"), + )?, } - None => print_resp_ok_fmt( - cmd, - format_args!("Error setting id {id}"), - )?, } } } + Err(common::ClientError::Status(status)) => { + print_resp_err(cmd, &status)? + } + Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, } - Err(ClientError::Status(status)) => { - print_resp_err(cmd, &status)? - } - Err(ClientError::Connection(msg)) => print_error(cmd, msg)?, } } + #[cfg(feature = "feature_kuksa")]{ + let datapoint_entries = match client.get_metadata(vec![path]).await { + Ok(data_entries) => Some(data_entries), + Err(common::ClientError::Status(status)) => { + print_resp_err("metadata", &status)?; + None + } + Err(common::ClientError::Connection(msg)) => { + print_error("metadata", msg)?; + None + } + }; + + if let Some(entries) = datapoint_entries { + for entry in entries{ + if let Some(metadata) = entry.metadata { + let data_value = try_into_data_value( + value, + root::proto::v1::DataType::from_i32(metadata.data_type).unwrap(), + ); + if data_value.is_err() { + println!( + "Could not parse \"{value}\" as {:?}", + root::proto::v1::DataType::from_i32(metadata.data_type).unwrap() + ); + continue; + } + + if metadata.entry_type != root::proto::v1::EntryType::Actuator.into() { + print_error( + cmd, + format!("{} is not an actuator.", path), + )?; + print_info( + "If you want to provide the signal value, use `feed`.", + )?; + continue; + } + + let ts = Timestamp::from(SystemTime::now()); + let datapoints = HashMap::from([( + path.to_string().clone(), + root::proto::v1::Datapoint { + timestamp: Some(ts), + value: Some(data_value.unwrap()), + }, + )]); + + match client.set_current_values(datapoints).await { + Ok(res) => { + for message in res { + if message.errors.is_empty() { + print_resp_ok(cmd)?; + } else { + for error in message.errors { + print_resp_ok(cmd)?; + let error_mes = error.error; + println!( + "Error setting {}: {}", + error.path, + Color::Red.paint(format!("{error_mes:?}")), + ); + } + } + match message.error{ + Some(error) => {print_resp_ok_fmt( + cmd, + format_args!("Error {error:?}"), + )?}, + None => () + }; + } + } + Err(common::ClientError::Status(status)) => { + print_resp_err(cmd, &status)? + } + Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, + } + } + } + } + } } "feed" => { interface.add_history_unique(line.clone()); @@ -401,74 +559,149 @@ async fn main() -> Result<(), Box> { continue; } - let datapoint_metadata = { - let mut datapoint_metadata = None; - for metadata in properties.iter() { - if metadata.name == path { - datapoint_metadata = Some(metadata) + #[cfg(feature = "feature_sdv")]{ + let datapoint_metadata = { + let mut datapoint_metadata = None; + for metadata in properties.iter() { + if metadata.name == path { + datapoint_metadata = Some(metadata) + } } - } - datapoint_metadata - }; + datapoint_metadata + }; - if datapoint_metadata.is_none() { - print_info( - format!("No metadata available for {path}. Needed to determine data type for serialization."), - )?; - continue; - } + if datapoint_metadata.is_none() { + print_info( + format!("No metadata available for {path}. Needed to determine data type for serialization."), + )?; + continue; + } - if let Some(metadata) = datapoint_metadata { - let data_value = try_into_data_value( - value, - proto::v1::DataType::from_i32(metadata.data_type).unwrap(), - ); - if data_value.is_err() { - println!( - "Could not parse \"{}\" as {:?}", + if let Some(metadata) = datapoint_metadata { + let data_value = try_into_data_value( value, - proto::v1::DataType::from_i32(metadata.data_type).unwrap() + root::proto::v1::DataType::from_i32(metadata.data_type).unwrap(), ); - continue; - } - let ts = Timestamp::from(SystemTime::now()); - let datapoints = HashMap::from([( - metadata.id, - proto::v1::Datapoint { - timestamp: Some(ts), - value: Some(data_value.unwrap()), - }, - )]); - match client.update_datapoints(datapoints).await { - Ok(message) => { - if message.errors.is_empty() { - print_resp_ok(cmd)? - } else { - for (id, error) in message.errors { - let identifier = if id == metadata.id { - metadata.name.to_string() - } else { - format!("id {id}") - }; - match proto::v1::DatapointError::from_i32(error) { - Some(error) => print_resp_ok_fmt( - cmd, - format_args!( - "Error providing {identifier}: {error:?}", - ), - )?, - None => print_resp_ok_fmt( - cmd, - format_args!("Error providing {identifier}",), - )?, + if data_value.is_err() { + println!( + "Could not parse \"{}\" as {:?}", + value, + root::proto::v1::DataType::from_i32(metadata.data_type).unwrap() + ); + continue; + } + let ts = Timestamp::from(SystemTime::now()); + let datapoints = HashMap::from([( + metadata.id, + root::proto::v1::Datapoint { + timestamp: Some(ts), + value: Some(data_value.unwrap()), + }, + )]); + + + match client.update_datapoints(datapoints).await { + Ok(message) => { + if message.errors.is_empty() { + print_resp_ok(cmd)? + } else { + for (id, error) in message.errors { + let identifier = if id == metadata.id { + metadata.name.to_string() + } else { + format!("id {id}") + }; + match root::proto::v1::DatapointError::from_i32(error) { + Some(error) => print_resp_ok_fmt( + cmd, + format_args!( + "Error providing {identifier}: {error:?}", + ), + )?, + None => print_resp_ok_fmt( + cmd, + format_args!("Error providing {identifier}",), + )?, + } } } } + Err(common::ClientError::Status(status)) => { + print_resp_err(cmd, &status)? + } + Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, } - Err(ClientError::Status(status)) => { - print_resp_err(cmd, &status)? + } + } + #[cfg(feature = "feature_kuksa")]{ + let datapoint_entries = match client.get_metadata(vec![path]).await { + Ok(data_entries) => Some(data_entries), + Err(common::ClientError::Status(status)) => { + print_resp_err("metadata", &status)?; + None + } + Err(common::ClientError::Connection(msg)) => { + print_error("metadata", msg)?; + None + } + }; + + if let Some(entries) = datapoint_entries { + for entry in entries{ + if let Some(metadata) = entry.metadata { + let data_value = try_into_data_value( + value, + root::proto::v1::DataType::from_i32(metadata.data_type).unwrap(), + ); + if data_value.is_err() { + println!( + "Could not parse \"{}\" as {:?}", + value, + root::proto::v1::DataType::from_i32(metadata.data_type).unwrap() + ); + continue; + } + let ts = Timestamp::from(SystemTime::now()); + let datapoints = HashMap::from([( + path.to_string().clone(), + root::proto::v1::Datapoint { + timestamp: Some(ts), + value: Some(data_value.unwrap()), + }, + )]); + + match client.set_current_values(datapoints).await { + Ok(res) => { + for message in res { + if message.errors.is_empty() { + print_resp_ok(cmd)?; + } else { + for error in message.errors { + print_resp_ok(cmd)?; + let error_mes = error.error; + println!( + "Error setting {}: {}", + error.path, + Color::Red.paint(format!("{error_mes:?}")), + ); + } + } + match message.error{ + Some(error) => {print_resp_ok_fmt( + cmd, + format_args!("Error {error:?}"), + )?}, + None => () + }; + } + } + Err(common::ClientError::Status(status)) => { + print_resp_err(cmd, &status)? + } + Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, + } + } } - Err(ClientError::Connection(msg)) => print_error(cmd, msg)?, } } } @@ -479,10 +712,17 @@ async fn main() -> Result<(), Box> { print_usage(cmd); continue; } + + let mut input; - let query = args.to_owned(); + #[cfg(feature = "feature_sdv")]{ + input = args.to_owned(); + } + #[cfg(feature = "feature_kuksa")]{ + input = args.split_whitespace().collect::>(); + } - match client.subscribe(query).await { + match client.subscribe(input).await { Ok(mut subscription) => { let iface = interface.clone(); tokio::spawn(async move { @@ -500,32 +740,66 @@ async fn main() -> Result<(), Box> { use std::fmt::Write; let mut output = String::new(); let mut first_line = true; - for (name, value) in resp.fields { - if first_line { - first_line = false; - write!( - output, - "{} ", - &sub_disp_color, - ) - .unwrap(); - } else { - write!( + #[cfg(feature = "feature_sdv")]{ + for (name, value) in resp.fields { + if first_line { + first_line = false; + write!( + output, + "{} ", + &sub_disp_color, + ) + .unwrap(); + } else { + write!( + output, + "{} ", + &sub_disp_pad, + ) + .unwrap(); + } + writeln!( output, - "{} ", - &sub_disp_pad, + "{}: {}", + name, + DisplayDatapoint(value) ) .unwrap(); } - writeln!( - output, - "{}: {}", - name, - DisplayDatapoint(value) - ) - .unwrap(); + write!(iface, "{output}").unwrap(); + } + #[cfg(feature = "feature_kuksa")]{ + for update in resp.updates { + if first_line { + first_line = false; + write!( + output, + "{} ", + &sub_disp_color, + ) + .unwrap(); + } else { + write!( + output, + "{} ", + &sub_disp_pad, + ) + .unwrap(); + } + if let Some(entry) = update.entry{ + if let Some(value) = entry.value{ + writeln!( + output, + "{}: {}", + entry.path, + DisplayDatapoint(value) + ) + .unwrap(); + } + } + } + write!(iface, "{output}").unwrap(); } - write!(iface, "{output}").unwrap(); } else { writeln!( iface, @@ -562,19 +836,19 @@ async fn main() -> Result<(), Box> { )?; subscription_nbr += 1; } - Err(ClientError::Status(status)) => print_resp_err(cmd, &status)?, - Err(ClientError::Connection(msg)) => print_error(cmd, msg)?, + Err(common::ClientError::Status(status)) => print_resp_err(cmd, &status)?, + Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, } } "connect" => { interface.add_history_unique(line.clone()); - if !client.is_connected() || !args.is_empty() { + if !client.basic_client.is_connected() || !args.is_empty() { if args.is_empty() { - match client.try_connect().await { + match client.basic_client.try_connect().await { Ok(()) => { print_info(format!( "[{cmd}] Successfully connected to {}", - client.get_uri() + client.basic_client.get_uri() ))?; } Err(err) => { @@ -584,11 +858,11 @@ async fn main() -> Result<(), Box> { } else { match to_uri(args) { Ok(valid_uri) => { - match client.try_connect_to(valid_uri).await { + match client.basic_client.try_connect_to(valid_uri).await { Ok(()) => { print_info(format!( "[{cmd}] Successfully connected to {}", - client.get_uri() + client.basic_client.get_uri() ))?; } Err(err) => { @@ -604,7 +878,7 @@ async fn main() -> Result<(), Box> { } } }; - if client.is_connected() { + if client.basic_client.is_connected() { match client.get_metadata(vec![]).await { Ok(metadata) => { interface.set_completer(Arc::new( @@ -612,10 +886,10 @@ async fn main() -> Result<(), Box> { )); properties = metadata; } - Err(ClientError::Status(status)) => { + Err(common::ClientError::Status(status)) => { print_resp_err("metadata", &status)?; } - Err(ClientError::Connection(msg)) => { + Err(common::ClientError::Connection(msg)) => { print_error("metadata", msg)?; } } @@ -626,70 +900,127 @@ async fn main() -> Result<(), Box> { interface.add_history_unique(line.clone()); let paths = args.split_whitespace().collect::>(); - match client.get_metadata(vec![]).await { - Ok(mut metadata) => { - metadata.sort_by(|a, b| a.name.cmp(&b.name)); - properties = metadata; - interface.set_completer(Arc::new(CliCompleter::from_metadata( - &properties, - ))); - print_resp_ok(cmd)?; - } - Err(ClientError::Status(status)) => { - print_resp_err(cmd, &status)?; - continue; + + #[cfg(feature = "feature_sdv")]{ + match client.get_metadata(vec![]).await { + Ok(mut metadata) => { + metadata.sort_by(|a, b| a.name.cmp(&b.name)); + properties = metadata; + interface.set_completer(Arc::new(CliCompleter::from_metadata( + &properties, + ))); + print_resp_ok(cmd)?; + } + Err(common::ClientError::Status(status)) => { + print_resp_err(cmd, &status)?; + continue; + } + Err(common::ClientError::Connection(msg)) => { + print_error(cmd, msg)?; + continue; + } } - Err(ClientError::Connection(msg)) => { - print_error(cmd, msg)?; - continue; + let mut filtered_metadata = Vec::new(); + if paths.is_empty() { + print_info("If you want to list metadata of signals, use `metadata PATTERN`")?; + // filtered_metadata.extend(&properties); + } else { + for path in &paths { + let path_re = path_to_regex(path); + let filtered = + properties.iter().filter(|item| match &path_re { + Ok(re) => re.is_match(&item.name), + Err(err) => { + print_info(format!("Invalid path: {err}")) + .unwrap_or_default(); + false + } + }); + filtered_metadata.extend(filtered); + } } - } - let mut filtered_metadata = Vec::new(); - if paths.is_empty() { - print_info("If you want to list metadata of signals, use `metadata PATTERN`")?; - // filtered_metadata.extend(&properties); - } else { - for path in &paths { - let path_re = path_to_regex(path); - let filtered = - properties.iter().filter(|item| match &path_re { - Ok(re) => re.is_match(&item.name), - Err(err) => { - print_info(format!("Invalid path: {err}")) - .unwrap_or_default(); - false + + if !filtered_metadata.is_empty() { + let max_len_path = + filtered_metadata.iter().fold(0, |mut max_len, item| { + if item.name.len() > max_len { + max_len = item.name.len(); } + max_len }); - filtered_metadata.extend(filtered); + + print_info(format!( + "{: { + print_resp_ok(cmd)?; + if !metadata.is_empty() { + let max_len_path = + metadata.iter().fold(0, |mut max_len, item| { + if item.path.len() > max_len { + max_len = item.path.len(); + } + max_len + }); - if !filtered_metadata.is_empty() { - let max_len_path = - filtered_metadata.iter().fold(0, |mut max_len, item| { - if item.name.len() > max_len { - max_len = item.name.len(); - } - max_len - }); + print_info(format!( + "{: { + print_resp_err(cmd, &status)?; + continue; + } + Err(common::ClientError::Connection(msg)) => { + print_error(cmd, msg)?; + continue; + } - for entry in &filtered_metadata { - println!( - "{: { @@ -873,30 +1204,56 @@ impl CliCompleter { } } - fn from_metadata(metadata: &[proto::v1::Metadata]) -> CliCompleter { - let mut root = PathPart::new(); - for entry in metadata { - let mut parent = &mut root; - let parts = entry.name.split('.'); - for part in parts { - let full_path = match parent.full_path.as_str() { - "" => part.to_owned(), - _ => format!("{}.{}", parent.full_path, part), - }; - let entry = parent - .children - .entry(part.to_lowercase()) - .or_insert(PathPart { - rel_path: part.to_owned(), - full_path, - children: HashMap::new(), - }); - - parent = entry; + #[cfg(feature = "feature_sdv")] + fn from_metadata(metadata: &[root::proto::v1::Metadata]) -> CliCompleter { + let mut root = PathPart::new(); + for entry in metadata { + let mut parent = &mut root; + let parts = entry.name.split('.'); + for part in parts { + let full_path = match parent.full_path.as_str() { + "" => part.to_owned(), + _ => format!("{}.{}", parent.full_path, part), + }; + let entry = parent + .children + .entry(part.to_lowercase()) + .or_insert(PathPart { + rel_path: part.to_owned(), + full_path, + children: HashMap::new(), + }); + + parent = entry; + } } + CliCompleter { paths: root } + } + #[cfg(feature = "feature_kuksa")] + fn from_metadata(entries: &Vec) -> CliCompleter { + let mut root = PathPart::new(); + for entry in entries { + let mut parent = &mut root; + let parts = entry.path.split('.'); + for part in parts { + let full_path = match parent.full_path.as_str() { + "" => part.to_owned(), + _ => format!("{}.{}", parent.full_path, part), + }; + let entry = parent + .children + .entry(part.to_lowercase()) + .or_insert(PathPart { + rel_path: part.to_owned(), + full_path, + children: HashMap::new(), + }); + + parent = entry; + } + } + CliCompleter { paths: root } } - CliCompleter { paths: root } - } fn complete_entry_path(&self, word: &str) -> Option> { if !self.paths.children.is_empty() { @@ -1173,10 +1530,11 @@ impl Function for EnterFunction { } } -struct DisplayDataType(Option); -struct DisplayEntryType(Option); -struct DisplayChangeType(Option); -struct DisplayDatapoint(proto::v1::Datapoint); +struct DisplayDataType(Option); +struct DisplayEntryType(Option); +// !!! ChangeType currently just exists in old API needs to be removed or added later !!! +struct DisplayChangeType(Option); +struct DisplayDatapoint(root::proto::v1::Datapoint); fn display_array(f: &mut fmt::Formatter<'_>, array: &[T]) -> fmt::Result where @@ -1195,36 +1553,61 @@ where impl fmt::Display for DisplayDatapoint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match &self.0.value { - Some(value) => match value { - proto::v1::datapoint::Value::BoolValue(value) => f.pad(&format!("{value}")), - proto::v1::datapoint::Value::FailureValue(failure) => f.pad(&format!( - "( {:?} )", - proto::v1::datapoint::Failure::from_i32(*failure).unwrap() - )), - proto::v1::datapoint::Value::Int32Value(value) => f.pad(&format!("{value}")), - proto::v1::datapoint::Value::Int64Value(value) => f.pad(&format!("{value}")), - proto::v1::datapoint::Value::Uint32Value(value) => f.pad(&format!("{value}")), - proto::v1::datapoint::Value::Uint64Value(value) => f.pad(&format!("{value}")), - proto::v1::datapoint::Value::FloatValue(value) => f.pad(&format!("{value:.2}")), - proto::v1::datapoint::Value::DoubleValue(value) => f.pad(&format!("{value}")), - proto::v1::datapoint::Value::StringValue(value) => f.pad(&format!("'{value}'")), - proto::v1::datapoint::Value::StringArray(array) => display_array(f, &array.values), - proto::v1::datapoint::Value::BoolArray(array) => display_array(f, &array.values), - proto::v1::datapoint::Value::Int32Array(array) => display_array(f, &array.values), - proto::v1::datapoint::Value::Int64Array(array) => display_array(f, &array.values), - proto::v1::datapoint::Value::Uint32Array(array) => display_array(f, &array.values), - proto::v1::datapoint::Value::Uint64Array(array) => display_array(f, &array.values), - proto::v1::datapoint::Value::FloatArray(array) => display_array(f, &array.values), - proto::v1::datapoint::Value::DoubleArray(array) => display_array(f, &array.values), - }, - None => f.pad("None"), + #[cfg(feature = "feature_sdv")]{ + match &self.0.value { + Some(value) => match value { + root::proto::v1::datapoint::Value::BoolValue(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::FailureValue(failure) => f.pad(&format!( + "( {:?} )", + root::proto::v1::datapoint::Failure::from_i32(*failure).unwrap() + )), + root::proto::v1::datapoint::Value::Int32Value(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Int64Value(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Uint32Value(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Uint64Value(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::FloatValue(value) => f.pad(&format!("{value:.2}")), + root::proto::v1::datapoint::Value::DoubleValue(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::StringValue(value) => f.pad(&format!("'{value}'")), + root::proto::v1::datapoint::Value::StringArray(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::BoolArray(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::Int32Array(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::Int64Array(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::Uint32Array(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::Uint64Array(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::FloatArray(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::DoubleArray(array) => display_array(f, &array.values), + }, + None => f.pad("None"), + } + } + #[cfg(feature = "feature_kuksa")]{ + match &self.0.value { + Some(value) => match value { + root::proto::v1::datapoint::Value::Bool(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Int32(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Int64(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Uint32(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Uint64(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Float(value) => f.pad(&format!("{value:.2}")), + root::proto::v1::datapoint::Value::Double(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::String(value) => f.pad(&format!("'{value}'")), + root::proto::v1::datapoint::Value::StringArray(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::BoolArray(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::Int32Array(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::Int64Array(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::Uint32Array(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::Uint64Array(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::FloatArray(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::DoubleArray(array) => display_array(f, &array.values), + }, + None => f.pad("None"), + } } } } -impl From> for DisplayEntryType { - fn from(input: Option) -> Self { +impl From> for DisplayEntryType { + fn from(input: Option) -> Self { DisplayEntryType(input) } } @@ -1238,8 +1621,8 @@ impl fmt::Display for DisplayEntryType { } } -impl From> for DisplayDataType { - fn from(input: Option) -> Self { +impl From> for DisplayDataType { + fn from(input: Option) -> Self { DisplayDataType(input) } } @@ -1253,8 +1636,8 @@ impl fmt::Display for DisplayDataType { } } -impl From> for DisplayChangeType { - fn from(input: Option) -> Self { +impl From> for DisplayChangeType { + fn from(input: Option) -> Self { DisplayChangeType(input) } } @@ -1306,136 +1689,270 @@ fn get_array_from_input(values: String) -> Result, fn try_into_data_value( input: &str, - data_type: proto::v1::DataType, -) -> Result { + data_type: root::proto::v1::DataType, +) -> Result { if input == "NotAvailable" { - return Ok(proto::v1::datapoint::Value::FailureValue( - proto::v1::datapoint::Failure::NotAvailable as i32, - )); + #[cfg(feature = "feature_sdv")]{ + return Ok(root::proto::v1::datapoint::Value::FailureValue( + root::proto::v1::datapoint::Failure::NotAvailable as i32, + )); + } + #[cfg(feature = "feature_kuksa")]{ + return Ok(root::proto::v1::datapoint::Value::String( + input.to_string(), + )); + } } - match data_type { - proto::v1::DataType::String => { - Ok(proto::v1::datapoint::Value::StringValue(input.to_owned())) + #[cfg(feature = "feature_sdv")]{ + match data_type { + root::proto::v1::DataType::String => { + Ok(root::proto::v1::datapoint::Value::StringValue(input.to_owned())) + } + root::proto::v1::DataType::StringArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( + root::proto::v1::StringArray { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Bool => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolValue(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::BoolArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolArray( + root::proto::v1::BoolArray { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int8 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value as i32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int8Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int16 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value as i32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int16Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int32 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int32Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int64 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Value(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int64Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Array( + root::proto::v1::Int64Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint8 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value as u32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint8Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint16 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value as u32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint16Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint32 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint32Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint64 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Value(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint64Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( + root::proto::v1::Uint64Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Float => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatValue(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::FloatArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatArray( + root::proto::v1::FloatArray { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Double => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleValue(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::DoubleArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( + root::proto::v1::DoubleArray { values: value }, + )), + Err(err) => Err(err), + }, + _ => Err(ParseError {}), } - proto::v1::DataType::StringArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::StringArray( - databroker_proto::sdv::databroker::v1::StringArray { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Bool => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::BoolValue(value)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::BoolArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::BoolArray( - databroker_proto::sdv::databroker::v1::BoolArray { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Int8 => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::Int32Value(value as i32)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::Int8Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Int32Array( - databroker_proto::sdv::databroker::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Int16 => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::Int32Value(value as i32)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::Int16Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Int32Array( - databroker_proto::sdv::databroker::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Int32 => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::Int32Value(value)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::Int32Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Int32Array( - databroker_proto::sdv::databroker::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Int64 => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::Int64Value(value)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::Int64Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Int64Array( - databroker_proto::sdv::databroker::v1::Int64Array { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Uint8 => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Value(value as u32)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::Uint8Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( - databroker_proto::sdv::databroker::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Uint16 => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Value(value as u32)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::Uint16Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( - databroker_proto::sdv::databroker::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Uint32 => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Value(value)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::Uint32Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( - databroker_proto::sdv::databroker::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Uint64 => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint64Value(value)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::Uint64Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint64Array( - databroker_proto::sdv::databroker::v1::Uint64Array { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Float => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::FloatValue(value)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::FloatArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::FloatArray( - databroker_proto::sdv::databroker::v1::FloatArray { values: value }, - )), - Err(err) => Err(err), - }, - proto::v1::DataType::Double => match input.parse::() { - Ok(value) => Ok(proto::v1::datapoint::Value::DoubleValue(value)), - Err(_) => Err(ParseError {}), - }, - proto::v1::DataType::DoubleArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::DoubleArray( - databroker_proto::sdv::databroker::v1::DoubleArray { values: value }, - )), - Err(err) => Err(err), - }, - _ => Err(ParseError {}), } + #[cfg(feature = "feature_kuksa")]{ + match data_type { + root::proto::v1::DataType::String => { + Ok(root::proto::v1::datapoint::Value::String(input.to_owned())) + } + root::proto::v1::DataType::StringArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( + root::proto::v1::StringArray { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Boolean => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Bool(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::BooleanArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolArray( + root::proto::v1::BoolArray { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int8 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value as i32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int8Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int16 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value as i32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int16Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int32 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int32Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int64 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int64Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Array( + root::proto::v1::Int64Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint8 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value as u32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint8Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint16 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value as u32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint16Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint32 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint32Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint64 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint64Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( + root::proto::v1::Uint64Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Float => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Float(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::FloatArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatArray( + root::proto::v1::FloatArray { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Double => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Double(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::DoubleArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( + root::proto::v1::DoubleArray { values: value }, + )), + Err(err) => Err(err), + }, + _ => Err(ParseError {}), + } + } + } #[cfg(test)] @@ -1445,100 +1962,215 @@ mod test { #[test] fn test_parse_values() { - // String - assert!(matches!( - try_into_data_value("test", proto::v1::DataType::String), - Ok(proto::v1::datapoint::Value::StringValue(value)) if value == "test" - )); - - // StringArray - assert!(matches!( - try_into_data_value("[test, test2, test4]", proto::v1::DataType::StringArray), - Ok(proto::v1::datapoint::Value::StringArray(value)) if value == databroker_proto::sdv::databroker::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} - )); - - // Bool - assert!(matches!( - try_into_data_value("true", proto::v1::DataType::Bool), - Ok(proto::v1::datapoint::Value::BoolValue(value)) if value - )); - - assert!(matches!( - try_into_data_value("false", proto::v1::DataType::Bool), - Ok(proto::v1::datapoint::Value::BoolValue(value)) if !value - )); - assert!(try_into_data_value("truefalse", proto::v1::DataType::Bool).is_err()); - // BoolArray - assert!(matches!( - try_into_data_value("[true, false, true]", proto::v1::DataType::BoolArray), - Ok(proto::v1::datapoint::Value::BoolArray(value)) if value == databroker_proto::sdv::databroker::v1::BoolArray{values: vec![true, false, true]} - )); - - // Int8 - assert!(matches!( - try_into_data_value("100", proto::v1::DataType::Int8), - Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == 100 - )); - assert!(matches!( - try_into_data_value("-100", proto::v1::DataType::Int8), - Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == -100 - )); - assert!(try_into_data_value("300", proto::v1::DataType::Int8).is_err()); - assert!(try_into_data_value("-300", proto::v1::DataType::Int8).is_err()); - assert!(try_into_data_value("-100.1", proto::v1::DataType::Int8).is_err()); - - // Int16 - assert!(matches!( - try_into_data_value("100", proto::v1::DataType::Int16), - Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == 100 - )); - assert!(matches!( - try_into_data_value("-100", proto::v1::DataType::Int16), - Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == -100 - )); - assert!(matches!( - try_into_data_value("32000", proto::v1::DataType::Int16), - Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == 32000 - )); - assert!(matches!( - try_into_data_value("-32000", proto::v1::DataType::Int16), - Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == -32000 - )); - assert!(try_into_data_value("33000", proto::v1::DataType::Int16).is_err()); - assert!(try_into_data_value("-33000", proto::v1::DataType::Int16).is_err()); - assert!(try_into_data_value("-32000.1", proto::v1::DataType::Int16).is_err()); + #[cfg(feature = "feature_sdv")]{ + // String + assert!(matches!( + try_into_data_value("test", root::proto::v1::DataType::String), + Ok(root::proto::v1::datapoint::Value::StringValue(value)) if value == "test" + )); + + // StringArray + assert!(matches!( + try_into_data_value("[test, test2, test4]", root::proto::v1::DataType::StringArray), + Ok(root::proto::v1::datapoint::Value::StringArray(value)) if value == root::proto::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} + )); + + // Bool + assert!(matches!( + try_into_data_value("true", root::proto::v1::DataType::Bool), + Ok(root::proto::v1::datapoint::Value::BoolValue(value)) if value + )); + + assert!(matches!( + try_into_data_value("false", root::proto::v1::DataType::Bool), + Ok(root::proto::v1::datapoint::Value::BoolValue(value)) if !value + )); + assert!(try_into_data_value("truefalse", root::proto::v1::DataType::Bool).is_err()); + // BoolArray + assert!(matches!( + try_into_data_value("[true, false, true]", root::proto::v1::DataType::BoolArray), + Ok(root::proto::v1::datapoint::Value::BoolArray(value)) if value == root::proto::v1::BoolArray{values: vec![true, false, true]} + )); + + // Int8 + assert!(matches!( + try_into_data_value("100", root::proto::v1::DataType::Int8), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", root::proto::v1::DataType::Int8), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -100 + )); + assert!(try_into_data_value("300", root::proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-300", root::proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-100.1", root::proto::v1::DataType::Int8).is_err()); + + // Int16 + assert!(matches!( + try_into_data_value("100", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -100 + )); + assert!(matches!( + try_into_data_value("32000", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 32000 + )); + assert!(matches!( + try_into_data_value("-32000", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -32000 + )); + assert!(try_into_data_value("33000", root::proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-33000", root::proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-32000.1", root::proto::v1::DataType::Int16).is_err()); + } + #[cfg(feature = "feature_kuksa")]{ + // String + assert!(matches!( + try_into_data_value("test", root::proto::v1::DataType::String), + Ok(root::proto::v1::datapoint::Value::String(value)) if value == "test" + )); + + // StringArray + assert!(matches!( + try_into_data_value("[test, test2, test4]", root::proto::v1::DataType::StringArray), + Ok(root::proto::v1::datapoint::Value::StringArray(value)) if value == root::proto::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} + )); + + // Bool + assert!(matches!( + try_into_data_value("true", root::proto::v1::DataType::Boolean), + Ok(root::proto::v1::datapoint::Value::Bool(value)) if value + )); + + assert!(matches!( + try_into_data_value("false", root::proto::v1::DataType::Boolean), + Ok(root::proto::v1::datapoint::Value::Bool(value)) if !value + )); + assert!(try_into_data_value("truefalse", root::proto::v1::DataType::Boolean).is_err()); + // BoolArray + assert!(matches!( + try_into_data_value("[true, false, true]", root::proto::v1::DataType::BooleanArray), + Ok(root::proto::v1::datapoint::Value::BoolArray(value)) if value == root::proto::v1::BoolArray{values: vec![true, false, true]} + )); + + // Int8 + assert!(matches!( + try_into_data_value("100", root::proto::v1::DataType::Int8), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", root::proto::v1::DataType::Int8), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -100 + )); + assert!(try_into_data_value("300", root::proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-300", root::proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-100.1", root::proto::v1::DataType::Int8).is_err()); + + // Int16 + assert!(matches!( + try_into_data_value("100", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -100 + )); + assert!(matches!( + try_into_data_value("32000", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 32000 + )); + assert!(matches!( + try_into_data_value("-32000", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -32000 + )); + assert!(try_into_data_value("33000", root::proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-33000", root::proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-32000.1", root::proto::v1::DataType::Int16).is_err()); + } } #[test] fn test_entry_path_completion() { - let metadata = &[ - proto::v1::Metadata { - id: 1, - name: "Vehicle.Test.Test1".into(), - data_type: proto::v1::DataType::Bool.into(), - entry_type: proto::v1::EntryType::Sensor.into(), - change_type: proto::v1::ChangeType::OnChange.into(), - description: "".into(), - }, - proto::v1::Metadata { - id: 2, - name: "Vehicle.AnotherTest.AnotherTest1".into(), - data_type: proto::v1::DataType::Bool.into(), - entry_type: proto::v1::EntryType::Sensor.into(), - change_type: proto::v1::ChangeType::OnChange.into(), - description: "".into(), - }, - proto::v1::Metadata { - id: 3, - name: "Vehicle.AnotherTest.AnotherTest2".into(), - data_type: proto::v1::DataType::Bool.into(), - entry_type: proto::v1::EntryType::Sensor.into(), - change_type: proto::v1::ChangeType::OnChange.into(), - description: "".into(), - }, - ]; + let mut metadata = Vec::new(); + #[cfg(feature = "feature_sdv")]{ + metadata = [ + root::proto::v1::Metadata { + id: 1, + name: "Vehicle.Test.Test1".into(), + data_type: root::proto::v1::DataType::Bool.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + change_type: root::proto::v1::ChangeType::OnChange.into(), + description: "".into(), + }, + root::proto::v1::Metadata { + id: 2, + name: "Vehicle.AnotherTest.AnotherTest1".into(), + data_type: root::proto::v1::DataType::Bool.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + change_type: root::proto::v1::ChangeType::OnChange.into(), + description: "".into(), + }, + root::proto::v1::Metadata { + id: 3, + name: "Vehicle.AnotherTest.AnotherTest2".into(), + data_type: root::proto::v1::DataType::Bool.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + change_type: root::proto::v1::ChangeType::OnChange.into(), + description: "".into(), + }, + ].to_vec(); + } + + #[cfg(feature = "feature_kuksa")]{ + metadata.push(root::proto::v1::DataEntry{ + path: "Vehicle.Test.Test1".into() , + value: None, + actuator_target: None, + metadata: Some(root::proto::v1::Metadata { + data_type: root::proto::v1::DataType::Boolean.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + })}); + metadata.push(root::proto::v1::DataEntry{ + path: "Vehicle.Test.AnotherTest1".into() , + value: None, + actuator_target: None, + metadata: Some(root::proto::v1::Metadata { + data_type: root::proto::v1::DataType::Boolean.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + })}); + metadata.push(root::proto::v1::DataEntry{ + path: "Vehicle.Test.AnotherTest2".into() , + value: None, + actuator_target: None, + metadata: Some(root::proto::v1::Metadata { + data_type: root::proto::v1::DataType::Boolean.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + })}); + } + - let completer = CliCompleter::from_metadata(metadata); + let completer = CliCompleter::from_metadata(&metadata); assert_eq!(completer.paths.children.len(), 1); assert_eq!(completer.paths.children["vehicle"].children.len(), 2); From 906a6cbe211cef1d225be0160d6562cf0911f7e8 Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Wed, 27 Sep 2023 11:35:06 +0200 Subject: [PATCH 03/14] Fix lint errors --- kuksa_databroker/databroker-cli/src/main.rs | 653 ++++++++++++-------- kuksa_databroker/lib/common.rs | 15 +- kuksa_databroker/lib/kuksa/src/client.rs | 89 +-- kuksa_databroker/lib/sdv/src/client.rs | 8 +- 4 files changed, 473 insertions(+), 292 deletions(-) diff --git a/kuksa_databroker/databroker-cli/src/main.rs b/kuksa_databroker/databroker-cli/src/main.rs index 743790fdc..665887daa 100644 --- a/kuksa_databroker/databroker-cli/src/main.rs +++ b/kuksa_databroker/databroker-cli/src/main.rs @@ -35,7 +35,11 @@ const CLI_COMMANDS: &[(&str, &str, &str)] = &[ ("connect", "[URI]", "Connect to server"), ("get", " [[PATH] ...]", "Get signal value(s)"), ("set", " ", "Set actuator signal"), - ("subscribe", "", "Subscribe to signals with QUERY, if you use kuksa feature comma separated list"), + ( + "subscribe", + "", + "Subscribe to signals with QUERY, if you use kuksa feature comma separated list", + ), ("feed", " ", "Publish signal value"), ( "metadata", @@ -110,7 +114,7 @@ async fn main() -> Result<(), Box> { println!("Using kuksa"); properties = Vec::::new(); } - + let mut subscription_nbr = 1; let completer = CliCompleter::new(); @@ -127,11 +131,13 @@ async fn main() -> Result<(), Box> { let mut basic_client = common::Client::new(to_uri(cli.server)?); let mut client; - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { client = root::SDVClient::new(basic_client); } - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { client = root::KuksaClient::new(basic_client); } @@ -177,7 +183,8 @@ async fn main() -> Result<(), Box> { match cli.command { Some(Commands::Get { paths }) => { - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { match client.get_datapoints(paths).await { Ok(datapoints) => { for (name, datapoint) in datapoints { @@ -189,14 +196,14 @@ async fn main() -> Result<(), Box> { } } } - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { match client.get_current_values(paths).await { Ok(data_entries) => { for entry in data_entries { if let Some(val) = entry.value { println!("{}: {}", entry.path, DisplayDatapoint(val),); - } - else{ + } else { println!("{}: NotAvailable", entry.path); } } @@ -218,9 +225,13 @@ async fn main() -> Result<(), Box> { match client.basic_client.try_connect().await { Ok(()) => { - print_info(format!("Successfully connected to {}", client.basic_client.get_uri()))?; + print_info(format!( + "Successfully connected to {}", + client.basic_client.get_uri() + ))?; let mut pattern = vec![]; - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { pattern = vec!["**"]; } match client.get_metadata(pattern).await { @@ -260,15 +271,16 @@ async fn main() -> Result<(), Box> { "get" => { interface.add_history_unique(line.clone()); - if args.is_empty() { - print_usage(cmd); - continue; - } - let paths = args - .split_whitespace() - .map(|path| path.to_owned()) - .collect(); - #[cfg(feature = "feature_sdv")]{ + if args.is_empty() { + print_usage(cmd); + continue; + } + let paths = args + .split_whitespace() + .map(|path| path.to_owned()) + .collect(); + #[cfg(feature = "feature_sdv")] + { match client.get_datapoints(paths).await { Ok(datapoints) => { print_resp_ok(cmd)?; @@ -284,15 +296,19 @@ async fn main() -> Result<(), Box> { } } } - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { match client.get_current_values(paths).await { Ok(data_entries) => { print_resp_ok(cmd)?; for entry in data_entries { - if let Some(val) = entry.value{ - println!("{}: {}", entry.path, DisplayDatapoint(val),); - } - else{ + if let Some(val) = entry.value { + println!( + "{}: {}", + entry.path, + DisplayDatapoint(val), + ); + } else { println!("{}: NotAvailable", entry.path); } } @@ -384,8 +400,9 @@ async fn main() -> Result<(), Box> { print_usage(cmd); continue; } - - #[cfg(feature = "feature_sdv")]{ + + #[cfg(feature = "feature_sdv")] + { let datapoint_metadata = { let mut datapoint_metadata = None; for metadata in properties.iter() { @@ -406,17 +423,21 @@ async fn main() -> Result<(), Box> { if let Some(metadata) = datapoint_metadata { let data_value = try_into_data_value( value, - root::proto::v1::DataType::from_i32(metadata.data_type).unwrap(), + root::proto::v1::DataType::from_i32(metadata.data_type) + .unwrap(), ); if data_value.is_err() { println!( "Could not parse \"{value}\" as {:?}", - root::proto::v1::DataType::from_i32(metadata.data_type).unwrap() + root::proto::v1::DataType::from_i32(metadata.data_type) + .unwrap() ); continue; } - if metadata.entry_type != root::proto::v1::EntryType::Actuator.into() { + if metadata.entry_type + != root::proto::v1::EntryType::Actuator.into() + { print_error( cmd, format!("{} is not an actuator.", metadata.name), @@ -435,20 +456,23 @@ async fn main() -> Result<(), Box> { value: Some(data_value.unwrap()), }, )]); - + match client.set_datapoints(datapoints).await { Ok(message) => { if message.errors.is_empty() { print_resp_ok(cmd)?; } else { for (id, error) in message.errors { - match root::proto::v1::DatapointError::from_i32(error) { + match root::proto::v1::DatapointError::from_i32( + error, + ) { Some(error) => { print_resp_ok(cmd)?; println!( "Error setting {}: {}", id, - Color::Red.paint(format!("{error:?}")), + Color::Red + .paint(format!("{error:?}")), ); } None => print_resp_ok_fmt( @@ -462,12 +486,16 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Status(status)) => { print_resp_err(cmd, &status)? } - Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, + Err(common::ClientError::Connection(msg)) => { + print_error(cmd, msg)? + } } } } - #[cfg(feature = "feature_kuksa")]{ - let datapoint_entries = match client.get_metadata(vec![path]).await { + #[cfg(feature = "feature_kuksa")] + { + let datapoint_entries = match client.get_metadata(vec![path]).await + { Ok(data_entries) => Some(data_entries), Err(common::ClientError::Status(status)) => { print_resp_err("metadata", &status)?; @@ -478,23 +506,31 @@ async fn main() -> Result<(), Box> { None } }; - + if let Some(entries) = datapoint_entries { - for entry in entries{ + for entry in entries { if let Some(metadata) = entry.metadata { let data_value = try_into_data_value( value, - root::proto::v1::DataType::from_i32(metadata.data_type).unwrap(), + root::proto::v1::DataType::from_i32( + metadata.data_type, + ) + .unwrap(), ); if data_value.is_err() { println!( "Could not parse \"{value}\" as {:?}", - root::proto::v1::DataType::from_i32(metadata.data_type).unwrap() + root::proto::v1::DataType::from_i32( + metadata.data_type + ) + .unwrap() ); continue; } - - if metadata.entry_type != root::proto::v1::EntryType::Actuator.into() { + + if metadata.entry_type + != root::proto::v1::EntryType::Actuator.into() + { print_error( cmd, format!("{} is not an actuator.", path), @@ -504,7 +540,7 @@ async fn main() -> Result<(), Box> { )?; continue; } - + let ts = Timestamp::from(SystemTime::now()); let datapoints = HashMap::from([( path.to_string().clone(), @@ -526,28 +562,32 @@ async fn main() -> Result<(), Box> { println!( "Error setting {}: {}", error.path, - Color::Red.paint(format!("{error_mes:?}")), + Color::Red.paint(format!( + "{error_mes:?}" + )), ); } } - match message.error{ - Some(error) => {print_resp_ok_fmt( + match message.error { + Some(error) => print_resp_ok_fmt( cmd, format_args!("Error {error:?}"), - )?}, - None => () + )?, + None => (), }; } } Err(common::ClientError::Status(status)) => { print_resp_err(cmd, &status)? } - Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, + Err(common::ClientError::Connection(msg)) => { + print_error(cmd, msg)? + } } } } } - } + } } "feed" => { interface.add_history_unique(line.clone()); @@ -559,7 +599,8 @@ async fn main() -> Result<(), Box> { continue; } - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { let datapoint_metadata = { let mut datapoint_metadata = None; for metadata in properties.iter() { @@ -580,13 +621,15 @@ async fn main() -> Result<(), Box> { if let Some(metadata) = datapoint_metadata { let data_value = try_into_data_value( value, - root::proto::v1::DataType::from_i32(metadata.data_type).unwrap(), + root::proto::v1::DataType::from_i32(metadata.data_type) + .unwrap(), ); if data_value.is_err() { println!( "Could not parse \"{}\" as {:?}", value, - root::proto::v1::DataType::from_i32(metadata.data_type).unwrap() + root::proto::v1::DataType::from_i32(metadata.data_type) + .unwrap() ); continue; } @@ -599,7 +642,6 @@ async fn main() -> Result<(), Box> { }, )]); - match client.update_datapoints(datapoints).await { Ok(message) => { if message.errors.is_empty() { @@ -629,12 +671,16 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Status(status)) => { print_resp_err(cmd, &status)? } - Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, + Err(common::ClientError::Connection(msg)) => { + print_error(cmd, msg)? + } } } } - #[cfg(feature = "feature_kuksa")]{ - let datapoint_entries = match client.get_metadata(vec![path]).await { + #[cfg(feature = "feature_kuksa")] + { + let datapoint_entries = match client.get_metadata(vec![path]).await + { Ok(data_entries) => Some(data_entries), Err(common::ClientError::Status(status)) => { print_resp_err("metadata", &status)?; @@ -645,19 +691,25 @@ async fn main() -> Result<(), Box> { None } }; - + if let Some(entries) = datapoint_entries { - for entry in entries{ + for entry in entries { if let Some(metadata) = entry.metadata { let data_value = try_into_data_value( value, - root::proto::v1::DataType::from_i32(metadata.data_type).unwrap(), + root::proto::v1::DataType::from_i32( + metadata.data_type, + ) + .unwrap(), ); if data_value.is_err() { println!( "Could not parse \"{}\" as {:?}", value, - root::proto::v1::DataType::from_i32(metadata.data_type).unwrap() + root::proto::v1::DataType::from_i32( + metadata.data_type + ) + .unwrap() ); continue; } @@ -682,23 +734,27 @@ async fn main() -> Result<(), Box> { println!( "Error setting {}: {}", error.path, - Color::Red.paint(format!("{error_mes:?}")), + Color::Red.paint(format!( + "{error_mes:?}" + )), ); } } - match message.error{ - Some(error) => {print_resp_ok_fmt( + match message.error { + Some(error) => print_resp_ok_fmt( cmd, format_args!("Error {error:?}"), - )?}, - None => () + )?, + None => (), }; } } Err(common::ClientError::Status(status)) => { print_resp_err(cmd, &status)? } - Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, + Err(common::ClientError::Connection(msg)) => { + print_error(cmd, msg)? + } } } } @@ -712,13 +768,15 @@ async fn main() -> Result<(), Box> { print_usage(cmd); continue; } - + let mut input; - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { input = args.to_owned(); } - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { input = args.split_whitespace().collect::>(); } @@ -740,7 +798,8 @@ async fn main() -> Result<(), Box> { use std::fmt::Write; let mut output = String::new(); let mut first_line = true; - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { for (name, value) in resp.fields { if first_line { first_line = false; @@ -768,7 +827,8 @@ async fn main() -> Result<(), Box> { } write!(iface, "{output}").unwrap(); } - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { for update in resp.updates { if first_line { first_line = false; @@ -786,8 +846,9 @@ async fn main() -> Result<(), Box> { ) .unwrap(); } - if let Some(entry) = update.entry{ - if let Some(value) = entry.value{ + if let Some(entry) = update.entry { + if let Some(value) = entry.value + { writeln!( output, "{}: {}", @@ -836,7 +897,9 @@ async fn main() -> Result<(), Box> { )?; subscription_nbr += 1; } - Err(common::ClientError::Status(status)) => print_resp_err(cmd, &status)?, + Err(common::ClientError::Status(status)) => { + print_resp_err(cmd, &status)? + } Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, } } @@ -858,7 +921,11 @@ async fn main() -> Result<(), Box> { } else { match to_uri(args) { Ok(valid_uri) => { - match client.basic_client.try_connect_to(valid_uri).await { + match client + .basic_client + .try_connect_to(valid_uri) + .await + { Ok(()) => { print_info(format!( "[{cmd}] Successfully connected to {}", @@ -900,15 +967,16 @@ async fn main() -> Result<(), Box> { interface.add_history_unique(line.clone()); let paths = args.split_whitespace().collect::>(); - - #[cfg(feature = "feature_sdv")]{ + + #[cfg(feature = "feature_sdv")] + { match client.get_metadata(vec![]).await { Ok(mut metadata) => { metadata.sort_by(|a, b| a.name.cmp(&b.name)); properties = metadata; - interface.set_completer(Arc::new(CliCompleter::from_metadata( - &properties, - ))); + interface.set_completer(Arc::new( + CliCompleter::from_metadata(&properties), + )); print_resp_ok(cmd)?; } Err(common::ClientError::Status(status)) => { @@ -958,17 +1026,22 @@ async fn main() -> Result<(), Box> { println!( "{: Result<(), Box> { ); } else { let name = entry.path.clone(); - println!( - "No entry metadata for {name}" - ); + println!("No entry metadata for {name}"); } } } @@ -1018,9 +1089,8 @@ async fn main() -> Result<(), Box> { print_error(cmd, msg)?; continue; } - } - } + } } } "quit" | "exit" => { @@ -1205,55 +1275,55 @@ impl CliCompleter { } #[cfg(feature = "feature_sdv")] - fn from_metadata(metadata: &[root::proto::v1::Metadata]) -> CliCompleter { - let mut root = PathPart::new(); - for entry in metadata { - let mut parent = &mut root; - let parts = entry.name.split('.'); - for part in parts { - let full_path = match parent.full_path.as_str() { - "" => part.to_owned(), - _ => format!("{}.{}", parent.full_path, part), - }; - let entry = parent - .children - .entry(part.to_lowercase()) - .or_insert(PathPart { - rel_path: part.to_owned(), - full_path, - children: HashMap::new(), - }); - - parent = entry; - } + fn from_metadata(metadata: &[root::proto::v1::Metadata]) -> CliCompleter { + let mut root = PathPart::new(); + for entry in metadata { + let mut parent = &mut root; + let parts = entry.name.split('.'); + for part in parts { + let full_path = match parent.full_path.as_str() { + "" => part.to_owned(), + _ => format!("{}.{}", parent.full_path, part), + }; + let entry = parent + .children + .entry(part.to_lowercase()) + .or_insert(PathPart { + rel_path: part.to_owned(), + full_path, + children: HashMap::new(), + }); + + parent = entry; } - CliCompleter { paths: root } } + CliCompleter { paths: root } + } #[cfg(feature = "feature_kuksa")] - fn from_metadata(entries: &Vec) -> CliCompleter { - let mut root = PathPart::new(); - for entry in entries { - let mut parent = &mut root; - let parts = entry.path.split('.'); - for part in parts { - let full_path = match parent.full_path.as_str() { - "" => part.to_owned(), - _ => format!("{}.{}", parent.full_path, part), - }; - let entry = parent - .children - .entry(part.to_lowercase()) - .or_insert(PathPart { - rel_path: part.to_owned(), - full_path, - children: HashMap::new(), - }); - - parent = entry; - } + fn from_metadata(entries: &Vec) -> CliCompleter { + let mut root = PathPart::new(); + for entry in entries { + let mut parent = &mut root; + let parts = entry.path.split('.'); + for part in parts { + let full_path = match parent.full_path.as_str() { + "" => part.to_owned(), + _ => format!("{}.{}", parent.full_path, part), + }; + let entry = parent + .children + .entry(part.to_lowercase()) + .or_insert(PathPart { + rel_path: part.to_owned(), + full_path, + children: HashMap::new(), + }); + + parent = entry; } - CliCompleter { paths: root } } + CliCompleter { paths: root } + } fn complete_entry_path(&self, word: &str) -> Option> { if !self.paths.children.is_empty() { @@ -1553,34 +1623,68 @@ where impl fmt::Display for DisplayDatapoint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { match &self.0.value { Some(value) => match value { - root::proto::v1::datapoint::Value::BoolValue(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::BoolValue(value) => { + f.pad(&format!("{value}")) + } root::proto::v1::datapoint::Value::FailureValue(failure) => f.pad(&format!( "( {:?} )", root::proto::v1::datapoint::Failure::from_i32(*failure).unwrap() )), - root::proto::v1::datapoint::Value::Int32Value(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Int64Value(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Uint32Value(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Uint64Value(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::FloatValue(value) => f.pad(&format!("{value:.2}")), - root::proto::v1::datapoint::Value::DoubleValue(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::StringValue(value) => f.pad(&format!("'{value}'")), - root::proto::v1::datapoint::Value::StringArray(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::BoolArray(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::Int32Array(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::Int64Array(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::Uint32Array(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::Uint64Array(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::FloatArray(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::DoubleArray(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::Int32Value(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::Int64Value(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::Uint32Value(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::Uint64Value(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::FloatValue(value) => { + f.pad(&format!("{value:.2}")) + } + root::proto::v1::datapoint::Value::DoubleValue(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::StringValue(value) => { + f.pad(&format!("'{value}'")) + } + root::proto::v1::datapoint::Value::StringArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::BoolArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Int32Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Int64Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Uint32Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Uint64Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::FloatArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::DoubleArray(array) => { + display_array(f, &array.values) + } }, None => f.pad("None"), } } - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { match &self.0.value { Some(value) => match value { root::proto::v1::datapoint::Value::Bool(value) => f.pad(&format!("{value}")), @@ -1588,17 +1692,37 @@ impl fmt::Display for DisplayDatapoint { root::proto::v1::datapoint::Value::Int64(value) => f.pad(&format!("{value}")), root::proto::v1::datapoint::Value::Uint32(value) => f.pad(&format!("{value}")), root::proto::v1::datapoint::Value::Uint64(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Float(value) => f.pad(&format!("{value:.2}")), + root::proto::v1::datapoint::Value::Float(value) => { + f.pad(&format!("{value:.2}")) + } root::proto::v1::datapoint::Value::Double(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::String(value) => f.pad(&format!("'{value}'")), - root::proto::v1::datapoint::Value::StringArray(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::BoolArray(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::Int32Array(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::Int64Array(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::Uint32Array(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::Uint64Array(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::FloatArray(array) => display_array(f, &array.values), - root::proto::v1::datapoint::Value::DoubleArray(array) => display_array(f, &array.values), + root::proto::v1::datapoint::Value::String(value) => { + f.pad(&format!("'{value}'")) + } + root::proto::v1::datapoint::Value::StringArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::BoolArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Int32Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Int64Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Uint32Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Uint64Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::FloatArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::DoubleArray(array) => { + display_array(f, &array.values) + } }, None => f.pad("None"), } @@ -1692,29 +1816,32 @@ fn try_into_data_value( data_type: root::proto::v1::DataType, ) -> Result { if input == "NotAvailable" { - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { return Ok(root::proto::v1::datapoint::Value::FailureValue( root::proto::v1::datapoint::Failure::NotAvailable as i32, )); } - #[cfg(feature = "feature_kuksa")]{ - return Ok(root::proto::v1::datapoint::Value::String( - input.to_string(), - )); + #[cfg(feature = "feature_kuksa")] + { + return Ok(root::proto::v1::datapoint::Value::String(input.to_string())); } } - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { match data_type { - root::proto::v1::DataType::String => { - Ok(root::proto::v1::datapoint::Value::StringValue(input.to_owned())) + root::proto::v1::DataType::String => Ok( + root::proto::v1::datapoint::Value::StringValue(input.to_owned()), + ), + root::proto::v1::DataType::StringArray => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( + root::proto::v1::StringArray { values: value }, + )), + Err(err) => Err(err), + } } - root::proto::v1::DataType::StringArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( - root::proto::v1::StringArray { values: value }, - )), - Err(err) => Err(err), - }, root::proto::v1::DataType::Bool => match input.parse::() { Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolValue(value)), Err(_) => Err(ParseError {}), @@ -1779,32 +1906,38 @@ fn try_into_data_value( Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value as u32)), Err(_) => Err(ParseError {}), }, - root::proto::v1::DataType::Uint16Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, + root::proto::v1::DataType::Uint16Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } root::proto::v1::DataType::Uint32 => match input.parse::() { Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value)), Err(_) => Err(ParseError {}), }, - root::proto::v1::DataType::Uint32Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, + root::proto::v1::DataType::Uint32Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } root::proto::v1::DataType::Uint64 => match input.parse::() { Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Value(value)), Err(_) => Err(ParseError {}), }, - root::proto::v1::DataType::Uint64Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( - root::proto::v1::Uint64Array { values: value }, - )), - Err(err) => Err(err), - }, + root::proto::v1::DataType::Uint64Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( + root::proto::v1::Uint64Array { values: value }, + )), + Err(err) => Err(err), + } + } root::proto::v1::DataType::Float => match input.parse::() { Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatValue(value)), Err(_) => Err(ParseError {}), @@ -1819,31 +1952,37 @@ fn try_into_data_value( Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleValue(value)), Err(_) => Err(ParseError {}), }, - root::proto::v1::DataType::DoubleArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( - root::proto::v1::DoubleArray { values: value }, - )), - Err(err) => Err(err), - }, + root::proto::v1::DataType::DoubleArray => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( + root::proto::v1::DoubleArray { values: value }, + )), + Err(err) => Err(err), + } + } _ => Err(ParseError {}), } } - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { match data_type { root::proto::v1::DataType::String => { Ok(root::proto::v1::datapoint::Value::String(input.to_owned())) } - root::proto::v1::DataType::StringArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( - root::proto::v1::StringArray { values: value }, - )), - Err(err) => Err(err), - }, + root::proto::v1::DataType::StringArray => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( + root::proto::v1::StringArray { values: value }, + )), + Err(err) => Err(err), + } + } root::proto::v1::DataType::Boolean => match input.parse::() { Ok(value) => Ok(root::proto::v1::datapoint::Value::Bool(value)), Err(_) => Err(ParseError {}), }, - root::proto::v1::DataType::BooleanArray => match get_array_from_input(input.to_owned()) { + root::proto::v1::DataType::BooleanArray => match get_array_from_input(input.to_owned()) + { Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolArray( root::proto::v1::BoolArray { values: value }, )), @@ -1903,32 +2042,38 @@ fn try_into_data_value( Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value as u32)), Err(_) => Err(ParseError {}), }, - root::proto::v1::DataType::Uint16Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, + root::proto::v1::DataType::Uint16Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } root::proto::v1::DataType::Uint32 => match input.parse::() { Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value)), Err(_) => Err(ParseError {}), }, - root::proto::v1::DataType::Uint32Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, + root::proto::v1::DataType::Uint32Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } root::proto::v1::DataType::Uint64 => match input.parse::() { Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64(value)), Err(_) => Err(ParseError {}), }, - root::proto::v1::DataType::Uint64Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( - root::proto::v1::Uint64Array { values: value }, - )), - Err(err) => Err(err), - }, + root::proto::v1::DataType::Uint64Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( + root::proto::v1::Uint64Array { values: value }, + )), + Err(err) => Err(err), + } + } root::proto::v1::DataType::Float => match input.parse::() { Ok(value) => Ok(root::proto::v1::datapoint::Value::Float(value)), Err(_) => Err(ParseError {}), @@ -1943,16 +2088,17 @@ fn try_into_data_value( Ok(value) => Ok(root::proto::v1::datapoint::Value::Double(value)), Err(_) => Err(ParseError {}), }, - root::proto::v1::DataType::DoubleArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( - root::proto::v1::DoubleArray { values: value }, - )), - Err(err) => Err(err), - }, + root::proto::v1::DataType::DoubleArray => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( + root::proto::v1::DoubleArray { values: value }, + )), + Err(err) => Err(err), + } + } _ => Err(ParseError {}), } } - } #[cfg(test)] @@ -1962,7 +2108,8 @@ mod test { #[test] fn test_parse_values() { - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { // String assert!(matches!( try_into_data_value("test", root::proto::v1::DataType::String), @@ -2026,7 +2173,8 @@ mod test { assert!(try_into_data_value("-33000", root::proto::v1::DataType::Int16).is_err()); assert!(try_into_data_value("-32000.1", root::proto::v1::DataType::Int16).is_err()); } - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { // String assert!(matches!( try_into_data_value("test", root::proto::v1::DataType::String), @@ -2095,7 +2243,8 @@ mod test { #[test] fn test_entry_path_completion() { let mut metadata = Vec::new(); - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { metadata = [ root::proto::v1::Metadata { id: 1, @@ -2121,12 +2270,14 @@ mod test { change_type: root::proto::v1::ChangeType::OnChange.into(), description: "".into(), }, - ].to_vec(); + ] + .to_vec(); } - #[cfg(feature = "feature_kuksa")]{ - metadata.push(root::proto::v1::DataEntry{ - path: "Vehicle.Test.Test1".into() , + #[cfg(feature = "feature_kuksa")] + { + metadata.push(root::proto::v1::DataEntry { + path: "Vehicle.Test.Test1".into(), value: None, actuator_target: None, metadata: Some(root::proto::v1::Metadata { @@ -2138,9 +2289,10 @@ mod test { value_restriction: None, entry_specific: None, description: Some("".to_string()), - })}); - metadata.push(root::proto::v1::DataEntry{ - path: "Vehicle.Test.AnotherTest1".into() , + }), + }); + metadata.push(root::proto::v1::DataEntry { + path: "Vehicle.Test.AnotherTest1".into(), value: None, actuator_target: None, metadata: Some(root::proto::v1::Metadata { @@ -2152,9 +2304,10 @@ mod test { value_restriction: None, entry_specific: None, description: Some("".to_string()), - })}); - metadata.push(root::proto::v1::DataEntry{ - path: "Vehicle.Test.AnotherTest2".into() , + }), + }); + metadata.push(root::proto::v1::DataEntry { + path: "Vehicle.Test.AnotherTest2".into(), value: None, actuator_target: None, metadata: Some(root::proto::v1::Metadata { @@ -2166,10 +2319,10 @@ mod test { value_restriction: None, entry_specific: None, description: Some("".to_string()), - })}); + }), + }); } - let completer = CliCompleter::from_metadata(&metadata); assert_eq!(completer.paths.children.len(), 1); diff --git a/kuksa_databroker/lib/common.rs b/kuksa_databroker/lib/common.rs index 4bcc3e47c..74f0d99b4 100644 --- a/kuksa_databroker/lib/common.rs +++ b/kuksa_databroker/lib/common.rs @@ -1,3 +1,16 @@ +/******************************************************************************** +* Copyright (c) 2023 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License 2.0 which is available at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + use std::convert::TryFrom; use http::Uri; @@ -204,4 +217,4 @@ impl Client { Ok(req) } } -} \ No newline at end of file +} diff --git a/kuksa_databroker/lib/kuksa/src/client.rs b/kuksa_databroker/lib/kuksa/src/client.rs index 0f420f2e7..c78d5c0a3 100644 --- a/kuksa_databroker/lib/kuksa/src/client.rs +++ b/kuksa_databroker/lib/kuksa/src/client.rs @@ -15,14 +15,14 @@ use std::collections::HashMap; use databroker_proto::kuksa::val::{self as proto, v1::DataEntry}; -use common::{Client, ClientError,}; +use common::{Client, ClientError}; -pub struct KuksaClient{ - pub basic_client: Client +pub struct KuksaClient { + pub basic_client: Client, } -impl KuksaClient{ - pub fn new(basic_client: Client) -> Self{ +impl KuksaClient { + pub fn new(basic_client: Client) -> Self { KuksaClient { basic_client } } @@ -59,11 +59,10 @@ impl KuksaClient{ Ok(metadata_result) } - pub async fn get_current_values( &mut self, paths: Vec>, - ) -> Result, ClientError>{ + ) -> Result, ClientError> { let mut client = proto::v1::val_client::ValClient::with_interceptor( self.basic_client.get_channel().await?.clone(), self.basic_client.get_auth_interceptor(), @@ -95,7 +94,7 @@ impl KuksaClient{ pub async fn get_target_values( &mut self, paths: Vec>, - ) -> Result, ClientError>{ + ) -> Result, ClientError> { let mut client = proto::v1::val_client::ValClient::with_interceptor( self.basic_client.get_channel().await?.clone(), self.basic_client.get_auth_interceptor(), @@ -133,9 +132,9 @@ impl KuksaClient{ self.basic_client.get_auth_interceptor(), ); - let mut set_result = Vec::new(); + let mut set_result = Vec::new(); - for (path, datapoint) in datapoints{ + for (path, datapoint) in datapoints { let set_request = proto::v1::SetRequest { updates: vec![proto::v1::EntryUpdate { entry: Some(proto::v1::DataEntry { @@ -144,7 +143,10 @@ impl KuksaClient{ actuator_target: None, metadata: None, }), - fields: vec![proto::v1::Field::Value.into(), proto::v1::Field::Path.into()], + fields: vec![ + proto::v1::Field::Value.into(), + proto::v1::Field::Path.into(), + ], }], }; match client.set(set_request).await { @@ -154,7 +156,7 @@ impl KuksaClient{ Err(err) => return Err(ClientError::Status(err)), } } - + Ok(set_result) } @@ -167,9 +169,9 @@ impl KuksaClient{ self.basic_client.get_auth_interceptor(), ); - let mut set_result = Vec::new(); + let mut set_result = Vec::new(); - for (path, datapoint) in datapoints{ + for (path, datapoint) in datapoints { let set_request = proto::v1::SetRequest { updates: vec![proto::v1::EntryUpdate { entry: Some(proto::v1::DataEntry { @@ -178,7 +180,10 @@ impl KuksaClient{ actuator_target: Some(datapoint), metadata: None, }), - fields: vec![proto::v1::Field::ActuatorTarget.into(), proto::v1::Field::Path.into()], + fields: vec![ + proto::v1::Field::ActuatorTarget.into(), + proto::v1::Field::Path.into(), + ], }], }; match client.set(set_request).await { @@ -188,7 +193,7 @@ impl KuksaClient{ Err(err) => return Err(ClientError::Status(err)), } } - + Ok(set_result) } @@ -201,9 +206,9 @@ impl KuksaClient{ self.basic_client.get_auth_interceptor(), ); - let mut set_result = Vec::new(); + let mut set_result = Vec::new(); - for (path, metadata) in metadatas{ + for (path, metadata) in metadatas { let set_request = proto::v1::SetRequest { updates: vec![proto::v1::EntryUpdate { entry: Some(proto::v1::DataEntry { @@ -212,7 +217,10 @@ impl KuksaClient{ actuator_target: None, metadata: Some(metadata), }), - fields: vec![proto::v1::Field::Metadata.into(), proto::v1::Field::Path.into()], + fields: vec![ + proto::v1::Field::Metadata.into(), + proto::v1::Field::Path.into(), + ], }], }; match client.set(set_request).await { @@ -222,7 +230,7 @@ impl KuksaClient{ Err(err) => return Err(ClientError::Status(err)), } } - + Ok(set_result) } @@ -236,13 +244,15 @@ impl KuksaClient{ ); let mut entries = Vec::new(); - for path in paths{ - entries.push( - proto::v1::SubscribeEntry{path: path.to_string(), view: proto::v1::View::CurrentValue.into() ,fields: vec![proto::v1::Field::Value.into()] } - ) + for path in paths { + entries.push(proto::v1::SubscribeEntry { + path: path.to_string(), + view: proto::v1::View::CurrentValue.into(), + fields: vec![proto::v1::Field::Value.into()], + }) } - let req = proto::v1::SubscribeRequest{entries: entries}; + let req = proto::v1::SubscribeRequest { entries: entries }; match client.subscribe(req).await { Ok(response) => Ok(response.into_inner()), @@ -251,7 +261,8 @@ impl KuksaClient{ } //masking subscribe curent values with subscribe - pub async fn subscribe(&mut self, + pub async fn subscribe( + &mut self, paths: Vec<&str>, ) -> Result, ClientError> { return self.subscribe_current_values(paths).await; @@ -266,13 +277,15 @@ impl KuksaClient{ self.basic_client.get_auth_interceptor(), ); let mut entries = Vec::new(); - for path in paths{ - entries.push( - proto::v1::SubscribeEntry{path: path.to_string(), view: proto::v1::View::TargetValue.into() ,fields: vec![proto::v1::Field::ActuatorTarget.into()] } - ) + for path in paths { + entries.push(proto::v1::SubscribeEntry { + path: path.to_string(), + view: proto::v1::View::TargetValue.into(), + fields: vec![proto::v1::Field::ActuatorTarget.into()], + }) } - let req = proto::v1::SubscribeRequest{entries: entries}; + let req = proto::v1::SubscribeRequest { entries: entries }; match client.subscribe(req).await { Ok(response) => Ok(response.into_inner()), @@ -289,17 +302,19 @@ impl KuksaClient{ self.basic_client.get_auth_interceptor(), ); let mut entries = Vec::new(); - for path in paths{ - entries.push( - proto::v1::SubscribeEntry{path: path.to_string(), view: proto::v1::View::Metadata.into() ,fields: vec![proto::v1::Field::Metadata.into()] } - ) + for path in paths { + entries.push(proto::v1::SubscribeEntry { + path: path.to_string(), + view: proto::v1::View::Metadata.into(), + fields: vec![proto::v1::Field::Metadata.into()], + }) } - let req = proto::v1::SubscribeRequest{entries: entries}; + let req = proto::v1::SubscribeRequest { entries: entries }; match client.subscribe(req).await { Ok(response) => Ok(response.into_inner()), Err(err) => Err(ClientError::Status(err)), } } -} \ No newline at end of file +} diff --git a/kuksa_databroker/lib/sdv/src/client.rs b/kuksa_databroker/lib/sdv/src/client.rs index 4f9a72ec2..5d57c6a8b 100644 --- a/kuksa_databroker/lib/sdv/src/client.rs +++ b/kuksa_databroker/lib/sdv/src/client.rs @@ -13,15 +13,15 @@ use std::collections::HashMap; -use databroker_proto::sdv::databroker as proto; use common::{Client, ClientError}; +use databroker_proto::sdv::databroker as proto; -pub struct SDVClient{ +pub struct SDVClient { pub basic_client: Client, } -impl SDVClient{ - pub fn new(basic_client: Client) -> Self{ +impl SDVClient { + pub fn new(basic_client: Client) -> Self { SDVClient { basic_client } } From 26024c7621326e2f5ea27b6b6c2512eda1865201 Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Fri, 29 Sep 2023 13:50:23 +0200 Subject: [PATCH 04/14] Fix custom error handling and autocompletion for subscribe --- kuksa_databroker/databroker-cli/src/main.rs | 139 ++++++++++++-------- kuksa_databroker/lib/common.rs | 10 ++ kuksa_databroker/lib/kuksa/src/client.rs | 36 ++++- 3 files changed, 129 insertions(+), 56 deletions(-) diff --git a/kuksa_databroker/databroker-cli/src/main.rs b/kuksa_databroker/databroker-cli/src/main.rs index 665887daa..94f5fc913 100644 --- a/kuksa_databroker/databroker-cli/src/main.rs +++ b/kuksa_databroker/databroker-cli/src/main.rs @@ -103,6 +103,7 @@ enum Commands { #[tokio::main] async fn main() -> Result<(), Box> { + #[allow(unused_assignments)] let mut properties = Vec::new(); #[cfg(feature = "feature_sdv")] { @@ -129,7 +130,7 @@ async fn main() -> Result<(), Box> { let cli = Cli::parse(); - let mut basic_client = common::Client::new(to_uri(cli.server)?); + let basic_client = common::Client::new(to_uri(cli.server)?); let mut client; #[cfg(feature = "feature_sdv")] { @@ -229,6 +230,7 @@ async fn main() -> Result<(), Box> { "Successfully connected to {}", client.basic_client.get_uri() ))?; + #[allow(unused_mut)] let mut pattern = vec![]; #[cfg(feature = "feature_kuksa")] { @@ -246,6 +248,9 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Connection(msg)) => { print_error("metadata", msg)?; } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt("metadata", format_args!("Error {msg:?}"))?; + } } } Err(err) => { @@ -294,6 +299,9 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Connection(msg)) => { print_error(cmd, msg)?; } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } } } #[cfg(feature = "feature_kuksa")] @@ -319,6 +327,9 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Connection(msg)) => { print_error(cmd, msg)?; } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } } } } @@ -346,6 +357,9 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Connection(msg)) => { print_error("metadata", msg)?; } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt("metadata", format_args!("Error {msg:?}"))?; + } } } Err(err) => print_error(cmd, &format!("Malformed token: {err}"))?, @@ -377,6 +391,9 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Connection(msg)) => { print_error("metadata", msg)?; } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } } } Err(err) => { @@ -489,6 +506,9 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Connection(msg)) => { print_error(cmd, msg)? } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } } } } @@ -505,6 +525,10 @@ async fn main() -> Result<(), Box> { print_error("metadata", msg)?; None } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + None + } }; if let Some(entries) = datapoint_entries { @@ -552,30 +576,7 @@ async fn main() -> Result<(), Box> { match client.set_current_values(datapoints).await { Ok(res) => { - for message in res { - if message.errors.is_empty() { - print_resp_ok(cmd)?; - } else { - for error in message.errors { - print_resp_ok(cmd)?; - let error_mes = error.error; - println!( - "Error setting {}: {}", - error.path, - Color::Red.paint(format!( - "{error_mes:?}" - )), - ); - } - } - match message.error { - Some(error) => print_resp_ok_fmt( - cmd, - format_args!("Error {error:?}"), - )?, - None => (), - }; - } + print_resp_ok(cmd)? } Err(common::ClientError::Status(status)) => { print_resp_err(cmd, &status)? @@ -583,6 +584,9 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Connection(msg)) => { print_error(cmd, msg)? } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))? + } } } } @@ -674,6 +678,9 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Connection(msg)) => { print_error(cmd, msg)? } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } } } } @@ -690,6 +697,10 @@ async fn main() -> Result<(), Box> { print_error("metadata", msg)?; None } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + None + } }; if let Some(entries) = datapoint_entries { @@ -724,30 +735,7 @@ async fn main() -> Result<(), Box> { match client.set_current_values(datapoints).await { Ok(res) => { - for message in res { - if message.errors.is_empty() { - print_resp_ok(cmd)?; - } else { - for error in message.errors { - print_resp_ok(cmd)?; - let error_mes = error.error; - println!( - "Error setting {}: {}", - error.path, - Color::Red.paint(format!( - "{error_mes:?}" - )), - ); - } - } - match message.error { - Some(error) => print_resp_ok_fmt( - cmd, - format_args!("Error {error:?}"), - )?, - None => (), - }; - } + print_resp_ok(cmd); } Err(common::ClientError::Status(status)) => { print_resp_err(cmd, &status)? @@ -755,6 +743,9 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Connection(msg)) => { print_error(cmd, msg)? } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } } } } @@ -769,6 +760,7 @@ async fn main() -> Result<(), Box> { continue; } + #[allow(unused_mut)] let mut input; #[cfg(feature = "feature_sdv")] @@ -901,6 +893,7 @@ async fn main() -> Result<(), Box> { print_resp_err(cmd, &status)? } Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, + Err(common::ClientError::Function(msg)) => print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?, } } "connect" => { @@ -959,6 +952,9 @@ async fn main() -> Result<(), Box> { Err(common::ClientError::Connection(msg)) => { print_error("metadata", msg)?; } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } } } }; @@ -987,6 +983,10 @@ async fn main() -> Result<(), Box> { print_error(cmd, msg)?; continue; } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + continue; + } } let mut filtered_metadata = Vec::new(); if paths.is_empty() { @@ -1089,6 +1089,9 @@ async fn main() -> Result<(), Box> { print_error(cmd, msg)?; continue; } + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } } } } @@ -1394,10 +1397,17 @@ impl CliCompleter { } fn set_connected_prompt(interface: &Arc>) { + let mut _text; + #[cfg(feature = "feature_sdv")]{ + _text = "sdv.databroker.v1" + } + #[cfg(feature = "feature_kuksa")]{ + _text = "kuksa.val.v1" + } let connected_prompt = format!( "\x01{prefix}\x02{text}\x01{suffix}\x02 > ", prefix = Color::Green.prefix(), - text = "sdv.databroker.v1", + text = _text, suffix = Color::Green.suffix() ); interface.set_prompt(&connected_prompt).unwrap(); @@ -1469,6 +1479,15 @@ fn print_resp_err(operation: impl AsRef, err: &tonic::Status) -> io::Result output.flush() } +fn print_resp_err_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { + let mut stderr = io::stderr().lock(); + let mut stdout = io::stdout().lock(); + write_resp_ok(&mut stderr, operation)?; + stdout.write_fmt(fmt)?; + stdout.write_all(b"\n")?; + stdout.flush() +} + fn print_resp_ok_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { let mut stderr = io::stderr().lock(); let mut stdout = io::stdout().lock(); @@ -1555,10 +1574,21 @@ impl Completer for CliCompleter { } } Some("get") | Some("metadata") => self.complete_entry_path(word), - Some("subscribe") => match words.next() { - None => Some(vec![Completion::simple("SELECT".to_owned())]), - Some(next) => { - if next == "SELECT" { + Some("subscribe") => { + #[cfg(feature = "feature_sdv")]{ + match words.next() { + None => Some(vec![Completion::simple("SELECT".to_owned())]), + Some(next) => { + if next == "SELECT" { + self.complete_entry_path(word) + } else { + None + } + } + } + } + #[cfg(feature = "feature_kuksa")]{ + if words.count() == 0 { self.complete_entry_path(word) } else { None @@ -2242,6 +2272,7 @@ mod test { #[test] fn test_entry_path_completion() { + #[allow(unused_mut, unused_assignments)] let mut metadata = Vec::new(); #[cfg(feature = "feature_sdv")] { diff --git a/kuksa_databroker/lib/common.rs b/kuksa_databroker/lib/common.rs index 74f0d99b4..e4821790c 100644 --- a/kuksa_databroker/lib/common.rs +++ b/kuksa_databroker/lib/common.rs @@ -37,6 +37,7 @@ pub enum ConnectionState { pub enum ClientError { Connection(String), Status(tonic::Status), + Function(Vec) } impl std::error::Error for ClientError {} @@ -45,6 +46,15 @@ impl std::fmt::Display for ClientError { match self { ClientError::Connection(con) => f.pad(con), ClientError::Status(status) => f.pad(&format!("{status}")), + ClientError::Function(err) => { + let formatted_result: String = err + .iter() + .map(|element| format!("{}", element)) + .collect::>() + .join(", "); // Join the elements with a comma and space + + f.pad(&formatted_result) + } } } } diff --git a/kuksa_databroker/lib/kuksa/src/client.rs b/kuksa_databroker/lib/kuksa/src/client.rs index c78d5c0a3..08f6b33bf 100644 --- a/kuksa_databroker/lib/kuksa/src/client.rs +++ b/kuksa_databroker/lib/kuksa/src/client.rs @@ -49,8 +49,18 @@ impl KuksaClient { match client.get(get_request).await { Ok(response) => { let message = response.into_inner(); - let entries = message.entries; - metadata_result = entries; + metadata_result = message.entries; + let mut errors = Vec::new(); + for error in message.errors{ + if let Some(err) = error.error{ + errors.push(err.code.to_string()); + errors.push(err.reason.to_string()); + errors.push(err.message.to_string()); + } + } + if !errors.is_empty(){ + return Err(ClientError::Function(errors)); + } } Err(err) => return Err(ClientError::Status(err)), } @@ -83,6 +93,17 @@ impl KuksaClient { Ok(response) => { let message = response.into_inner(); get_result = message.entries; + let mut errors = Vec::new(); + for error in message.errors{ + if let Some(err) = error.error{ + errors.push(err.code.to_string()); + errors.push(err.reason.to_string()); + errors.push(err.message.to_string()); + } + } + if !errors.is_empty(){ + return Err(ClientError::Function(errors)); + } } Err(err) => return Err(ClientError::Status(err)), } @@ -115,6 +136,17 @@ impl KuksaClient { Ok(response) => { let message = response.into_inner(); get_result = message.entries; + let mut errors = Vec::new(); + for error in message.errors{ + if let Some(err) = error.error{ + errors.push(err.code.to_string()); + errors.push(err.reason.to_string()); + errors.push(err.message.to_string()); + } + } + if !errors.is_empty(){ + return Err(ClientError::Function(errors)); + } } Err(err) => return Err(ClientError::Status(err)), } From 845687039ea1db58cfe15d736041c4563c86f296 Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Fri, 29 Sep 2023 14:35:16 +0200 Subject: [PATCH 05/14] Add CI for kuksa_feature and fix clippy findings --- .../workflows/kuksa_databroker-cli_build.yml | 3 + kuksa_databroker/databroker-cli/src/main.rs | 87 ++++++++++++------- kuksa_databroker/lib/Cargo.toml | 2 +- kuksa_databroker/lib/common.rs | 4 +- kuksa_databroker/lib/kuksa/Cargo.toml | 2 +- kuksa_databroker/lib/kuksa/src/client.rs | 26 +++--- kuksa_databroker/lib/sdv/Cargo.toml | 2 +- 7 files changed, 78 insertions(+), 48 deletions(-) diff --git a/.github/workflows/kuksa_databroker-cli_build.yml b/.github/workflows/kuksa_databroker-cli_build.yml index e0798fe0a..2a7b0a0d4 100644 --- a/.github/workflows/kuksa_databroker-cli_build.yml +++ b/.github/workflows/kuksa_databroker-cli_build.yml @@ -45,6 +45,9 @@ jobs: - name: cargo clippy working-directory: ${{github.workspace}} run: cargo clippy --all-targets -- -W warnings -D warnings + - name: cargo clippy feature kuksa + working-directory: ${{github.workspace}} + run: cargo clippy --all-targets --no-default-features --features feature_kuksa -- -W warnings -D warnings - name: "Createbom: License check and Dash output generation" working-directory: ${{github.workspace}}/kuksa_databroker/createbom diff --git a/kuksa_databroker/databroker-cli/src/main.rs b/kuksa_databroker/databroker-cli/src/main.rs index 94f5fc913..c91e9eebe 100644 --- a/kuksa_databroker/databroker-cli/src/main.rs +++ b/kuksa_databroker/databroker-cli/src/main.rs @@ -103,17 +103,14 @@ enum Commands { #[tokio::main] async fn main() -> Result<(), Box> { - #[allow(unused_assignments)] - let mut properties = Vec::new(); + let mut _properties = Vec::::new(); #[cfg(feature = "feature_sdv")] { println!("Using sdv"); - properties = Vec::::new(); } #[cfg(feature = "feature_kuksa")] { println!("Using kuksa"); - properties = Vec::::new(); } let mut subscription_nbr = 1; @@ -230,7 +227,7 @@ async fn main() -> Result<(), Box> { "Successfully connected to {}", client.basic_client.get_uri() ))?; - #[allow(unused_mut)] + #[allow(unused_mut, unused_assignments)] let mut pattern = vec![]; #[cfg(feature = "feature_kuksa")] { @@ -240,7 +237,10 @@ async fn main() -> Result<(), Box> { Ok(metadata) => { interface .set_completer(Arc::new(CliCompleter::from_metadata(&metadata))); - properties = metadata; + #[cfg(feature = "feature_sdv")] + { + _properties = metadata; + } } Err(common::ClientError::Status(status)) => { print_resp_err("metadata", &status)?; @@ -349,7 +349,10 @@ async fn main() -> Result<(), Box> { interface.set_completer(Arc::new( CliCompleter::from_metadata(&metadata), )); - properties = metadata; + #[cfg(feature = "feature_sdv")] + { + _properties = metadata; + } } Err(common::ClientError::Status(status)) => { print_resp_err("metadata", &status)?; @@ -358,7 +361,10 @@ async fn main() -> Result<(), Box> { print_error("metadata", msg)?; } Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt("metadata", format_args!("Error {msg:?}"))?; + print_resp_err_fmt( + "metadata", + format_args!("Error {msg:?}"), + )?; } } } @@ -383,7 +389,10 @@ async fn main() -> Result<(), Box> { interface.set_completer(Arc::new( CliCompleter::from_metadata(&metadata), )); - properties = metadata; + #[cfg(feature = "feature_sdv")] + { + _properties = metadata; + } } Err(common::ClientError::Status(status)) => { print_resp_err("metadata", &status)?; @@ -392,7 +401,10 @@ async fn main() -> Result<(), Box> { print_error("metadata", msg)?; } Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )?; } } } @@ -422,7 +434,7 @@ async fn main() -> Result<(), Box> { { let datapoint_metadata = { let mut datapoint_metadata = None; - for metadata in properties.iter() { + for metadata in _properties.iter() { if metadata.name == path { datapoint_metadata = Some(metadata) } @@ -575,9 +587,7 @@ async fn main() -> Result<(), Box> { )]); match client.set_current_values(datapoints).await { - Ok(res) => { - print_resp_ok(cmd)? - } + Ok(_) => print_resp_ok(cmd)?, Err(common::ClientError::Status(status)) => { print_resp_err(cmd, &status)? } @@ -585,7 +595,10 @@ async fn main() -> Result<(), Box> { print_error(cmd, msg)? } Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))? + print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )? } } } @@ -607,7 +620,7 @@ async fn main() -> Result<(), Box> { { let datapoint_metadata = { let mut datapoint_metadata = None; - for metadata in properties.iter() { + for metadata in _properties.iter() { if metadata.name == path { datapoint_metadata = Some(metadata) } @@ -734,8 +747,8 @@ async fn main() -> Result<(), Box> { )]); match client.set_current_values(datapoints).await { - Ok(res) => { - print_resp_ok(cmd); + Ok(_) => { + print_resp_ok(cmd)?; } Err(common::ClientError::Status(status)) => { print_resp_err(cmd, &status)? @@ -744,7 +757,10 @@ async fn main() -> Result<(), Box> { print_error(cmd, msg)? } Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )?; } } } @@ -893,7 +909,9 @@ async fn main() -> Result<(), Box> { print_resp_err(cmd, &status)? } Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, - Err(common::ClientError::Function(msg)) => print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?, + Err(common::ClientError::Function(msg)) => { + print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))? + } } } "connect" => { @@ -944,7 +962,10 @@ async fn main() -> Result<(), Box> { interface.set_completer(Arc::new( CliCompleter::from_metadata(&metadata), )); - properties = metadata; + #[cfg(feature = "feature_sdv")] + { + _properties = metadata; + } } Err(common::ClientError::Status(status)) => { print_resp_err("metadata", &status)?; @@ -969,9 +990,9 @@ async fn main() -> Result<(), Box> { match client.get_metadata(vec![]).await { Ok(mut metadata) => { metadata.sort_by(|a, b| a.name.cmp(&b.name)); - properties = metadata; + _properties = metadata; interface.set_completer(Arc::new( - CliCompleter::from_metadata(&properties), + CliCompleter::from_metadata(&_properties), )); print_resp_ok(cmd)?; } @@ -991,12 +1012,12 @@ async fn main() -> Result<(), Box> { let mut filtered_metadata = Vec::new(); if paths.is_empty() { print_info("If you want to list metadata of signals, use `metadata PATTERN`")?; - // filtered_metadata.extend(&properties); + // filtered_metadata.extend(&_properties); } else { for path in &paths { let path_re = path_to_regex(path); let filtered = - properties.iter().filter(|item| match &path_re { + _properties.iter().filter(|item| match &path_re { Ok(re) => re.is_match(&item.name), Err(err) => { print_info(format!("Invalid path: {err}")) @@ -1398,10 +1419,12 @@ impl CliCompleter { fn set_connected_prompt(interface: &Arc>) { let mut _text; - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { _text = "sdv.databroker.v1" } - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { _text = "kuksa.val.v1" } let connected_prompt = format!( @@ -1454,6 +1477,7 @@ fn to_uri(uri: impl AsRef) -> Result { tonic::transport::Uri::from_parts(parts).map_err(|err| format!("{err}")) } +#[allow(dead_code)] fn path_to_regex(path: impl AsRef) -> Result { let path_as_re = format!( // Match the whole line (from left '^' to right '$') @@ -1488,6 +1512,7 @@ fn print_resp_err_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io stdout.flush() } +#[allow(dead_code)] fn print_resp_ok_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { let mut stderr = io::stderr().lock(); let mut stdout = io::stdout().lock(); @@ -1575,7 +1600,8 @@ impl Completer for CliCompleter { } Some("get") | Some("metadata") => self.complete_entry_path(word), Some("subscribe") => { - #[cfg(feature = "feature_sdv")]{ + #[cfg(feature = "feature_sdv")] + { match words.next() { None => Some(vec![Completion::simple("SELECT".to_owned())]), Some(next) => { @@ -1587,14 +1613,15 @@ impl Completer for CliCompleter { } } } - #[cfg(feature = "feature_kuksa")]{ + #[cfg(feature = "feature_kuksa")] + { if words.count() == 0 { self.complete_entry_path(word) } else { None } } - }, + } Some("token-file") => { let path_completer = linefeed::complete::PathCompleter; path_completer.complete(word, prompter, start, _end) diff --git a/kuksa_databroker/lib/Cargo.toml b/kuksa_databroker/lib/Cargo.toml index fa6c8e1c5..44003dd22 100644 --- a/kuksa_databroker/lib/Cargo.toml +++ b/kuksa_databroker/lib/Cargo.toml @@ -34,4 +34,4 @@ path = "common.rs" [features] default = ["tls"] -tls = ["tonic/tls"] \ No newline at end of file +tls = ["tonic/tls"] diff --git a/kuksa_databroker/lib/common.rs b/kuksa_databroker/lib/common.rs index e4821790c..35cf4d3ec 100644 --- a/kuksa_databroker/lib/common.rs +++ b/kuksa_databroker/lib/common.rs @@ -37,7 +37,7 @@ pub enum ConnectionState { pub enum ClientError { Connection(String), Status(tonic::Status), - Function(Vec) + Function(Vec), } impl std::error::Error for ClientError {} @@ -49,7 +49,7 @@ impl std::fmt::Display for ClientError { ClientError::Function(err) => { let formatted_result: String = err .iter() - .map(|element| format!("{}", element)) + .map(|element| element.to_string()) .collect::>() .join(", "); // Join the elements with a comma and space diff --git a/kuksa_databroker/lib/kuksa/Cargo.toml b/kuksa_databroker/lib/kuksa/Cargo.toml index 18c57c3f7..b2cd9aab7 100644 --- a/kuksa_databroker/lib/kuksa/Cargo.toml +++ b/kuksa_databroker/lib/kuksa/Cargo.toml @@ -35,4 +35,4 @@ path = "src/client.rs" [features] default = ["tls"] -tls = ["tonic/tls"] \ No newline at end of file +tls = ["tonic/tls"] diff --git a/kuksa_databroker/lib/kuksa/src/client.rs b/kuksa_databroker/lib/kuksa/src/client.rs index 08f6b33bf..b59e9bf7a 100644 --- a/kuksa_databroker/lib/kuksa/src/client.rs +++ b/kuksa_databroker/lib/kuksa/src/client.rs @@ -51,14 +51,14 @@ impl KuksaClient { let message = response.into_inner(); metadata_result = message.entries; let mut errors = Vec::new(); - for error in message.errors{ - if let Some(err) = error.error{ + for error in message.errors { + if let Some(err) = error.error { errors.push(err.code.to_string()); errors.push(err.reason.to_string()); errors.push(err.message.to_string()); } } - if !errors.is_empty(){ + if !errors.is_empty() { return Err(ClientError::Function(errors)); } } @@ -94,14 +94,14 @@ impl KuksaClient { let message = response.into_inner(); get_result = message.entries; let mut errors = Vec::new(); - for error in message.errors{ - if let Some(err) = error.error{ + for error in message.errors { + if let Some(err) = error.error { errors.push(err.code.to_string()); errors.push(err.reason.to_string()); errors.push(err.message.to_string()); } } - if !errors.is_empty(){ + if !errors.is_empty() { return Err(ClientError::Function(errors)); } } @@ -137,14 +137,14 @@ impl KuksaClient { let message = response.into_inner(); get_result = message.entries; let mut errors = Vec::new(); - for error in message.errors{ - if let Some(err) = error.error{ + for error in message.errors { + if let Some(err) = error.error { errors.push(err.code.to_string()); errors.push(err.reason.to_string()); errors.push(err.message.to_string()); } } - if !errors.is_empty(){ + if !errors.is_empty() { return Err(ClientError::Function(errors)); } } @@ -284,7 +284,7 @@ impl KuksaClient { }) } - let req = proto::v1::SubscribeRequest { entries: entries }; + let req = proto::v1::SubscribeRequest { entries }; match client.subscribe(req).await { Ok(response) => Ok(response.into_inner()), @@ -297,7 +297,7 @@ impl KuksaClient { &mut self, paths: Vec<&str>, ) -> Result, ClientError> { - return self.subscribe_current_values(paths).await; + self.subscribe_current_values(paths).await } pub async fn subscribe_target_values( @@ -317,7 +317,7 @@ impl KuksaClient { }) } - let req = proto::v1::SubscribeRequest { entries: entries }; + let req = proto::v1::SubscribeRequest { entries }; match client.subscribe(req).await { Ok(response) => Ok(response.into_inner()), @@ -342,7 +342,7 @@ impl KuksaClient { }) } - let req = proto::v1::SubscribeRequest { entries: entries }; + let req = proto::v1::SubscribeRequest { entries }; match client.subscribe(req).await { Ok(response) => Ok(response.into_inner()), diff --git a/kuksa_databroker/lib/sdv/Cargo.toml b/kuksa_databroker/lib/sdv/Cargo.toml index bfa8dc713..64df34e63 100644 --- a/kuksa_databroker/lib/sdv/Cargo.toml +++ b/kuksa_databroker/lib/sdv/Cargo.toml @@ -35,4 +35,4 @@ path = "src/client.rs" [features] default = ["tls"] -tls = ["tonic/tls"] \ No newline at end of file +tls = ["tonic/tls"] From ff029510d036086ac3aa138892375a6d61e0ee33 Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Thu, 2 Nov 2023 09:59:06 +0100 Subject: [PATCH 06/14] Create separate files for sdv and kuksa API and make it changeable through protcol option --- kuksa_databroker/README.md | 10 +- kuksa_databroker/databroker-cli/Cargo.toml | 8 +- kuksa_databroker/databroker-cli/src/cli.rs | 391 +++ kuksa_databroker/databroker-cli/src/help.rs | 1016 +++++++ .../databroker-cli/src/kuksa_cli.rs | 1323 +++++++++ kuksa_databroker/databroker-cli/src/main.rs | 2426 +---------------- .../databroker-cli/src/sdv_cli.rs | 1379 ++++++++++ kuksa_databroker/lib/common.rs | 9 +- kuksa_databroker/lib/kuksa/src/client.rs | 315 +-- kuksa_databroker/lib/sdv/src/client.rs | 5 +- 10 files changed, 4286 insertions(+), 2596 deletions(-) create mode 100644 kuksa_databroker/databroker-cli/src/cli.rs create mode 100644 kuksa_databroker/databroker-cli/src/help.rs create mode 100644 kuksa_databroker/databroker-cli/src/kuksa_cli.rs create mode 100644 kuksa_databroker/databroker-cli/src/sdv_cli.rs diff --git a/kuksa_databroker/README.md b/kuksa_databroker/README.md index dc817a519..801a52ad2 100644 --- a/kuksa_databroker/README.md +++ b/kuksa_databroker/README.md @@ -112,12 +112,14 @@ cargo run --bin databroker -- --port 55556 Please note, this also applies if you use a container environment like K3S or Docker on Mac OS. If you forward the port or exposing the host network interfaces, the same problem occurs. -For more information see also https://developer.apple.com/forums/thread/671197 +For more information see also https://developer.apple.com/forums/thread/671197 Currently, to run databroker-cli (see below), you do need to change the port it connects to in databroker-cli code and recompile it. ## Test the Databroker using CLI +The databroker supports both of ```sdv.databroker.v1``` and ```kuksa.val.v1``` as an API. Per default the databroker-cli uses the ```sdv.databroker.v1``` interface. To change it use ```--protocol``` option when starting. Chosse eihter one of ```kuksa-val-v1``` and ```sdv-databroker-v1```. + Run the cli with: `cargo run --bin databroker-cli` @@ -142,7 +144,7 @@ This will enable `TAB`-completion for the available properties in the client. Ru Get data points by running "get" ```shell -sdv.databroker.v1 > get Vehicle.ADAS.CruiseControl.IsEnabled +sdv.databroker.v1 > get Vehicle.ADAS.CruiseControl.IsEnabled [get] OK Vehicle.ADAS.CruiseControl.IsEnabled: ( NotAvailable ) ``` @@ -164,7 +166,7 @@ or interactively sdv.databroker.v1 > token XXXXXXXXXX ``` -or +or ```shell sdv.databroker.v1 > token-file jwt/read-vehicle-speed.token @@ -215,7 +217,7 @@ Use the following command to run Databroker test cases ```shell cargo test --all-targets ``` - + ## Build and run Databroker using Docker To build the release version of databroker, run the following command: diff --git a/kuksa_databroker/databroker-cli/Cargo.toml b/kuksa_databroker/databroker-cli/Cargo.toml index 5ba13dd54..3adb7fa37 100644 --- a/kuksa_databroker/databroker-cli/Cargo.toml +++ b/kuksa_databroker/databroker-cli/Cargo.toml @@ -20,8 +20,8 @@ license = "Apache-2.0" [dependencies] common = { path = "../lib"} -kuksa = { path = "../lib/kuksa", optional = true} -sdv = { path = "../lib/sdv", optional = true} +kuksa = { path = "../lib/kuksa"} +sdv = { path = "../lib/sdv"} databroker-proto = { workspace = true } tonic = { workspace = true, features = ["transport", "channel", "prost"] } prost = { workspace = true } @@ -44,7 +44,5 @@ regex = "1.6.0" http = "0.2.8" [features] -default = ["tls", "feature_sdv"] -feature_kuksa = ["kuksa"] -feature_sdv = ["sdv"] +default = ["tls"] tls = ["tonic/tls"] diff --git a/kuksa_databroker/databroker-cli/src/cli.rs b/kuksa_databroker/databroker-cli/src/cli.rs new file mode 100644 index 000000000..5ed3116ec --- /dev/null +++ b/kuksa_databroker/databroker-cli/src/cli.rs @@ -0,0 +1,391 @@ +/******************************************************************************** +* Copyright (c) 2022 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License 2.0 which is available at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + +use http::Uri; +use std::io::Write; +use std::sync::Arc; +use std::{fmt, io}; + +use ansi_term::Color; + +use clap::{Parser, Subcommand, ValueEnum}; + +use linefeed::{DefaultTerminal, Interface, Terminal, Prompter, Function}; + +#[derive(Debug)] +pub struct ParseError {} + +impl std::error::Error for ParseError {} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("parse error") + } +} + +#[derive(Debug, Parser, Clone)] +#[clap(author, version, about, long_about = None)] +pub struct Cli { + /// Server to connect to + #[clap(long, display_order = 1, default_value = "http://127.0.0.1:55555")] + server: String, + + // #[clap(short, long)] + // port: Option, + /// File containing access token + #[clap(long, value_name = "FILE", display_order = 2)] + token_file: Option, + + /// CA certificate used to verify server certificate + #[cfg(feature = "tls")] + #[clap(long, value_name = "CERT", display_order = 3)] + ca_cert: Option, + + #[arg(value_enum)] + #[clap(long, short = 'p', value_enum, default_value = "sdv-databroker-v1")] + protocol: CliAPI, + + // Sub command + #[clap(subcommand)] + command: Option, +} + +impl Cli{ + pub fn get_ca_cert(&mut self) -> Option{ + return self.ca_cert.clone(); + } + + pub fn get_token_file(&mut self) -> Option{ + return self.token_file.clone(); + } + + pub fn get_command(&mut self) -> Option{ + return self.command.clone(); + } + + pub fn get_server(&mut self) -> String{ + return self.server.clone(); + } + + pub fn get_protocol(&mut self) -> CliAPI{ + return self.protocol; + } +} + +#[derive(Debug, Subcommand, Clone)] +pub enum Commands { + /// Get one or more datapoint(s) + Get { + #[clap(value_name = "PATH")] + paths: Vec, + }, + // Subscribe, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +pub enum CliAPI { + KuksaValV1 = 1, + SdvDatabrokerV1 = 2, +} + +pub fn set_connected_prompt(interface: &Arc>, text: String) { + let _text = text; + let connected_prompt = format!( + "\x01{prefix}\x02{text}\x01{suffix}\x02 > ", + prefix = Color::Green.prefix(), + text = _text, + suffix = Color::Green.suffix() + ); + interface.set_prompt(&connected_prompt).unwrap(); +} + +pub fn set_disconnected_prompt(interface: &Arc>) { + let disconnected_prompt = format!( + "\x01{prefix}\x02{text}\x01{suffix}\x02 > ", + prefix = Color::Red.prefix(), + text = "not connected", + suffix = Color::Red.suffix() + ); + interface.set_prompt(&disconnected_prompt).unwrap(); +} + +pub fn print_logo(version: impl fmt::Display) { + let mut output = io::stderr().lock(); + writeln!(output).unwrap(); + writeln!( + output, + " {}{}", + Color::Fixed(23).paint("⠀⠀⠀⢀⣤⣶⣾⣿"), + Color::White.dimmed().paint("⢸⣿⣿⣷⣶⣤⡀") + ) + .unwrap(); + writeln!( + output, + " {}{}", + Color::Fixed(23).paint("⠀⠀⣴⣿⡿⠋⣿⣿"), + Color::White.dimmed().paint("⠀⠀⠀⠈⠙⢿⣿⣦⠀") + ) + .unwrap(); + writeln!( + output, + " {}{}", + Color::Fixed(23).paint("⠀⣾⣿⠋⠀⠀⣿⣿"), + Color::White.dimmed().paint("⠀⠀⣶⣿⠀⠀⠙⣿⣷ ") + ) + .unwrap(); + writeln!( + output, + " {}{}", + Color::Fixed(23).paint("⣸⣿⠇⠀⠀⠀⣿⣿"), + Color::White + .dimmed() + .paint("⠠⣾⡿⠃⠀⠀⠀⠸⣿⣇⠀⠀⣶⠀⣠⡶⠂⠀⣶⠀⠀⢰⡆⠀⢰⡆⢀⣴⠖⠀⢠⡶⠶⠶⡦⠀⠀⠀⣰⣶⡀") + ) + .unwrap(); + writeln!( + output, + " {}{}", + Color::Fixed(23).paint("⣿⣿⠀⠀⠀⠀⠿⢿⣷⣦⡀"), + Color::White + .dimmed() + .paint("⠀⠀⠀⠀⠀⣿⣿⠀⠀⣿⢾⣏⠀⠀⠀⣿⠀⠀⢸⡇⠀⢸⡷⣿⡁⠀⠀⠘⠷⠶⠶⣦⠀⠀⢠⡟⠘⣷") + ) + .unwrap(); + writeln!( + output, + " {}{}{}{}", + Color::Fixed(23).paint("⢹⣿⡆⠀⠀⠀"), + Color::White.dimmed().paint("⣿⣶"), + Color::Fixed(23).paint("⠈⢻⣿⡆"), + Color::White + .dimmed() + .paint("⠀⠀⠀⢰⣿⡏⠀⠀⠿⠀⠙⠷⠄⠀⠙⠷⠶⠟⠁⠀⠸⠇⠈⠻⠦⠀⠐⠷⠶⠶⠟⠀⠠⠿⠁⠀⠹⠧") + ) + .unwrap(); + writeln!( + output, + " {}{}{}{}", + Color::Fixed(23).paint("⠀⢿⣿⣄⠀⠀"), + Color::White.dimmed().paint("⣿⣿"), + Color::Fixed(23).paint("⠀⠀⠿⣿"), + Color::White.dimmed().paint("⠀⠀⣠⣿⡿"), + ) + .unwrap(); + writeln!( + output, + " {}{} {}", + Color::Fixed(23).paint("⠀⠀⠻⣿⣷⡄"), + Color::White.dimmed().paint("⣿⣿⠀⠀⠀⢀⣠⣾⣿⠟"), + Color::White + .dimmed() + .paint(format!("{:<30}", "databroker-cli")), + ) + .unwrap(); + writeln!( + output, + " {}{} {}", + Color::Fixed(23).paint("⠀⠀⠀⠈⠛⠇"), + Color::White.dimmed().paint("⢿⣿⣿⣿⣿⡿⠿⠛⠁"), + Color::White.dimmed().paint(format!("{version:<30}")), + ) + .unwrap(); + writeln!(output).unwrap(); +} + +pub fn print_resp_err(operation: impl AsRef, err: &tonic::Status) -> io::Result<()> { + let mut output = io::stderr().lock(); + output.write_fmt(format_args!( + "{} {} {}", + Color::White + .dimmed() + .paint(format!("[{}]", operation.as_ref())), + Color::White + .on(Color::Red) + .paint(format!(" {} ", code_to_text(&err.code()))), + err.message(), + ))?; + output.write_all(b"\n")?; + output.flush() +} + +pub fn print_resp_err_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { + let mut stderr = io::stderr().lock(); + let mut stdout = io::stdout().lock(); + write_resp_ok(&mut stderr, operation)?; + stdout.write_fmt(fmt)?; + stdout.write_all(b"\n")?; + stdout.flush() +} + +#[allow(dead_code)] +pub fn print_resp_ok_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { + let mut stderr = io::stderr().lock(); + let mut stdout = io::stdout().lock(); + write_resp_ok(&mut stderr, operation)?; + stdout.write_fmt(fmt)?; + stdout.write_all(b"\n")?; + stdout.flush() +} + +pub fn print_resp_ok(operation: impl AsRef) -> io::Result<()> { + let mut output = io::stderr().lock(); + write_resp_ok(&mut output, operation)?; + output.write_all(b"\n")?; + output.flush() +} + +pub fn write_resp_ok(output: &mut impl Write, operation: impl AsRef) -> io::Result<()> { + output.write_fmt(format_args!( + "{} {} ", + Color::White + .dimmed() + .paint(format!("[{}]", operation.as_ref())), + Color::Black.on(Color::Green).paint(" OK "), + )) +} + +pub fn print_info(info: impl AsRef) -> io::Result<()> { + let mut output = io::stderr().lock(); + output.write_fmt(format_args!( + "{}\n", + Color::White.dimmed().paint(info.as_ref()), + ))?; + output.flush() +} + +pub fn print_error(operation: impl AsRef, msg: impl AsRef) -> io::Result<()> { + let mut output = io::stderr().lock(); + output.write_fmt(format_args!( + "{} {} {}\n", + Color::White + .dimmed() + .paint(format!("[{}]", operation.as_ref())), + Color::White.on(Color::Red).paint(" Error "), + msg.as_ref(), + ))?; + output.flush() +} + +pub fn split_first_word(s: &str) -> (&str, &str) { + let s = s.trim(); + + match s.find(|ch: char| ch.is_whitespace()) { + Some(pos) => (&s[..pos], s[pos..].trim_start()), + None => (s, ""), + } +} + +pub fn code_to_text(code: &tonic::Code) -> &str { + match code { + tonic::Code::Ok => "Ok", + tonic::Code::Cancelled => "Cancelled", + tonic::Code::Unknown => "Unknown", + tonic::Code::InvalidArgument => "InvalidArgument", + tonic::Code::DeadlineExceeded => "DeadlineExceeded", + tonic::Code::NotFound => "NotFound", + tonic::Code::AlreadyExists => "AlreadyExists", + tonic::Code::PermissionDenied => "PermissionDenied", + tonic::Code::ResourceExhausted => "ResourceExhausted", + tonic::Code::FailedPrecondition => "FailedPrecondition", + tonic::Code::Aborted => "Aborted", + tonic::Code::OutOfRange => "OutOfRange", + tonic::Code::Unimplemented => "Unimplemented", + tonic::Code::Internal => "Internal", + tonic::Code::Unavailable => "Unavailable", + tonic::Code::DataLoss => "DataLoss", + tonic::Code::Unauthenticated => "Unauthenticated", + } +} + +pub fn get_array_from_input(values: String) -> Result, ParseError> { + let raw_input = values + .strip_prefix('[') + .and_then(|s| s.strip_suffix(']')) + .ok_or(ParseError {})?; + + let pattern = r#"(?:\\.|[^",])*"(?:\\.|[^"])*"|[^",]+"#; + + let regex = regex::Regex::new(pattern).unwrap(); + let inputs = regex.captures_iter(raw_input); + + let mut array: Vec = vec![]; + for part in inputs { + match part[0] + .trim() + .replace('\"', "") + .replace('\\', "\"") + .parse::() + { + Ok(value) => array.push(value), + Err(_) => return Err(ParseError {}), + } + } + Ok(array) +} + +pub struct EnterFunction; + +impl Function for EnterFunction { + fn execute(&self, prompter: &mut Prompter, count: i32, _ch: char) -> io::Result<()> { + if prompter + .buffer() + .trim() + // .to_lowercase() + .starts_with("subscribe") + { + if prompter.buffer().ends_with('\n') { + let len = prompter.buffer().len(); + prompter.delete_range(len - 1..len)?; + prompter.accept_input() + } else if count > 0 { + // Start multiline + prompter.insert_str("\n") + } else { + Ok(()) + } + } else { + prompter.accept_input() + } + } +} + +pub fn to_uri(uri: impl AsRef) -> Result { + let uri = uri + .as_ref() + .parse::() + .map_err(|err| format!("{err}"))?; + let mut parts = uri.into_parts(); + + if parts.scheme.is_none() { + parts.scheme = Some("http".parse().expect("http should be valid scheme")); + } + + match &parts.authority { + Some(_authority) => { + // match (authority.port_u16(), port) { + // (Some(uri_port), Some(port)) => { + // if uri_port != port { + // parts.authority = format!("{}:{}", authority.host(), port) + // .parse::() + // .map_err(|err| format!("{}", err)) + // .ok(); + // } + // } + // (_, _) => {} + // } + } + None => return Err("No server uri specified".to_owned()), + } + parts.path_and_query = Some("".parse().expect("uri path should be empty string")); + tonic::transport::Uri::from_parts(parts).map_err(|err| format!("{err}")) +} diff --git a/kuksa_databroker/databroker-cli/src/help.rs b/kuksa_databroker/databroker-cli/src/help.rs new file mode 100644 index 000000000..4a5503897 --- /dev/null +++ b/kuksa_databroker/databroker-cli/src/help.rs @@ -0,0 +1,1016 @@ +fn set_connected_prompt(interface: &Arc>, text: str) { + let mut _text; + _text = text; + let connected_prompt = format!( + "\x01{prefix}\x02{text}\x01{suffix}\x02 > ", + prefix = Color::Green.prefix(), + text = _text, + suffix = Color::Green.suffix() + ); + interface.set_prompt(&connected_prompt).unwrap(); +} + +fn set_disconnected_prompt(interface: &Arc>) { + let disconnected_prompt = format!( + "\x01{prefix}\x02{text}\x01{suffix}\x02 > ", + prefix = Color::Red.prefix(), + text = "not connected", + suffix = Color::Red.suffix() + ); + interface.set_prompt(&disconnected_prompt).unwrap(); +} + +fn to_uri(uri: impl AsRef) -> Result { + let uri = uri + .as_ref() + .parse::() + .map_err(|err| format!("{err}"))?; + let mut parts = uri.into_parts(); + + if parts.scheme.is_none() { + parts.scheme = Some("http".parse().expect("http should be valid scheme")); + } + + match &parts.authority { + Some(_authority) => { + // match (authority.port_u16(), port) { + // (Some(uri_port), Some(port)) => { + // if uri_port != port { + // parts.authority = format!("{}:{}", authority.host(), port) + // .parse::() + // .map_err(|err| format!("{}", err)) + // .ok(); + // } + // } + // (_, _) => {} + // } + } + None => return Err("No server uri specified".to_owned()), + } + parts.path_and_query = Some("".parse().expect("uri path should be empty string")); + tonic::transport::Uri::from_parts(parts).map_err(|err| format!("{err}")) +} + +fn print_logo(version: impl fmt::Display) { + let mut output = io::stderr().lock(); + writeln!(output).unwrap(); + writeln!( + output, + " {}{}", + Color::Fixed(23).paint("⠀⠀⠀⢀⣤⣶⣾⣿"), + Color::White.dimmed().paint("⢸⣿⣿⣷⣶⣤⡀") + ) + .unwrap(); + writeln!( + output, + " {}{}", + Color::Fixed(23).paint("⠀⠀⣴⣿⡿⠋⣿⣿"), + Color::White.dimmed().paint("⠀⠀⠀⠈⠙⢿⣿⣦⠀") + ) + .unwrap(); + writeln!( + output, + " {}{}", + Color::Fixed(23).paint("⠀⣾⣿⠋⠀⠀⣿⣿"), + Color::White.dimmed().paint("⠀⠀⣶⣿⠀⠀⠙⣿⣷ ") + ) + .unwrap(); + writeln!( + output, + " {}{}", + Color::Fixed(23).paint("⣸⣿⠇⠀⠀⠀⣿⣿"), + Color::White + .dimmed() + .paint("⠠⣾⡿⠃⠀⠀⠀⠸⣿⣇⠀⠀⣶⠀⣠⡶⠂⠀⣶⠀⠀⢰⡆⠀⢰⡆⢀⣴⠖⠀⢠⡶⠶⠶⡦⠀⠀⠀⣰⣶⡀") + ) + .unwrap(); + writeln!( + output, + " {}{}", + Color::Fixed(23).paint("⣿⣿⠀⠀⠀⠀⠿⢿⣷⣦⡀"), + Color::White + .dimmed() + .paint("⠀⠀⠀⠀⠀⣿⣿⠀⠀⣿⢾⣏⠀⠀⠀⣿⠀⠀⢸⡇⠀⢸⡷⣿⡁⠀⠀⠘⠷⠶⠶⣦⠀⠀⢠⡟⠘⣷") + ) + .unwrap(); + writeln!( + output, + " {}{}{}{}", + Color::Fixed(23).paint("⢹⣿⡆⠀⠀⠀"), + Color::White.dimmed().paint("⣿⣶"), + Color::Fixed(23).paint("⠈⢻⣿⡆"), + Color::White + .dimmed() + .paint("⠀⠀⠀⢰⣿⡏⠀⠀⠿⠀⠙⠷⠄⠀⠙⠷⠶⠟⠁⠀⠸⠇⠈⠻⠦⠀⠐⠷⠶⠶⠟⠀⠠⠿⠁⠀⠹⠧") + ) + .unwrap(); + writeln!( + output, + " {}{}{}{}", + Color::Fixed(23).paint("⠀⢿⣿⣄⠀⠀"), + Color::White.dimmed().paint("⣿⣿"), + Color::Fixed(23).paint("⠀⠀⠿⣿"), + Color::White.dimmed().paint("⠀⠀⣠⣿⡿"), + ) + .unwrap(); + writeln!( + output, + " {}{} {}", + Color::Fixed(23).paint("⠀⠀⠻⣿⣷⡄"), + Color::White.dimmed().paint("⣿⣿⠀⠀⠀⢀⣠⣾⣿⠟"), + Color::White + .dimmed() + .paint(format!("{:<30}", "databroker-cli")), + ) + .unwrap(); + writeln!( + output, + " {}{} {}", + Color::Fixed(23).paint("⠀⠀⠀⠈⠛⠇"), + Color::White.dimmed().paint("⢿⣿⣿⣿⣿⡿⠿⠛⠁"), + Color::White.dimmed().paint(format!("{version:<30}")), + ) + .unwrap(); + writeln!(output).unwrap(); +} + +#[allow(dead_code)] +fn path_to_regex(path: impl AsRef) -> Result { + let path_as_re = format!( + // Match the whole line (from left '^' to right '$') + "^{}$", + path.as_ref().replace('.', r"\.").replace('*', r"(.*)") + ); + regex::Regex::new(&path_as_re) +} + +fn print_resp_err(operation: impl AsRef, err: &tonic::Status) -> io::Result<()> { + let mut output = io::stderr().lock(); + output.write_fmt(format_args!( + "{} {} {}", + Color::White + .dimmed() + .paint(format!("[{}]", operation.as_ref())), + Color::White + .on(Color::Red) + .paint(format!(" {} ", code_to_text(&err.code()))), + err.message(), + ))?; + output.write_all(b"\n")?; + output.flush() +} + +fn print_resp_err_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { + let mut stderr = io::stderr().lock(); + let mut stdout = io::stdout().lock(); + write_resp_ok(&mut stderr, operation)?; + stdout.write_fmt(fmt)?; + stdout.write_all(b"\n")?; + stdout.flush() +} + +#[allow(dead_code)] +fn print_resp_ok_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { + let mut stderr = io::stderr().lock(); + let mut stdout = io::stdout().lock(); + write_resp_ok(&mut stderr, operation)?; + stdout.write_fmt(fmt)?; + stdout.write_all(b"\n")?; + stdout.flush() +} + +fn print_resp_ok(operation: impl AsRef) -> io::Result<()> { + let mut output = io::stderr().lock(); + write_resp_ok(&mut output, operation)?; + output.write_all(b"\n")?; + output.flush() +} + +fn write_resp_ok(output: &mut impl Write, operation: impl AsRef) -> io::Result<()> { + output.write_fmt(format_args!( + "{} {} ", + Color::White + .dimmed() + .paint(format!("[{}]", operation.as_ref())), + Color::Black.on(Color::Green).paint(" OK "), + )) +} + +fn print_info(info: impl AsRef) -> io::Result<()> { + let mut output = io::stderr().lock(); + output.write_fmt(format_args!( + "{}\n", + Color::White.dimmed().paint(info.as_ref()), + ))?; + output.flush() +} + +fn print_error(operation: impl AsRef, msg: impl AsRef) -> io::Result<()> { + let mut output = io::stderr().lock(); + output.write_fmt(format_args!( + "{} {} {}\n", + Color::White + .dimmed() + .paint(format!("[{}]", operation.as_ref())), + Color::White.on(Color::Red).paint(" Error "), + msg.as_ref(), + ))?; + output.flush() +} + +struct EnterFunction; + +impl Function for EnterFunction { + fn execute(&self, prompter: &mut Prompter, count: i32, _ch: char) -> io::Result<()> { + if prompter + .buffer() + .trim() + // .to_lowercase() + .starts_with("subscribe") + { + if prompter.buffer().ends_with('\n') { + let len = prompter.buffer().len(); + prompter.delete_range(len - 1..len)?; + prompter.accept_input() + } else if count > 0 { + // Start multiline + prompter.insert_str("\n") + } else { + Ok(()) + } + } else { + prompter.accept_input() + } + } +} + +struct DisplayDataType(Option); +struct DisplayEntryType(Option); +// !!! ChangeType currently just exists in old API needs to be removed or added later !!! +struct DisplayChangeType(Option); +struct DisplayDatapoint(root::proto::v1::Datapoint); + +fn display_array(f: &mut fmt::Formatter<'_>, array: &[T]) -> fmt::Result +where + T: fmt::Display, +{ + f.write_str("[")?; + let real_delimiter = ", "; + let mut delimiter = ""; + for value in array { + write!(f, "{delimiter}")?; + delimiter = real_delimiter; + write!(f, "{value}")?; + } + f.write_str("]") +} + +impl fmt::Display for DisplayDatapoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + #[cfg(feature = "feature_sdv")] + { + match &self.0.value { + Some(value) => match value { + root::proto::v1::datapoint::Value::BoolValue(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::FailureValue(failure) => f.pad(&format!( + "( {:?} )", + root::proto::v1::datapoint::Failure::from_i32(*failure).unwrap() + )), + root::proto::v1::datapoint::Value::Int32Value(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::Int64Value(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::Uint32Value(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::Uint64Value(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::FloatValue(value) => { + f.pad(&format!("{value:.2}")) + } + root::proto::v1::datapoint::Value::DoubleValue(value) => { + f.pad(&format!("{value}")) + } + root::proto::v1::datapoint::Value::StringValue(value) => { + f.pad(&format!("'{value}'")) + } + root::proto::v1::datapoint::Value::StringArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::BoolArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Int32Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Int64Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Uint32Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Uint64Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::FloatArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::DoubleArray(array) => { + display_array(f, &array.values) + } + }, + None => f.pad("None"), + } + } + #[cfg(feature = "feature_kuksa")] + { + match &self.0.value { + Some(value) => match value { + root::proto::v1::datapoint::Value::Bool(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Int32(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Int64(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Uint32(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Uint64(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::Float(value) => { + f.pad(&format!("{value:.2}")) + } + root::proto::v1::datapoint::Value::Double(value) => f.pad(&format!("{value}")), + root::proto::v1::datapoint::Value::String(value) => { + f.pad(&format!("'{value}'")) + } + root::proto::v1::datapoint::Value::StringArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::BoolArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Int32Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Int64Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Uint32Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::Uint64Array(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::FloatArray(array) => { + display_array(f, &array.values) + } + root::proto::v1::datapoint::Value::DoubleArray(array) => { + display_array(f, &array.values) + } + }, + None => f.pad("None"), + } + } + } +} + +impl From> for DisplayEntryType { + fn from(input: Option) -> Self { + DisplayEntryType(input) + } +} + +impl fmt::Display for DisplayEntryType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Some(entry_type) => f.pad(&format!("{entry_type:?}")), + None => f.pad("Unknown"), + } + } +} + +impl From> for DisplayDataType { + fn from(input: Option) -> Self { + DisplayDataType(input) + } +} + +impl fmt::Display for DisplayDataType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Some(data_type) => f.pad(&format!("{data_type:?}")), + None => f.pad("Unknown"), + } + } +} + +impl From> for DisplayChangeType { + fn from(input: Option) -> Self { + DisplayChangeType(input) + } +} +impl fmt::Display for DisplayChangeType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Some(data_type) => f.pad(&format!("{data_type:?}")), + None => f.pad("Unknown"), + } + } +} + +#[derive(Debug)] +struct ParseError {} + +impl std::error::Error for ParseError {} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("parse error") + } +} + +fn get_array_from_input(values: String) -> Result, ParseError> { + let raw_input = values + .strip_prefix('[') + .and_then(|s| s.strip_suffix(']')) + .ok_or(ParseError {})?; + + let pattern = r#"(?:\\.|[^",])*"(?:\\.|[^"])*"|[^",]+"#; + + let regex = regex::Regex::new(pattern).unwrap(); + let inputs = regex.captures_iter(raw_input); + + let mut array: Vec = vec![]; + for part in inputs { + match part[0] + .trim() + .replace('\"', "") + .replace('\\', "\"") + .parse::() + { + Ok(value) => array.push(value), + Err(_) => return Err(ParseError {}), + } + } + Ok(array) +} + +fn try_into_data_value( + input: &str, + data_type: root::proto::v1::DataType, +) -> Result { + if input == "NotAvailable" { + #[cfg(feature = "feature_sdv")] + { + return Ok(root::proto::v1::datapoint::Value::FailureValue( + root::proto::v1::datapoint::Failure::NotAvailable as i32, + )); + } + #[cfg(feature = "feature_kuksa")] + { + return Ok(root::proto::v1::datapoint::Value::String(input.to_string())); + } + } + + #[cfg(feature = "feature_sdv")] + { + match data_type { + root::proto::v1::DataType::String => Ok( + root::proto::v1::datapoint::Value::StringValue(input.to_owned()), + ), + root::proto::v1::DataType::StringArray => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( + root::proto::v1::StringArray { values: value }, + )), + Err(err) => Err(err), + } + } + root::proto::v1::DataType::Bool => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolValue(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::BoolArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolArray( + root::proto::v1::BoolArray { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int8 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value as i32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int8Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int16 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value as i32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int16Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int32 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int32Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int64 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Value(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int64Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Array( + root::proto::v1::Int64Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint8 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value as u32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint8Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint16 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value as u32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint16Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } + root::proto::v1::DataType::Uint32 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint32Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } + root::proto::v1::DataType::Uint64 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Value(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint64Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( + root::proto::v1::Uint64Array { values: value }, + )), + Err(err) => Err(err), + } + } + root::proto::v1::DataType::Float => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatValue(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::FloatArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatArray( + root::proto::v1::FloatArray { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Double => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleValue(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::DoubleArray => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( + root::proto::v1::DoubleArray { values: value }, + )), + Err(err) => Err(err), + } + } + _ => Err(ParseError {}), + } + } + #[cfg(feature = "feature_kuksa")] + { + match data_type { + root::proto::v1::DataType::String => { + Ok(root::proto::v1::datapoint::Value::String(input.to_owned())) + } + root::proto::v1::DataType::StringArray => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( + root::proto::v1::StringArray { values: value }, + )), + Err(err) => Err(err), + } + } + root::proto::v1::DataType::Boolean => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Bool(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::BooleanArray => match get_array_from_input(input.to_owned()) + { + Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolArray( + root::proto::v1::BoolArray { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int8 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value as i32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int8Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int16 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value as i32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int16Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int32 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int32Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( + root::proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Int64 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Int64Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Array( + root::proto::v1::Int64Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint8 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value as u32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint8Array => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Uint16 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value as u32)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint16Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } + root::proto::v1::DataType::Uint32 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint32Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( + root::proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } + root::proto::v1::DataType::Uint64 => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::Uint64Array => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( + root::proto::v1::Uint64Array { values: value }, + )), + Err(err) => Err(err), + } + } + root::proto::v1::DataType::Float => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Float(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::FloatArray => match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatArray( + root::proto::v1::FloatArray { values: value }, + )), + Err(err) => Err(err), + }, + root::proto::v1::DataType::Double => match input.parse::() { + Ok(value) => Ok(root::proto::v1::datapoint::Value::Double(value)), + Err(_) => Err(ParseError {}), + }, + root::proto::v1::DataType::DoubleArray => { + match get_array_from_input(input.to_owned()) { + Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( + root::proto::v1::DoubleArray { values: value }, + )), + Err(err) => Err(err), + } + } + _ => Err(ParseError {}), + } + } +} + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn test_parse_values() { + #[cfg(feature = "feature_sdv")] + { + // String + assert!(matches!( + try_into_data_value("test", root::proto::v1::DataType::String), + Ok(root::proto::v1::datapoint::Value::StringValue(value)) if value == "test" + )); + + // StringArray + assert!(matches!( + try_into_data_value("[test, test2, test4]", root::proto::v1::DataType::StringArray), + Ok(root::proto::v1::datapoint::Value::StringArray(value)) if value == root::proto::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} + )); + + // Bool + assert!(matches!( + try_into_data_value("true", root::proto::v1::DataType::Bool), + Ok(root::proto::v1::datapoint::Value::BoolValue(value)) if value + )); + + assert!(matches!( + try_into_data_value("false", root::proto::v1::DataType::Bool), + Ok(root::proto::v1::datapoint::Value::BoolValue(value)) if !value + )); + assert!(try_into_data_value("truefalse", root::proto::v1::DataType::Bool).is_err()); + // BoolArray + assert!(matches!( + try_into_data_value("[true, false, true]", root::proto::v1::DataType::BoolArray), + Ok(root::proto::v1::datapoint::Value::BoolArray(value)) if value == root::proto::v1::BoolArray{values: vec![true, false, true]} + )); + + // Int8 + assert!(matches!( + try_into_data_value("100", root::proto::v1::DataType::Int8), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", root::proto::v1::DataType::Int8), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -100 + )); + assert!(try_into_data_value("300", root::proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-300", root::proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-100.1", root::proto::v1::DataType::Int8).is_err()); + + // Int16 + assert!(matches!( + try_into_data_value("100", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -100 + )); + assert!(matches!( + try_into_data_value("32000", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 32000 + )); + assert!(matches!( + try_into_data_value("-32000", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -32000 + )); + assert!(try_into_data_value("33000", root::proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-33000", root::proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-32000.1", root::proto::v1::DataType::Int16).is_err()); + } + #[cfg(feature = "feature_kuksa")] + { + // String + assert!(matches!( + try_into_data_value("test", root::proto::v1::DataType::String), + Ok(root::proto::v1::datapoint::Value::String(value)) if value == "test" + )); + + // StringArray + assert!(matches!( + try_into_data_value("[test, test2, test4]", root::proto::v1::DataType::StringArray), + Ok(root::proto::v1::datapoint::Value::StringArray(value)) if value == root::proto::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} + )); + + // Bool + assert!(matches!( + try_into_data_value("true", root::proto::v1::DataType::Boolean), + Ok(root::proto::v1::datapoint::Value::Bool(value)) if value + )); + + assert!(matches!( + try_into_data_value("false", root::proto::v1::DataType::Boolean), + Ok(root::proto::v1::datapoint::Value::Bool(value)) if !value + )); + assert!(try_into_data_value("truefalse", root::proto::v1::DataType::Boolean).is_err()); + // BoolArray + assert!(matches!( + try_into_data_value("[true, false, true]", root::proto::v1::DataType::BooleanArray), + Ok(root::proto::v1::datapoint::Value::BoolArray(value)) if value == root::proto::v1::BoolArray{values: vec![true, false, true]} + )); + + // Int8 + assert!(matches!( + try_into_data_value("100", root::proto::v1::DataType::Int8), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", root::proto::v1::DataType::Int8), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -100 + )); + assert!(try_into_data_value("300", root::proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-300", root::proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-100.1", root::proto::v1::DataType::Int8).is_err()); + + // Int16 + assert!(matches!( + try_into_data_value("100", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -100 + )); + assert!(matches!( + try_into_data_value("32000", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 32000 + )); + assert!(matches!( + try_into_data_value("-32000", root::proto::v1::DataType::Int16), + Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -32000 + )); + assert!(try_into_data_value("33000", root::proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-33000", root::proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-32000.1", root::proto::v1::DataType::Int16).is_err()); + } + } + + #[test] + fn test_entry_path_completion() { + #[allow(unused_mut, unused_assignments)] + let mut metadata = Vec::new(); + #[cfg(feature = "feature_sdv")] + { + metadata = [ + root::proto::v1::Metadata { + id: 1, + name: "Vehicle.Test.Test1".into(), + data_type: root::proto::v1::DataType::Bool.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + change_type: root::proto::v1::ChangeType::OnChange.into(), + description: "".into(), + }, + root::proto::v1::Metadata { + id: 2, + name: "Vehicle.AnotherTest.AnotherTest1".into(), + data_type: root::proto::v1::DataType::Bool.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + change_type: root::proto::v1::ChangeType::OnChange.into(), + description: "".into(), + }, + root::proto::v1::Metadata { + id: 3, + name: "Vehicle.AnotherTest.AnotherTest2".into(), + data_type: root::proto::v1::DataType::Bool.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + change_type: root::proto::v1::ChangeType::OnChange.into(), + description: "".into(), + }, + ] + .to_vec(); + } + + #[cfg(feature = "feature_kuksa")] + { + metadata.push(root::proto::v1::DataEntry { + path: "Vehicle.Test.Test1".into(), + value: None, + actuator_target: None, + metadata: Some(root::proto::v1::Metadata { + data_type: root::proto::v1::DataType::Boolean.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + }), + }); + metadata.push(root::proto::v1::DataEntry { + path: "Vehicle.Test.AnotherTest1".into(), + value: None, + actuator_target: None, + metadata: Some(root::proto::v1::Metadata { + data_type: root::proto::v1::DataType::Boolean.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + }), + }); + metadata.push(root::proto::v1::DataEntry { + path: "Vehicle.Test.AnotherTest2".into(), + value: None, + actuator_target: None, + metadata: Some(root::proto::v1::Metadata { + data_type: root::proto::v1::DataType::Boolean.into(), + entry_type: root::proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + }), + }); + } + + let completer = CliCompleter::from_metadata(&metadata); + + assert_eq!(completer.paths.children.len(), 1); + assert_eq!(completer.paths.children["vehicle"].children.len(), 2); + + match completer.complete_entry_path("") { + Some(completions) => { + assert_eq!(completions.len(), 1); + assert_eq!(completions[0].display(), "Vehicle."); + } + None => panic!("expected completions, got None"), + } + + match completer.complete_entry_path("v") { + Some(completions) => { + assert_eq!(completions.len(), 1); + assert_eq!(completions[0].display(), "Vehicle."); + } + None => panic!("expected completions, got None"), + } + + match completer.complete_entry_path("vehicle.") { + Some(completions) => { + assert_eq!(completions.len(), 2); + assert_eq!(completions[0].display(), "AnotherTest."); + assert_eq!(completions[1].display(), "Test."); + } + None => panic!("expected completions, got None"), + } + + match completer.complete_entry_path("vehicle") { + Some(completions) => { + assert_eq!(completions.len(), 2); + assert_eq!(completions[0].display(), "AnotherTest."); + assert_eq!(completions[1].display(), "Test."); + } + None => panic!("expected completions, got None"), + } + } + + #[test] + fn test_alignment() { + let max = 7; + assert_eq!("hej 1 4", format!("{: [[PATH] ...]", "Get signal value(s)"), + ("set", " ", "Set actuator signal"), + ( + "subscribe", + "", + "Subscribe to signals with QUERY, if you use kuksa feature comma separated list", + ), + ("feed", " ", "Publish signal value"), + ( + "metadata", + "[PATTERN]", + "Fetch metadata. Provide PATTERN to list metadata of signals matching pattern.", + ), + ("token", "", "Use TOKEN as access token"), + ( + "token-file", + "", + "Use content of FILE as access token", + ), + ("help", "", "You're looking at it."), + ("quit", "", "Quit"), +]; + +fn print_usage(command: impl AsRef) { + for (cmd, usage, _) in CLI_COMMANDS { + if *cmd == command.as_ref() { + println!("Usage: {cmd} {usage}"); + } + } +} + +pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ + println!("Using {VERSION}"); + + let mut subscription_nbr = 1; + + let completer = CliCompleter::new(); + let interface = Arc::new(Interface::new("client")?); + interface.set_completer(Arc::new(completer)); + + interface.define_function("enter-function", Arc::new(cli::EnterFunction)); + interface.bind_sequence("\r", Command::from_str("enter-function")); + interface.bind_sequence("\n", Command::from_str("enter-function")); + + cli::set_disconnected_prompt(&interface); + + let mut cli = _cli; + let mut client = KuksaClient::new(common::to_uri(cli.get_server())?); + + if let Some(token_filename) = cli.get_token_file() { + let token = std::fs::read_to_string(token_filename)?; + client.basic_client.set_access_token(token)?; + } + + #[cfg(feature = "tls")] + if let Some(ca_cert_filename) = cli.get_ca_cert() { + let pem = std::fs::read(ca_cert_filename)?; + let ca_cert = tonic::transport::Certificate::from_pem(pem); + + let tls_config = tonic::transport::ClientTlsConfig::new().ca_certificate(ca_cert); + + client.basic_client.set_tls_config(tls_config); + } + + let mut connection_state_subscription = client.basic_client.subscribe_to_connection_state(); + let interface_ref = interface.clone(); + + tokio::spawn(async move { + while let Some(state) = connection_state_subscription.next().await { + match state { + Ok(state) => match state { + common::ConnectionState::Connected => { + cli::set_connected_prompt(&interface_ref, VERSION.to_string()); + } + common::ConnectionState::Disconnected => { + cli::set_disconnected_prompt(&interface_ref); + } + }, + Err(err) => { + cli::print_error( + "connection", + format!("Connection state subscription failed: {err}"), + ) + .unwrap_or_default(); + } + } + } + }); + + match cli.get_command() { + Some(cli::Commands::Get { paths }) => { + match client.get_current_values(paths).await { + Ok(data_entries) => { + for entry in data_entries { + if let Some(val) = entry.value { + println!("{}: {}", entry.path, DisplayDatapoint(val),); + } else { + println!("{}: NotAvailable", entry.path); + } + } + } + Err(err) => { + eprintln!("{err}"); + } + } + return Ok(()); + } + None => { + // No subcommand => run interactive client + let version = match option_env!("CARGO_PKG_VERSION") { + Some(version) => format!("v{version}"), + None => String::new(), + }; + cli::print_logo(version); + + match client.basic_client.try_connect().await { + Ok(()) => { + cli::print_info(format!( + "Successfully connected to {}", + client.basic_client.get_uri() + ))?; + + let pattern = vec!["**"]; + + match client.get_metadata(pattern).await { + Ok(metadata) => { + interface + .set_completer(Arc::new(CliCompleter::from_metadata(&metadata))); + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err("metadata", &status)?; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error("metadata", msg)?; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt("metadata", format_args!("Error {msg:?}"))?; + } + } + } + Err(err) => { + cli::print_error("connect", format!("{err}"))?; + } + } + } + }; + + loop { + if let Some(res) = interface.read_line_step(Some(TIMEOUT))? { + match res { + ReadResult::Input(line) => { + let (cmd, args) = cli::split_first_word(&line); + match cmd { + "help" => { + println!(); + for &(cmd, args, help) in CLI_COMMANDS { + println!(" {:24} {}", format!("{cmd} {args}"), help); + } + println!(); + } + "get" => { + interface.add_history_unique(line.clone()); + + if args.is_empty() { + print_usage(cmd); + continue; + } + let paths = args + .split_whitespace() + .map(|path| path.to_owned()) + .collect(); + match client.get_current_values(paths).await { + Ok(data_entries) => { + cli::print_resp_ok(cmd)?; + for entry in data_entries { + if let Some(val) = entry.value { + println!( + "{}: {}", + entry.path, + DisplayDatapoint(val), + ); + } else { + println!("{}: NotAvailable", entry.path); + } + } + } + Err(common::ClientError::Status(err)) => { + cli::print_resp_err(cmd, &err)?; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error(cmd, msg)?; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } + } + } + "token" => { + interface.add_history_unique(line.clone()); + + if args.is_empty() { + print_usage(cmd); + continue; + } + + match client.basic_client.set_access_token(args) { + Ok(()) => { + cli::print_info("Access token set.")?; + match client.get_metadata(vec![]).await { + Ok(metadata) => { + interface.set_completer(Arc::new( + CliCompleter::from_metadata(&metadata), + )); + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err("metadata", &status)?; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error("metadata", msg)?; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt( + "metadata", + format_args!("Error {msg:?}"), + )?; + } + } + } + Err(err) => cli::print_error(cmd, &format!("Malformed token: {err}"))?, + } + } + "token-file" => { + interface.add_history_unique(line.clone()); + + if args.is_empty() { + print_usage(cmd); + continue; + } + + let token_filename = args.trim(); + match std::fs::read_to_string(token_filename) { + Ok(token) => match client.basic_client.set_access_token(token) { + Ok(()) => { + cli::print_info("Access token set.")?; + match client.get_metadata(vec![]).await { + Ok(metadata) => { + interface.set_completer(Arc::new( + CliCompleter::from_metadata(&metadata), + )); + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err("metadata", &status)?; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error("metadata", msg)?; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )?; + } + } + } + Err(err) => { + cli::print_error(cmd, &format!("Malformed token: {err}"))? + } + }, + Err(err) => cli::print_error( + cmd, + &format!( + "Failed to open token file \"{token_filename}\": {err}" + ), + )?, + } + } + "set" => { + interface.add_history_unique(line.clone()); + + let (path, value) = cli::split_first_word(args); + + if value.is_empty() { + print_usage(cmd); + continue; + } + + let datapoint_entries = match client.get_metadata(vec![path]).await + { + Ok(data_entries) => Some(data_entries), + Err(common::ClientError::Status(status)) => { + cli::print_resp_err("metadata", &status)?; + None + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error("metadata", msg)?; + None + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + None + } + }; + + if let Some(entries) = datapoint_entries { + for entry in entries { + if let Some(metadata) = entry.metadata { + let data_value = try_into_data_value( + value, + proto::v1::DataType::from_i32( + metadata.data_type, + ) + .unwrap(), + ); + if data_value.is_err() { + println!( + "Could not parse \"{value}\" as {:?}", + proto::v1::DataType::from_i32( + metadata.data_type + ) + .unwrap() + ); + continue; + } + + if metadata.entry_type + != proto::v1::EntryType::Actuator.into() + { + cli::print_error( + cmd, + format!("{} is not an actuator.", path), + )?; + cli::print_info( + "If you want to provide the signal value, use `feed`.", + )?; + continue; + } + + let ts = Timestamp::from(SystemTime::now()); + let datapoints = HashMap::from([( + path.to_string().clone(), + proto::v1::Datapoint { + timestamp: Some(ts), + value: Some(data_value.unwrap()), + }, + )]); + + match client.set_current_values(datapoints).await { + Ok(_) => cli::print_resp_ok(cmd)?, + Err(common::ClientError::Status(status)) => { + cli::print_resp_err(cmd, &status)? + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error(cmd, msg)? + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )? + } + } + } + } + } + } + "feed" => { + interface.add_history_unique(line.clone()); + + let (path, value) = cli::split_first_word(args); + + if value.is_empty() { + print_usage(cmd); + continue; + } + + let datapoint_entries = match client.get_metadata(vec![path]).await + { + Ok(data_entries) => Some(data_entries), + Err(common::ClientError::Status(status)) => { + cli::print_resp_err("metadata", &status)?; + None + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error("metadata", msg)?; + None + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + None + } + }; + + if let Some(entries) = datapoint_entries { + for entry in entries { + if let Some(metadata) = entry.metadata { + let data_value = try_into_data_value( + value, + proto::v1::DataType::from_i32( + metadata.data_type, + ) + .unwrap(), + ); + if data_value.is_err() { + println!( + "Could not parse \"{}\" as {:?}", + value, + proto::v1::DataType::from_i32( + metadata.data_type + ) + .unwrap() + ); + continue; + } + let ts = Timestamp::from(SystemTime::now()); + let datapoints = HashMap::from([( + path.to_string().clone(), + proto::v1::Datapoint { + timestamp: Some(ts), + value: Some(data_value.unwrap()), + }, + )]); + + match client.set_current_values(datapoints).await { + Ok(_) => { + cli::print_resp_ok(cmd)?; + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err(cmd, &status)? + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error(cmd, msg)? + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )?; + } + } + } + } + } + } + "subscribe" => { + interface.add_history_unique(line.clone()); + + if args.is_empty() { + print_usage(cmd); + continue; + } + + let input = args.split_whitespace().collect::>(); + + match client.subscribe(input).await { + Ok(mut subscription) => { + let iface = interface.clone(); + tokio::spawn(async move { + let sub_disp = format!("[{subscription_nbr}]"); + let sub_disp_pad = " ".repeat(sub_disp.len()); + let sub_disp_color = + format!("{}", Color::White.dimmed().paint(&sub_disp)); + + loop { + match subscription.message().await { + Ok(subscribe_resp) => { + if let Some(resp) = subscribe_resp { + // Build output before writing it + // (to avoid interleaving confusion) + use std::fmt::Write; + let mut output = String::new(); + let mut first_line = true; + for update in resp.updates { + if first_line { + first_line = false; + write!( + output, + "{} ", + &sub_disp_color, + ) + .unwrap(); + } else { + write!( + output, + "{} ", + &sub_disp_pad, + ) + .unwrap(); + } + if let Some(entry) = update.entry { + if let Some(value) = entry.value + { + writeln!( + output, + "{}: {}", + entry.path, + DisplayDatapoint(value) + ) + .unwrap(); + } + } + } + write!(iface, "{output}").unwrap(); + } else { + writeln!( + iface, + "{} {}", + Color::Red.dimmed().paint(&sub_disp), + Color::White.dimmed().paint( + "Server gone. Subscription stopped" + ), + ) + .unwrap(); + break; + } + } + Err(err) => { + write!( + iface, + "{} {}", + &sub_disp_color, + Color::Red + .dimmed() + .paint(format!("Channel error: {err}")) + ) + .unwrap(); + break; + } + } + } + }); + + cli::print_resp_ok(cmd)?; + cli::print_info(format!( + "Subscription is now running in the background. Received data is identified by [{subscription_nbr}]." + ) + )?; + subscription_nbr += 1; + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err(cmd, &status)? + } + Err(common::ClientError::Connection(msg)) => cli::print_error(cmd, msg)?, + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))? + } + } + } + "connect" => { + interface.add_history_unique(line.clone()); + if !client.basic_client.is_connected() || !args.is_empty() { + if args.is_empty() { + match client.basic_client.try_connect().await { + Ok(()) => { + cli::print_info(format!( + "[{cmd}] Successfully connected to {}", + client.basic_client.get_uri() + ))?; + } + Err(err) => { + cli::print_error(cmd, format!("{err}"))?; + } + } + } else { + match common::to_uri(args) { + Ok(valid_uri) => { + match client + .basic_client + .try_connect_to(valid_uri) + .await + { + Ok(()) => { + cli::print_info(format!( + "[{cmd}] Successfully connected to {}", + client.basic_client.get_uri() + ))?; + } + Err(err) => { + cli::print_error(cmd, format!("{err}"))?; + } + } + } + Err(err) => { + cli::print_error( + cmd, + format!("Failed to parse endpoint address: {err}"), + )?; + } + } + }; + if client.basic_client.is_connected() { + match client.get_metadata(vec![]).await { + Ok(metadata) => { + interface.set_completer(Arc::new( + CliCompleter::from_metadata(&metadata), + )); + #[cfg(feature = "feature_sdv")] + { + _properties = metadata; + } + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err("metadata", &status)?; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error("metadata", msg)?; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } + } + } + }; + } + "metadata" => { + interface.add_history_unique(line.clone()); + + let paths = args.split_whitespace().collect::>(); + + if paths.is_empty() { + cli::print_info("If you want to list metadata of signals, use `metadata PATTERN`")?; + } else { + match client.get_metadata(paths).await { + Ok(metadata) => { + cli::print_resp_ok(cmd)?; + if !metadata.is_empty() { + let max_len_path = + metadata.iter().fold(0, |mut max_len, item| { + if item.path.len() > max_len { + max_len = item.path.len(); + } + max_len + }); + + cli::print_info(format!( + "{: { + cli::print_resp_err(cmd, &status)?; + continue; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error(cmd, msg)?; + continue; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } + } + } + } + "quit" | "exit" => { + println!("Bye bye!"); + break; + } + "" => {} // Ignore empty input + _ => { + println!( + "Unknown command. See `help` for a list of available commands." + ); + interface.add_history_unique(line.clone()); + } + } + } + ReadResult::Eof => { + println!("Bye bye!"); + break; + } + ReadResult::Signal(sig) => { + // println!("received signal: {:?}", sig); + if sig == linefeed::Signal::Interrupt { + interface.cancel_read_line()?; + } + + let _ = writeln!(interface, "signal received: {sig:?}"); + } + } + } + } + + Ok(()) +} + +struct CliCompleter { + paths: PathPart, +} + +#[derive(Debug)] +struct PathPart { + rel_path: String, + full_path: String, + children: HashMap, +} + +impl PathPart { + fn new() -> Self { + PathPart { + rel_path: "".into(), + full_path: "".into(), + children: HashMap::new(), + } + } +} +impl CliCompleter { + fn new() -> CliCompleter { + CliCompleter { + paths: PathPart::new(), + } + } + + fn from_metadata(entries: &Vec) -> CliCompleter { + let mut root = PathPart::new(); + for entry in entries { + let mut parent = &mut root; + let parts = entry.path.split('.'); + for part in parts { + let full_path = match parent.full_path.as_str() { + "" => part.to_owned(), + _ => format!("{}.{}", parent.full_path, part), + }; + let entry = parent + .children + .entry(part.to_lowercase()) + .or_insert(PathPart { + rel_path: part.to_owned(), + full_path, + children: HashMap::new(), + }); + + parent = entry; + } + } + CliCompleter { paths: root } + } + + fn complete_entry_path(&self, word: &str) -> Option> { + if !self.paths.children.is_empty() { + let mut res = Vec::new(); + + let lowercase_word = word.to_lowercase(); + let mut parts = lowercase_word.split('.'); + let mut path = &self.paths; + loop { + match parts.next() { + Some(part) => { + match path.children.get(part) { + Some(matching_path) => { + path = matching_path; + } + None => { + // match partial + for (path_part_lower, path_spec) in &path.children { + if path_part_lower.starts_with(part) { + if !path_spec.children.is_empty() { + // This is a branch + res.push(Completion { + completion: format!("{}.", path_spec.full_path), + display: Some(format!("{}.", path_spec.rel_path)), + suffix: Suffix::None, + }); + } else { + res.push(Completion { + completion: path_spec.full_path.to_owned(), + display: Some(path_spec.rel_path.to_owned()), + suffix: Suffix::Default, + }); + } + } + } + break; + } + } + } + None => { + for path_spec in path.children.values() { + if !path_spec.children.is_empty() { + // This is a branch + res.push(Completion { + completion: format!("{}.", path_spec.full_path), + display: Some(format!("{}.", path_spec.rel_path)), + suffix: Suffix::None, + }); + } else { + res.push(Completion { + completion: path_spec.full_path.to_owned(), + display: Some(path_spec.rel_path.to_owned()), + suffix: Suffix::Default, + }); + } + } + break; + } + } + } + + res.sort_by(|a, b| a.display().cmp(&b.display())); + Some(res) + } else { + None + } + } +} + +impl Completer for CliCompleter { + fn complete( + &self, + word: &str, + prompter: &Prompter, + start: usize, + _end: usize, + ) -> Option> { + let line = prompter.buffer(); + + let mut words = line[..start].split_whitespace(); + + match words.next() { + // Complete command name + None => { + let mut compls = Vec::new(); + + for &(cmd, _, _) in CLI_COMMANDS { + if cmd.starts_with(word) { + compls.push(Completion { + completion: cmd.to_owned(), + display: None, + suffix: Suffix::default(), //Suffix::Some('('), + }); + } + } + + Some(compls) + } + // Complete command parameters + Some("set") | Some("feed") => { + if words.count() == 0 { + self.complete_entry_path(word) + } else { + None + } + } + Some("get") | Some("metadata") => self.complete_entry_path(word), + Some("subscribe") => { + if words.count() == 0 { + self.complete_entry_path(word) + } else { + None + } + } + Some("token-file") => { + let path_completer = linefeed::complete::PathCompleter; + path_completer.complete(word, prompter, start, _end) + } + _ => None, + } + } +} + + +struct DisplayDataType(Option); +struct DisplayEntryType(Option); +struct DisplayDatapoint(proto::v1::Datapoint); + +fn display_array(f: &mut fmt::Formatter<'_>, array: &[T]) -> fmt::Result +where + T: fmt::Display, +{ + f.write_str("[")?; + let real_delimiter = ", "; + let mut delimiter = ""; + for value in array { + write!(f, "{delimiter}")?; + delimiter = real_delimiter; + write!(f, "{value}")?; + } + f.write_str("]") +} + +impl fmt::Display for DisplayDatapoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.0.value { + Some(value) => match value { + proto::v1::datapoint::Value::Bool(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::Int32(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::Int64(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::Uint32(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::Uint64(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::Float(value) => { + f.pad(&format!("{value:.2}")) + } + proto::v1::datapoint::Value::Double(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::String(value) => { + f.pad(&format!("'{value}'")) + } + proto::v1::datapoint::Value::StringArray(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::BoolArray(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::Int32Array(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::Int64Array(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::Uint32Array(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::Uint64Array(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::FloatArray(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::DoubleArray(array) => { + display_array(f, &array.values) + } + }, + None => f.pad("None"), + } + } +} + +impl From> for DisplayEntryType { + fn from(input: Option) -> Self { + DisplayEntryType(input) + } +} + +impl fmt::Display for DisplayEntryType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Some(entry_type) => f.pad(&format!("{entry_type:?}")), + None => f.pad("Unknown"), + } + } +} + +impl From> for DisplayDataType { + fn from(input: Option) -> Self { + DisplayDataType(input) + } +} + +impl fmt::Display for DisplayDataType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Some(data_type) => f.pad(&format!("{data_type:?}")), + None => f.pad("Unknown"), + } + } +} + +fn try_into_data_value( + input: &str, + data_type: proto::v1::DataType, +) -> Result { + if input == "NotAvailable" { + return Ok(proto::v1::datapoint::Value::String(input.to_string())); + } + + match data_type { + proto::v1::DataType::String => { + Ok(proto::v1::datapoint::Value::String(input.to_owned())) + } + proto::v1::DataType::StringArray => { + match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::StringArray( + proto::v1::StringArray { values: value }, + )), + Err(err) => Err(err), + } + } + proto::v1::DataType::Boolean => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Bool(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::BooleanArray => match cli::get_array_from_input(input.to_owned()) + { + Ok(value) => Ok(proto::v1::datapoint::Value::BoolArray( + proto::v1::BoolArray { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Int8 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32(value as i32)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Int8Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32Array( + proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Int16 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32(value as i32)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Int16Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32Array( + proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Int32 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Int32Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32Array( + proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Int64 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Int64(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Int64Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Int64Array( + proto::v1::Int64Array { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Uint8 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32(value as u32)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Uint8Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( + proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Uint16 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32(value as u32)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Uint16Array => { + match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( + proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } + proto::v1::DataType::Uint32 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Uint32Array => { + match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( + proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } + proto::v1::DataType::Uint64 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint64(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Uint64Array => { + match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint64Array( + proto::v1::Uint64Array { values: value }, + )), + Err(err) => Err(err), + } + } + proto::v1::DataType::Float => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Float(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::FloatArray => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::FloatArray( + proto::v1::FloatArray { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Double => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Double(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::DoubleArray => { + match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::DoubleArray( + proto::v1::DoubleArray { values: value }, + )), + Err(err) => Err(err), + } + } + _ => Err(ParseError {}), + } +} + + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn test_parse_values() { + // String + assert!(matches!( + try_into_data_value("test", proto::v1::DataType::String), + Ok(proto::v1::datapoint::Value::String(value)) if value == "test" + )); + + // StringArray + assert!(matches!( + try_into_data_value("[test, test2, test4]", proto::v1::DataType::StringArray), + Ok(proto::v1::datapoint::Value::StringArray(value)) if value == proto::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} + )); + + // Bool + assert!(matches!( + try_into_data_value("true", proto::v1::DataType::Boolean), + Ok(proto::v1::datapoint::Value::Bool(value)) if value + )); + + assert!(matches!( + try_into_data_value("false", proto::v1::DataType::Boolean), + Ok(proto::v1::datapoint::Value::Bool(value)) if !value + )); + assert!(try_into_data_value("truefalse", proto::v1::DataType::Boolean).is_err()); + // BoolArray + assert!(matches!( + try_into_data_value("[true, false, true]", proto::v1::DataType::BooleanArray), + Ok(proto::v1::datapoint::Value::BoolArray(value)) if value == proto::v1::BoolArray{values: vec![true, false, true]} + )); + + // Int8 + assert!(matches!( + try_into_data_value("100", proto::v1::DataType::Int8), + Ok(proto::v1::datapoint::Value::Int32(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", proto::v1::DataType::Int8), + Ok(proto::v1::datapoint::Value::Int32(value)) if value == -100 + )); + assert!(try_into_data_value("300", proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-300", proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-100.1", proto::v1::DataType::Int8).is_err()); + + // Int16 + assert!(matches!( + try_into_data_value("100", proto::v1::DataType::Int16), + Ok(proto::v1::datapoint::Value::Int32(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", proto::v1::DataType::Int16), + Ok(proto::v1::datapoint::Value::Int32(value)) if value == -100 + )); + assert!(matches!( + try_into_data_value("32000", proto::v1::DataType::Int16), + Ok(proto::v1::datapoint::Value::Int32(value)) if value == 32000 + )); + assert!(matches!( + try_into_data_value("-32000", proto::v1::DataType::Int16), + Ok(proto::v1::datapoint::Value::Int32(value)) if value == -32000 + )); + assert!(try_into_data_value("33000", proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-33000", proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-32000.1", proto::v1::DataType::Int16).is_err()); +} + + #[test] + fn test_entry_path_completion() { + #[allow(unused_mut, unused_assignments)] + let mut metadata = Vec::new(); + metadata.push(proto::v1::DataEntry { + path: "Vehicle.Test.Test1".into(), + value: None, + actuator_target: None, + metadata: Some(proto::v1::Metadata { + data_type: proto::v1::DataType::Boolean.into(), + entry_type: proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + }), + }); + metadata.push(proto::v1::DataEntry { + path: "Vehicle.Test.AnotherTest1".into(), + value: None, + actuator_target: None, + metadata: Some(proto::v1::Metadata { + data_type: proto::v1::DataType::Boolean.into(), + entry_type: proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + }), + }); + metadata.push(proto::v1::DataEntry { + path: "Vehicle.Test.AnotherTest2".into(), + value: None, + actuator_target: None, + metadata: Some(proto::v1::Metadata { + data_type: proto::v1::DataType::Boolean.into(), + entry_type: proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + }), + }); + + let completer = CliCompleter::from_metadata(&metadata); + + assert_eq!(completer.paths.children.len(), 1); + assert_eq!(completer.paths.children["vehicle"].children.len(), 2); + + match completer.complete_entry_path("") { + Some(completions) => { + assert_eq!(completions.len(), 1); + assert_eq!(completions[0].display(), "Vehicle."); + } + None => panic!("expected completions, got None"), + } + + match completer.complete_entry_path("v") { + Some(completions) => { + assert_eq!(completions.len(), 1); + assert_eq!(completions[0].display(), "Vehicle."); + } + None => panic!("expected completions, got None"), + } + + match completer.complete_entry_path("vehicle.") { + Some(completions) => { + assert_eq!(completions.len(), 2); + assert_eq!(completions[0].display(), "AnotherTest."); + assert_eq!(completions[1].display(), "Test."); + } + None => panic!("expected completions, got None"), + } + + match completer.complete_entry_path("vehicle") { + Some(completions) => { + assert_eq!(completions.len(), 2); + assert_eq!(completions[0].display(), "AnotherTest."); + assert_eq!(completions[1].display(), "Test."); + } + None => panic!("expected completions, got None"), + } + } + + #[test] + fn test_alignment() { + let max = 7; + assert_eq!("hej 1 4", format!("{: [[PATH] ...]", "Get signal value(s)"), - ("set", " ", "Set actuator signal"), - ( - "subscribe", - "", - "Subscribe to signals with QUERY, if you use kuksa feature comma separated list", - ), - ("feed", " ", "Publish signal value"), - ( - "metadata", - "[PATTERN]", - "Fetch metadata. Provide PATTERN to list metadata of signals matching pattern.", - ), - ("token", "", "Use TOKEN as access token"), - ( - "token-file", - "", - "Use content of FILE as access token", - ), - ("help", "", "You're looking at it."), - ("quit", "", "Quit"), -]; - -#[cfg(feature = "feature_kuksa")] -mod root { - pub use databroker_proto::kuksa::val as proto; - pub use kuksa::*; -} - -#[cfg(feature = "feature_sdv")] -mod root { - pub use databroker_proto::sdv::databroker as proto; - pub use sdv::*; -} - -#[derive(Debug, Parser)] -#[clap(author, version, about, long_about = None)] -struct Cli { - /// Server to connect to - #[clap(long, display_order = 1, default_value = "http://127.0.0.1:55555")] - server: String, - - // #[clap(short, long)] - // port: Option, - /// File containing access token - #[clap(long, value_name = "FILE", display_order = 2)] - token_file: Option, - - /// CA certificate used to verify server certificate - #[cfg(feature = "tls")] - #[clap(long, value_name = "CERT", display_order = 3)] - ca_cert: Option, - - // Sub command - #[clap(subcommand)] - command: Option, -} - -#[derive(Debug, Subcommand)] -enum Commands { - /// Get one or more datapoint(s) - Get { - #[clap(value_name = "PATH")] - paths: Vec, - }, - // Subscribe, -} +mod sdv_cli; +mod kuksa_cli; +pub mod cli; #[tokio::main] -async fn main() -> Result<(), Box> { - let mut _properties = Vec::::new(); - #[cfg(feature = "feature_sdv")] - { - println!("Using sdv"); - } - #[cfg(feature = "feature_kuksa")] - { - println!("Using kuksa"); - } - - let mut subscription_nbr = 1; - - let completer = CliCompleter::new(); - let interface = Arc::new(Interface::new("client")?); - interface.set_completer(Arc::new(completer)); - - interface.define_function("enter-function", Arc::new(EnterFunction)); - interface.bind_sequence("\r", Command::from_str("enter-function")); - interface.bind_sequence("\n", Command::from_str("enter-function")); - - set_disconnected_prompt(&interface); - - let cli = Cli::parse(); - - let basic_client = common::Client::new(to_uri(cli.server)?); - let mut client; - #[cfg(feature = "feature_sdv")] - { - client = root::SDVClient::new(basic_client); - } - - #[cfg(feature = "feature_kuksa")] - { - client = root::KuksaClient::new(basic_client); - } - - if let Some(token_filename) = cli.token_file { - let token = std::fs::read_to_string(token_filename)?; - client.basic_client.set_access_token(token)?; - } - - #[cfg(feature = "tls")] - if let Some(ca_cert_filename) = cli.ca_cert { - let pem = std::fs::read(ca_cert_filename)?; - let ca_cert = tonic::transport::Certificate::from_pem(pem); - - let tls_config = tonic::transport::ClientTlsConfig::new().ca_certificate(ca_cert); - - client.basic_client.set_tls_config(tls_config); - } - - let mut connection_state_subscription = client.basic_client.subscribe_to_connection_state(); - let interface_ref = interface.clone(); - - tokio::spawn(async move { - while let Some(state) = connection_state_subscription.next().await { - match state { - Ok(state) => match state { - common::ConnectionState::Connected => { - set_connected_prompt(&interface_ref); - } - common::ConnectionState::Disconnected => { - set_disconnected_prompt(&interface_ref); - } - }, - Err(err) => { - print_error( - "connection", - format!("Connection state subscription failed: {err}"), - ) - .unwrap_or_default(); - } - } - } - }); - - match cli.command { - Some(Commands::Get { paths }) => { - #[cfg(feature = "feature_sdv")] - { - match client.get_datapoints(paths).await { - Ok(datapoints) => { - for (name, datapoint) in datapoints { - println!("{}: {}", name, DisplayDatapoint(datapoint),); - } - } - Err(err) => { - eprintln!("{err}"); - } - } - } - #[cfg(feature = "feature_kuksa")] - { - match client.get_current_values(paths).await { - Ok(data_entries) => { - for entry in data_entries { - if let Some(val) = entry.value { - println!("{}: {}", entry.path, DisplayDatapoint(val),); - } else { - println!("{}: NotAvailable", entry.path); - } - } - } - Err(err) => { - eprintln!("{err}"); - } - } - } - return Ok(()); - } - None => { - // No subcommand => run interactive client - let version = match option_env!("CARGO_PKG_VERSION") { - Some(version) => format!("v{version}"), - None => String::new(), - }; - print_logo(version); - - match client.basic_client.try_connect().await { - Ok(()) => { - print_info(format!( - "Successfully connected to {}", - client.basic_client.get_uri() - ))?; - #[allow(unused_mut, unused_assignments)] - let mut pattern = vec![]; - #[cfg(feature = "feature_kuksa")] - { - pattern = vec!["**"]; - } - match client.get_metadata(pattern).await { - Ok(metadata) => { - interface - .set_completer(Arc::new(CliCompleter::from_metadata(&metadata))); - #[cfg(feature = "feature_sdv")] - { - _properties = metadata; - } - } - Err(common::ClientError::Status(status)) => { - print_resp_err("metadata", &status)?; - } - Err(common::ClientError::Connection(msg)) => { - print_error("metadata", msg)?; - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt("metadata", format_args!("Error {msg:?}"))?; - } - } - } - Err(err) => { - print_error("connect", format!("{err}"))?; - } - } +async fn main() { + let mut cli = cli::Cli::parse(); + if cli.get_protocol() == CliAPI::SdvDatabrokerV1{ + let err = sdv_cli::sdv_main(cli.clone()).await; + match err { + Ok(_) => (), + Err(e) => eprintln!("Error: {}", e) } - }; - - loop { - if let Some(res) = interface.read_line_step(Some(TIMEOUT))? { - match res { - ReadResult::Input(line) => { - let (cmd, args) = split_first_word(&line); - match cmd { - "help" => { - println!(); - for &(cmd, args, help) in CLI_COMMANDS { - println!(" {:24} {}", format!("{cmd} {args}"), help); - } - println!(); - } - "get" => { - interface.add_history_unique(line.clone()); - - if args.is_empty() { - print_usage(cmd); - continue; - } - let paths = args - .split_whitespace() - .map(|path| path.to_owned()) - .collect(); - #[cfg(feature = "feature_sdv")] - { - match client.get_datapoints(paths).await { - Ok(datapoints) => { - print_resp_ok(cmd)?; - for (name, datapoint) in datapoints { - println!("{}: {}", name, DisplayDatapoint(datapoint),); - } - } - Err(common::ClientError::Status(err)) => { - print_resp_err(cmd, &err)?; - } - Err(common::ClientError::Connection(msg)) => { - print_error(cmd, msg)?; - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; - } - } - } - #[cfg(feature = "feature_kuksa")] - { - match client.get_current_values(paths).await { - Ok(data_entries) => { - print_resp_ok(cmd)?; - for entry in data_entries { - if let Some(val) = entry.value { - println!( - "{}: {}", - entry.path, - DisplayDatapoint(val), - ); - } else { - println!("{}: NotAvailable", entry.path); - } - } - } - Err(common::ClientError::Status(err)) => { - print_resp_err(cmd, &err)?; - } - Err(common::ClientError::Connection(msg)) => { - print_error(cmd, msg)?; - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; - } - } - } - } - "token" => { - interface.add_history_unique(line.clone()); - - if args.is_empty() { - print_usage(cmd); - continue; - } - - match client.basic_client.set_access_token(args) { - Ok(()) => { - print_info("Access token set.")?; - match client.get_metadata(vec![]).await { - Ok(metadata) => { - interface.set_completer(Arc::new( - CliCompleter::from_metadata(&metadata), - )); - #[cfg(feature = "feature_sdv")] - { - _properties = metadata; - } - } - Err(common::ClientError::Status(status)) => { - print_resp_err("metadata", &status)?; - } - Err(common::ClientError::Connection(msg)) => { - print_error("metadata", msg)?; - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt( - "metadata", - format_args!("Error {msg:?}"), - )?; - } - } - } - Err(err) => print_error(cmd, &format!("Malformed token: {err}"))?, - } - } - "token-file" => { - interface.add_history_unique(line.clone()); - - if args.is_empty() { - print_usage(cmd); - continue; - } - - let token_filename = args.trim(); - match std::fs::read_to_string(token_filename) { - Ok(token) => match client.basic_client.set_access_token(token) { - Ok(()) => { - print_info("Access token set.")?; - match client.get_metadata(vec![]).await { - Ok(metadata) => { - interface.set_completer(Arc::new( - CliCompleter::from_metadata(&metadata), - )); - #[cfg(feature = "feature_sdv")] - { - _properties = metadata; - } - } - Err(common::ClientError::Status(status)) => { - print_resp_err("metadata", &status)?; - } - Err(common::ClientError::Connection(msg)) => { - print_error("metadata", msg)?; - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt( - cmd, - format_args!("Error {msg:?}"), - )?; - } - } - } - Err(err) => { - print_error(cmd, &format!("Malformed token: {err}"))? - } - }, - Err(err) => print_error( - cmd, - &format!( - "Failed to open token file \"{token_filename}\": {err}" - ), - )?, - } - } - "set" => { - interface.add_history_unique(line.clone()); - - let (path, value) = split_first_word(args); - - if value.is_empty() { - print_usage(cmd); - continue; - } - - #[cfg(feature = "feature_sdv")] - { - let datapoint_metadata = { - let mut datapoint_metadata = None; - for metadata in _properties.iter() { - if metadata.name == path { - datapoint_metadata = Some(metadata) - } - } - datapoint_metadata - }; - - if datapoint_metadata.is_none() { - print_info(format!( - "No metadata available for {path}. Needed to determine data type for serialization." - ))?; - continue; - } - - if let Some(metadata) = datapoint_metadata { - let data_value = try_into_data_value( - value, - root::proto::v1::DataType::from_i32(metadata.data_type) - .unwrap(), - ); - if data_value.is_err() { - println!( - "Could not parse \"{value}\" as {:?}", - root::proto::v1::DataType::from_i32(metadata.data_type) - .unwrap() - ); - continue; - } - - if metadata.entry_type - != root::proto::v1::EntryType::Actuator.into() - { - print_error( - cmd, - format!("{} is not an actuator.", metadata.name), - )?; - print_info( - "If you want to provide the signal value, use `feed`.", - )?; - continue; - } - - let ts = Timestamp::from(SystemTime::now()); - let datapoints = HashMap::from([( - metadata.name.clone(), - root::proto::v1::Datapoint { - timestamp: Some(ts), - value: Some(data_value.unwrap()), - }, - )]); - - match client.set_datapoints(datapoints).await { - Ok(message) => { - if message.errors.is_empty() { - print_resp_ok(cmd)?; - } else { - for (id, error) in message.errors { - match root::proto::v1::DatapointError::from_i32( - error, - ) { - Some(error) => { - print_resp_ok(cmd)?; - println!( - "Error setting {}: {}", - id, - Color::Red - .paint(format!("{error:?}")), - ); - } - None => print_resp_ok_fmt( - cmd, - format_args!("Error setting id {id}"), - )?, - } - } - } - } - Err(common::ClientError::Status(status)) => { - print_resp_err(cmd, &status)? - } - Err(common::ClientError::Connection(msg)) => { - print_error(cmd, msg)? - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; - } - } - } - } - #[cfg(feature = "feature_kuksa")] - { - let datapoint_entries = match client.get_metadata(vec![path]).await - { - Ok(data_entries) => Some(data_entries), - Err(common::ClientError::Status(status)) => { - print_resp_err("metadata", &status)?; - None - } - Err(common::ClientError::Connection(msg)) => { - print_error("metadata", msg)?; - None - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; - None - } - }; - - if let Some(entries) = datapoint_entries { - for entry in entries { - if let Some(metadata) = entry.metadata { - let data_value = try_into_data_value( - value, - root::proto::v1::DataType::from_i32( - metadata.data_type, - ) - .unwrap(), - ); - if data_value.is_err() { - println!( - "Could not parse \"{value}\" as {:?}", - root::proto::v1::DataType::from_i32( - metadata.data_type - ) - .unwrap() - ); - continue; - } - - if metadata.entry_type - != root::proto::v1::EntryType::Actuator.into() - { - print_error( - cmd, - format!("{} is not an actuator.", path), - )?; - print_info( - "If you want to provide the signal value, use `feed`.", - )?; - continue; - } - - let ts = Timestamp::from(SystemTime::now()); - let datapoints = HashMap::from([( - path.to_string().clone(), - root::proto::v1::Datapoint { - timestamp: Some(ts), - value: Some(data_value.unwrap()), - }, - )]); - - match client.set_current_values(datapoints).await { - Ok(_) => print_resp_ok(cmd)?, - Err(common::ClientError::Status(status)) => { - print_resp_err(cmd, &status)? - } - Err(common::ClientError::Connection(msg)) => { - print_error(cmd, msg)? - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt( - cmd, - format_args!("Error {msg:?}"), - )? - } - } - } - } - } - } - } - "feed" => { - interface.add_history_unique(line.clone()); - - let (path, value) = split_first_word(args); - - if value.is_empty() { - print_usage(cmd); - continue; - } - - #[cfg(feature = "feature_sdv")] - { - let datapoint_metadata = { - let mut datapoint_metadata = None; - for metadata in _properties.iter() { - if metadata.name == path { - datapoint_metadata = Some(metadata) - } - } - datapoint_metadata - }; - - if datapoint_metadata.is_none() { - print_info( - format!("No metadata available for {path}. Needed to determine data type for serialization."), - )?; - continue; - } - - if let Some(metadata) = datapoint_metadata { - let data_value = try_into_data_value( - value, - root::proto::v1::DataType::from_i32(metadata.data_type) - .unwrap(), - ); - if data_value.is_err() { - println!( - "Could not parse \"{}\" as {:?}", - value, - root::proto::v1::DataType::from_i32(metadata.data_type) - .unwrap() - ); - continue; - } - let ts = Timestamp::from(SystemTime::now()); - let datapoints = HashMap::from([( - metadata.id, - root::proto::v1::Datapoint { - timestamp: Some(ts), - value: Some(data_value.unwrap()), - }, - )]); - - match client.update_datapoints(datapoints).await { - Ok(message) => { - if message.errors.is_empty() { - print_resp_ok(cmd)? - } else { - for (id, error) in message.errors { - let identifier = if id == metadata.id { - metadata.name.to_string() - } else { - format!("id {id}") - }; - match root::proto::v1::DatapointError::from_i32(error) { - Some(error) => print_resp_ok_fmt( - cmd, - format_args!( - "Error providing {identifier}: {error:?}", - ), - )?, - None => print_resp_ok_fmt( - cmd, - format_args!("Error providing {identifier}",), - )?, - } - } - } - } - Err(common::ClientError::Status(status)) => { - print_resp_err(cmd, &status)? - } - Err(common::ClientError::Connection(msg)) => { - print_error(cmd, msg)? - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; - } - } - } - } - #[cfg(feature = "feature_kuksa")] - { - let datapoint_entries = match client.get_metadata(vec![path]).await - { - Ok(data_entries) => Some(data_entries), - Err(common::ClientError::Status(status)) => { - print_resp_err("metadata", &status)?; - None - } - Err(common::ClientError::Connection(msg)) => { - print_error("metadata", msg)?; - None - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; - None - } - }; - - if let Some(entries) = datapoint_entries { - for entry in entries { - if let Some(metadata) = entry.metadata { - let data_value = try_into_data_value( - value, - root::proto::v1::DataType::from_i32( - metadata.data_type, - ) - .unwrap(), - ); - if data_value.is_err() { - println!( - "Could not parse \"{}\" as {:?}", - value, - root::proto::v1::DataType::from_i32( - metadata.data_type - ) - .unwrap() - ); - continue; - } - let ts = Timestamp::from(SystemTime::now()); - let datapoints = HashMap::from([( - path.to_string().clone(), - root::proto::v1::Datapoint { - timestamp: Some(ts), - value: Some(data_value.unwrap()), - }, - )]); - - match client.set_current_values(datapoints).await { - Ok(_) => { - print_resp_ok(cmd)?; - } - Err(common::ClientError::Status(status)) => { - print_resp_err(cmd, &status)? - } - Err(common::ClientError::Connection(msg)) => { - print_error(cmd, msg)? - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt( - cmd, - format_args!("Error {msg:?}"), - )?; - } - } - } - } - } - } - } - "subscribe" => { - interface.add_history_unique(line.clone()); - - if args.is_empty() { - print_usage(cmd); - continue; - } - - #[allow(unused_mut)] - let mut input; - - #[cfg(feature = "feature_sdv")] - { - input = args.to_owned(); - } - #[cfg(feature = "feature_kuksa")] - { - input = args.split_whitespace().collect::>(); - } - - match client.subscribe(input).await { - Ok(mut subscription) => { - let iface = interface.clone(); - tokio::spawn(async move { - let sub_disp = format!("[{subscription_nbr}]"); - let sub_disp_pad = " ".repeat(sub_disp.len()); - let sub_disp_color = - format!("{}", Color::White.dimmed().paint(&sub_disp)); - - loop { - match subscription.message().await { - Ok(subscribe_resp) => { - if let Some(resp) = subscribe_resp { - // Build output before writing it - // (to avoid interleaving confusion) - use std::fmt::Write; - let mut output = String::new(); - let mut first_line = true; - #[cfg(feature = "feature_sdv")] - { - for (name, value) in resp.fields { - if first_line { - first_line = false; - write!( - output, - "{} ", - &sub_disp_color, - ) - .unwrap(); - } else { - write!( - output, - "{} ", - &sub_disp_pad, - ) - .unwrap(); - } - writeln!( - output, - "{}: {}", - name, - DisplayDatapoint(value) - ) - .unwrap(); - } - write!(iface, "{output}").unwrap(); - } - #[cfg(feature = "feature_kuksa")] - { - for update in resp.updates { - if first_line { - first_line = false; - write!( - output, - "{} ", - &sub_disp_color, - ) - .unwrap(); - } else { - write!( - output, - "{} ", - &sub_disp_pad, - ) - .unwrap(); - } - if let Some(entry) = update.entry { - if let Some(value) = entry.value - { - writeln!( - output, - "{}: {}", - entry.path, - DisplayDatapoint(value) - ) - .unwrap(); - } - } - } - write!(iface, "{output}").unwrap(); - } - } else { - writeln!( - iface, - "{} {}", - Color::Red.dimmed().paint(&sub_disp), - Color::White.dimmed().paint( - "Server gone. Subscription stopped" - ), - ) - .unwrap(); - break; - } - } - Err(err) => { - write!( - iface, - "{} {}", - &sub_disp_color, - Color::Red - .dimmed() - .paint(format!("Channel error: {err}")) - ) - .unwrap(); - break; - } - } - } - }); - - print_resp_ok(cmd)?; - print_info(format!( - "Subscription is now running in the background. Received data is identified by [{subscription_nbr}]." - ) - )?; - subscription_nbr += 1; - } - Err(common::ClientError::Status(status)) => { - print_resp_err(cmd, &status)? - } - Err(common::ClientError::Connection(msg)) => print_error(cmd, msg)?, - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))? - } - } - } - "connect" => { - interface.add_history_unique(line.clone()); - if !client.basic_client.is_connected() || !args.is_empty() { - if args.is_empty() { - match client.basic_client.try_connect().await { - Ok(()) => { - print_info(format!( - "[{cmd}] Successfully connected to {}", - client.basic_client.get_uri() - ))?; - } - Err(err) => { - print_error(cmd, format!("{err}"))?; - } - } - } else { - match to_uri(args) { - Ok(valid_uri) => { - match client - .basic_client - .try_connect_to(valid_uri) - .await - { - Ok(()) => { - print_info(format!( - "[{cmd}] Successfully connected to {}", - client.basic_client.get_uri() - ))?; - } - Err(err) => { - print_error(cmd, format!("{err}"))?; - } - } - } - Err(err) => { - print_error( - cmd, - format!("Failed to parse endpoint address: {err}"), - )?; - } - } - }; - if client.basic_client.is_connected() { - match client.get_metadata(vec![]).await { - Ok(metadata) => { - interface.set_completer(Arc::new( - CliCompleter::from_metadata(&metadata), - )); - #[cfg(feature = "feature_sdv")] - { - _properties = metadata; - } - } - Err(common::ClientError::Status(status)) => { - print_resp_err("metadata", &status)?; - } - Err(common::ClientError::Connection(msg)) => { - print_error("metadata", msg)?; - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; - } - } - } - }; - } - "metadata" => { - interface.add_history_unique(line.clone()); - - let paths = args.split_whitespace().collect::>(); - - #[cfg(feature = "feature_sdv")] - { - match client.get_metadata(vec![]).await { - Ok(mut metadata) => { - metadata.sort_by(|a, b| a.name.cmp(&b.name)); - _properties = metadata; - interface.set_completer(Arc::new( - CliCompleter::from_metadata(&_properties), - )); - print_resp_ok(cmd)?; - } - Err(common::ClientError::Status(status)) => { - print_resp_err(cmd, &status)?; - continue; - } - Err(common::ClientError::Connection(msg)) => { - print_error(cmd, msg)?; - continue; - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; - continue; - } - } - let mut filtered_metadata = Vec::new(); - if paths.is_empty() { - print_info("If you want to list metadata of signals, use `metadata PATTERN`")?; - // filtered_metadata.extend(&_properties); - } else { - for path in &paths { - let path_re = path_to_regex(path); - let filtered = - _properties.iter().filter(|item| match &path_re { - Ok(re) => re.is_match(&item.name), - Err(err) => { - print_info(format!("Invalid path: {err}")) - .unwrap_or_default(); - false - } - }); - filtered_metadata.extend(filtered); - } - } - - if !filtered_metadata.is_empty() { - let max_len_path = - filtered_metadata.iter().fold(0, |mut max_len, item| { - if item.name.len() > max_len { - max_len = item.name.len(); - } - max_len - }); - - print_info(format!( - "{: { - print_resp_ok(cmd)?; - if !metadata.is_empty() { - let max_len_path = - metadata.iter().fold(0, |mut max_len, item| { - if item.path.len() > max_len { - max_len = item.path.len(); - } - max_len - }); - - print_info(format!( - "{: { - print_resp_err(cmd, &status)?; - continue; - } - Err(common::ClientError::Connection(msg)) => { - print_error(cmd, msg)?; - continue; - } - Err(common::ClientError::Function(msg)) => { - print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; - } - } - } - } - } - "quit" | "exit" => { - println!("Bye bye!"); - break; - } - "" => {} // Ignore empty input - _ => { - println!( - "Unknown command. See `help` for a list of available commands." - ); - interface.add_history_unique(line.clone()); - } - } - } - ReadResult::Eof => { - println!("Bye bye!"); - break; - } - ReadResult::Signal(sig) => { - // println!("received signal: {:?}", sig); - if sig == linefeed::Signal::Interrupt { - interface.cancel_read_line()?; - } - - let _ = writeln!(interface, "signal received: {sig:?}"); - } - } - } - } - - Ok(()) -} - -fn print_usage(command: impl AsRef) { - for (cmd, usage, _) in CLI_COMMANDS { - if *cmd == command.as_ref() { - println!("Usage: {cmd} {usage}"); - } - } -} - -fn print_logo(version: impl fmt::Display) { - let mut output = io::stderr().lock(); - writeln!(output).unwrap(); - writeln!( - output, - " {}{}", - Color::Fixed(23).paint("⠀⠀⠀⢀⣤⣶⣾⣿"), - Color::White.dimmed().paint("⢸⣿⣿⣷⣶⣤⡀") - ) - .unwrap(); - writeln!( - output, - " {}{}", - Color::Fixed(23).paint("⠀⠀⣴⣿⡿⠋⣿⣿"), - Color::White.dimmed().paint("⠀⠀⠀⠈⠙⢿⣿⣦⠀") - ) - .unwrap(); - writeln!( - output, - " {}{}", - Color::Fixed(23).paint("⠀⣾⣿⠋⠀⠀⣿⣿"), - Color::White.dimmed().paint("⠀⠀⣶⣿⠀⠀⠙⣿⣷ ") - ) - .unwrap(); - writeln!( - output, - " {}{}", - Color::Fixed(23).paint("⣸⣿⠇⠀⠀⠀⣿⣿"), - Color::White - .dimmed() - .paint("⠠⣾⡿⠃⠀⠀⠀⠸⣿⣇⠀⠀⣶⠀⣠⡶⠂⠀⣶⠀⠀⢰⡆⠀⢰⡆⢀⣴⠖⠀⢠⡶⠶⠶⡦⠀⠀⠀⣰⣶⡀") - ) - .unwrap(); - writeln!( - output, - " {}{}", - Color::Fixed(23).paint("⣿⣿⠀⠀⠀⠀⠿⢿⣷⣦⡀"), - Color::White - .dimmed() - .paint("⠀⠀⠀⠀⠀⣿⣿⠀⠀⣿⢾⣏⠀⠀⠀⣿⠀⠀⢸⡇⠀⢸⡷⣿⡁⠀⠀⠘⠷⠶⠶⣦⠀⠀⢠⡟⠘⣷") - ) - .unwrap(); - writeln!( - output, - " {}{}{}{}", - Color::Fixed(23).paint("⢹⣿⡆⠀⠀⠀"), - Color::White.dimmed().paint("⣿⣶"), - Color::Fixed(23).paint("⠈⢻⣿⡆"), - Color::White - .dimmed() - .paint("⠀⠀⠀⢰⣿⡏⠀⠀⠿⠀⠙⠷⠄⠀⠙⠷⠶⠟⠁⠀⠸⠇⠈⠻⠦⠀⠐⠷⠶⠶⠟⠀⠠⠿⠁⠀⠹⠧") - ) - .unwrap(); - writeln!( - output, - " {}{}{}{}", - Color::Fixed(23).paint("⠀⢿⣿⣄⠀⠀"), - Color::White.dimmed().paint("⣿⣿"), - Color::Fixed(23).paint("⠀⠀⠿⣿"), - Color::White.dimmed().paint("⠀⠀⣠⣿⡿"), - ) - .unwrap(); - writeln!( - output, - " {}{} {}", - Color::Fixed(23).paint("⠀⠀⠻⣿⣷⡄"), - Color::White.dimmed().paint("⣿⣿⠀⠀⠀⢀⣠⣾⣿⠟"), - Color::White - .dimmed() - .paint(format!("{:<30}", "databroker-cli")), - ) - .unwrap(); - writeln!( - output, - " {}{} {}", - Color::Fixed(23).paint("⠀⠀⠀⠈⠛⠇"), - Color::White.dimmed().paint("⢿⣿⣿⣿⣿⡿⠿⠛⠁"), - Color::White.dimmed().paint(format!("{version:<30}")), - ) - .unwrap(); - writeln!(output).unwrap(); -} - -fn split_first_word(s: &str) -> (&str, &str) { - let s = s.trim(); - - match s.find(|ch: char| ch.is_whitespace()) { - Some(pos) => (&s[..pos], s[pos..].trim_start()), - None => (s, ""), } -} - -fn code_to_text(code: &tonic::Code) -> &str { - match code { - tonic::Code::Ok => "Ok", - tonic::Code::Cancelled => "Cancelled", - tonic::Code::Unknown => "Unknown", - tonic::Code::InvalidArgument => "InvalidArgument", - tonic::Code::DeadlineExceeded => "DeadlineExceeded", - tonic::Code::NotFound => "NotFound", - tonic::Code::AlreadyExists => "AlreadyExists", - tonic::Code::PermissionDenied => "PermissionDenied", - tonic::Code::ResourceExhausted => "ResourceExhausted", - tonic::Code::FailedPrecondition => "FailedPrecondition", - tonic::Code::Aborted => "Aborted", - tonic::Code::OutOfRange => "OutOfRange", - tonic::Code::Unimplemented => "Unimplemented", - tonic::Code::Internal => "Internal", - tonic::Code::Unavailable => "Unavailable", - tonic::Code::DataLoss => "DataLoss", - tonic::Code::Unauthenticated => "Unauthenticated", - } -} - -struct CliCompleter { - paths: PathPart, -} - -#[derive(Debug)] -struct PathPart { - rel_path: String, - full_path: String, - children: HashMap, -} - -impl PathPart { - fn new() -> Self { - PathPart { - rel_path: "".into(), - full_path: "".into(), - children: HashMap::new(), - } - } -} -impl CliCompleter { - fn new() -> CliCompleter { - CliCompleter { - paths: PathPart::new(), - } - } - - #[cfg(feature = "feature_sdv")] - fn from_metadata(metadata: &[root::proto::v1::Metadata]) -> CliCompleter { - let mut root = PathPart::new(); - for entry in metadata { - let mut parent = &mut root; - let parts = entry.name.split('.'); - for part in parts { - let full_path = match parent.full_path.as_str() { - "" => part.to_owned(), - _ => format!("{}.{}", parent.full_path, part), - }; - let entry = parent - .children - .entry(part.to_lowercase()) - .or_insert(PathPart { - rel_path: part.to_owned(), - full_path, - children: HashMap::new(), - }); - - parent = entry; - } - } - CliCompleter { paths: root } - } - #[cfg(feature = "feature_kuksa")] - fn from_metadata(entries: &Vec) -> CliCompleter { - let mut root = PathPart::new(); - for entry in entries { - let mut parent = &mut root; - let parts = entry.path.split('.'); - for part in parts { - let full_path = match parent.full_path.as_str() { - "" => part.to_owned(), - _ => format!("{}.{}", parent.full_path, part), - }; - let entry = parent - .children - .entry(part.to_lowercase()) - .or_insert(PathPart { - rel_path: part.to_owned(), - full_path, - children: HashMap::new(), - }); - - parent = entry; - } + else if cli.get_protocol() == CliAPI::KuksaValV1{ + let err = kuksa_cli::kuksa_main(cli.clone()).await; + match err { + Ok(_) => (), + Err(e) => eprintln!("Error: {}", e) } - CliCompleter { paths: root } } - - fn complete_entry_path(&self, word: &str) -> Option> { - if !self.paths.children.is_empty() { - let mut res = Vec::new(); - - let lowercase_word = word.to_lowercase(); - let mut parts = lowercase_word.split('.'); - let mut path = &self.paths; - loop { - match parts.next() { - Some(part) => { - match path.children.get(part) { - Some(matching_path) => { - path = matching_path; - } - None => { - // match partial - for (path_part_lower, path_spec) in &path.children { - if path_part_lower.starts_with(part) { - if !path_spec.children.is_empty() { - // This is a branch - res.push(Completion { - completion: format!("{}.", path_spec.full_path), - display: Some(format!("{}.", path_spec.rel_path)), - suffix: Suffix::None, - }); - } else { - res.push(Completion { - completion: path_spec.full_path.to_owned(), - display: Some(path_spec.rel_path.to_owned()), - suffix: Suffix::Default, - }); - } - } - } - break; - } - } - } - None => { - for path_spec in path.children.values() { - if !path_spec.children.is_empty() { - // This is a branch - res.push(Completion { - completion: format!("{}.", path_spec.full_path), - display: Some(format!("{}.", path_spec.rel_path)), - suffix: Suffix::None, - }); - } else { - res.push(Completion { - completion: path_spec.full_path.to_owned(), - display: Some(path_spec.rel_path.to_owned()), - suffix: Suffix::Default, - }); - } - } - break; - } - } - } - - res.sort_by(|a, b| a.display().cmp(&b.display())); - Some(res) - } else { - None - } - } -} - -fn set_connected_prompt(interface: &Arc>) { - let mut _text; - #[cfg(feature = "feature_sdv")] - { - _text = "sdv.databroker.v1" - } - #[cfg(feature = "feature_kuksa")] - { - _text = "kuksa.val.v1" - } - let connected_prompt = format!( - "\x01{prefix}\x02{text}\x01{suffix}\x02 > ", - prefix = Color::Green.prefix(), - text = _text, - suffix = Color::Green.suffix() - ); - interface.set_prompt(&connected_prompt).unwrap(); -} - -fn set_disconnected_prompt(interface: &Arc>) { - let disconnected_prompt = format!( - "\x01{prefix}\x02{text}\x01{suffix}\x02 > ", - prefix = Color::Red.prefix(), - text = "not connected", - suffix = Color::Red.suffix() - ); - interface.set_prompt(&disconnected_prompt).unwrap(); -} - -fn to_uri(uri: impl AsRef) -> Result { - let uri = uri - .as_ref() - .parse::() - .map_err(|err| format!("{err}"))?; - let mut parts = uri.into_parts(); - - if parts.scheme.is_none() { - parts.scheme = Some("http".parse().expect("http should be valid scheme")); - } - - match &parts.authority { - Some(_authority) => { - // match (authority.port_u16(), port) { - // (Some(uri_port), Some(port)) => { - // if uri_port != port { - // parts.authority = format!("{}:{}", authority.host(), port) - // .parse::() - // .map_err(|err| format!("{}", err)) - // .ok(); - // } - // } - // (_, _) => {} - // } - } - None => return Err("No server uri specified".to_owned()), - } - parts.path_and_query = Some("".parse().expect("uri path should be empty string")); - tonic::transport::Uri::from_parts(parts).map_err(|err| format!("{err}")) -} - -#[allow(dead_code)] -fn path_to_regex(path: impl AsRef) -> Result { - let path_as_re = format!( - // Match the whole line (from left '^' to right '$') - "^{}$", - path.as_ref().replace('.', r"\.").replace('*', r"(.*)") - ); - regex::Regex::new(&path_as_re) -} - -fn print_resp_err(operation: impl AsRef, err: &tonic::Status) -> io::Result<()> { - let mut output = io::stderr().lock(); - output.write_fmt(format_args!( - "{} {} {}", - Color::White - .dimmed() - .paint(format!("[{}]", operation.as_ref())), - Color::White - .on(Color::Red) - .paint(format!(" {} ", code_to_text(&err.code()))), - err.message(), - ))?; - output.write_all(b"\n")?; - output.flush() -} - -fn print_resp_err_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { - let mut stderr = io::stderr().lock(); - let mut stdout = io::stdout().lock(); - write_resp_ok(&mut stderr, operation)?; - stdout.write_fmt(fmt)?; - stdout.write_all(b"\n")?; - stdout.flush() -} - -#[allow(dead_code)] -fn print_resp_ok_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { - let mut stderr = io::stderr().lock(); - let mut stdout = io::stdout().lock(); - write_resp_ok(&mut stderr, operation)?; - stdout.write_fmt(fmt)?; - stdout.write_all(b"\n")?; - stdout.flush() -} - -fn print_resp_ok(operation: impl AsRef) -> io::Result<()> { - let mut output = io::stderr().lock(); - write_resp_ok(&mut output, operation)?; - output.write_all(b"\n")?; - output.flush() -} - -fn write_resp_ok(output: &mut impl Write, operation: impl AsRef) -> io::Result<()> { - output.write_fmt(format_args!( - "{} {} ", - Color::White - .dimmed() - .paint(format!("[{}]", operation.as_ref())), - Color::Black.on(Color::Green).paint(" OK "), - )) -} - -fn print_info(info: impl AsRef) -> io::Result<()> { - let mut output = io::stderr().lock(); - output.write_fmt(format_args!( - "{}\n", - Color::White.dimmed().paint(info.as_ref()), - ))?; - output.flush() -} - -fn print_error(operation: impl AsRef, msg: impl AsRef) -> io::Result<()> { - let mut output = io::stderr().lock(); - output.write_fmt(format_args!( - "{} {} {}\n", - Color::White - .dimmed() - .paint(format!("[{}]", operation.as_ref())), - Color::White.on(Color::Red).paint(" Error "), - msg.as_ref(), - ))?; - output.flush() -} - -impl Completer for CliCompleter { - fn complete( - &self, - word: &str, - prompter: &Prompter, - start: usize, - _end: usize, - ) -> Option> { - let line = prompter.buffer(); - - let mut words = line[..start].split_whitespace(); - - match words.next() { - // Complete command name - None => { - let mut compls = Vec::new(); - - for &(cmd, _, _) in CLI_COMMANDS { - if cmd.starts_with(word) { - compls.push(Completion { - completion: cmd.to_owned(), - display: None, - suffix: Suffix::default(), //Suffix::Some('('), - }); - } - } - - Some(compls) - } - // Complete command parameters - Some("set") | Some("feed") => { - if words.count() == 0 { - self.complete_entry_path(word) - } else { - None - } - } - Some("get") | Some("metadata") => self.complete_entry_path(word), - Some("subscribe") => { - #[cfg(feature = "feature_sdv")] - { - match words.next() { - None => Some(vec![Completion::simple("SELECT".to_owned())]), - Some(next) => { - if next == "SELECT" { - self.complete_entry_path(word) - } else { - None - } - } - } - } - #[cfg(feature = "feature_kuksa")] - { - if words.count() == 0 { - self.complete_entry_path(word) - } else { - None - } - } - } - Some("token-file") => { - let path_completer = linefeed::complete::PathCompleter; - path_completer.complete(word, prompter, start, _end) - } - _ => None, - } - } -} - -struct EnterFunction; - -impl Function for EnterFunction { - fn execute(&self, prompter: &mut Prompter, count: i32, _ch: char) -> io::Result<()> { - if prompter - .buffer() - .trim() - // .to_lowercase() - .starts_with("subscribe") - { - if prompter.buffer().ends_with('\n') { - let len = prompter.buffer().len(); - prompter.delete_range(len - 1..len)?; - prompter.accept_input() - } else if count > 0 { - // Start multiline - prompter.insert_str("\n") - } else { - Ok(()) - } - } else { - prompter.accept_input() - } - } -} - -struct DisplayDataType(Option); -struct DisplayEntryType(Option); -// !!! ChangeType currently just exists in old API needs to be removed or added later !!! -struct DisplayChangeType(Option); -struct DisplayDatapoint(root::proto::v1::Datapoint); - -fn display_array(f: &mut fmt::Formatter<'_>, array: &[T]) -> fmt::Result -where - T: fmt::Display, -{ - f.write_str("[")?; - let real_delimiter = ", "; - let mut delimiter = ""; - for value in array { - write!(f, "{delimiter}")?; - delimiter = real_delimiter; - write!(f, "{value}")?; - } - f.write_str("]") -} - -impl fmt::Display for DisplayDatapoint { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(feature = "feature_sdv")] - { - match &self.0.value { - Some(value) => match value { - root::proto::v1::datapoint::Value::BoolValue(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::FailureValue(failure) => f.pad(&format!( - "( {:?} )", - root::proto::v1::datapoint::Failure::from_i32(*failure).unwrap() - )), - root::proto::v1::datapoint::Value::Int32Value(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::Int64Value(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::Uint32Value(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::Uint64Value(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::FloatValue(value) => { - f.pad(&format!("{value:.2}")) - } - root::proto::v1::datapoint::Value::DoubleValue(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::StringValue(value) => { - f.pad(&format!("'{value}'")) - } - root::proto::v1::datapoint::Value::StringArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::BoolArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Int32Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Int64Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Uint32Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Uint64Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::FloatArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::DoubleArray(array) => { - display_array(f, &array.values) - } - }, - None => f.pad("None"), - } - } - #[cfg(feature = "feature_kuksa")] - { - match &self.0.value { - Some(value) => match value { - root::proto::v1::datapoint::Value::Bool(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Int32(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Int64(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Uint32(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Uint64(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Float(value) => { - f.pad(&format!("{value:.2}")) - } - root::proto::v1::datapoint::Value::Double(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::String(value) => { - f.pad(&format!("'{value}'")) - } - root::proto::v1::datapoint::Value::StringArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::BoolArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Int32Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Int64Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Uint32Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Uint64Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::FloatArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::DoubleArray(array) => { - display_array(f, &array.values) - } - }, - None => f.pad("None"), - } - } - } -} - -impl From> for DisplayEntryType { - fn from(input: Option) -> Self { - DisplayEntryType(input) - } -} - -impl fmt::Display for DisplayEntryType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - Some(entry_type) => f.pad(&format!("{entry_type:?}")), - None => f.pad("Unknown"), - } - } -} - -impl From> for DisplayDataType { - fn from(input: Option) -> Self { - DisplayDataType(input) - } -} - -impl fmt::Display for DisplayDataType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - Some(data_type) => f.pad(&format!("{data_type:?}")), - None => f.pad("Unknown"), - } - } -} - -impl From> for DisplayChangeType { - fn from(input: Option) -> Self { - DisplayChangeType(input) - } -} -impl fmt::Display for DisplayChangeType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - Some(data_type) => f.pad(&format!("{data_type:?}")), - None => f.pad("Unknown"), - } - } -} - -#[derive(Debug)] -struct ParseError {} - -impl std::error::Error for ParseError {} - -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("parse error") - } -} - -fn get_array_from_input(values: String) -> Result, ParseError> { - let raw_input = values - .strip_prefix('[') - .and_then(|s| s.strip_suffix(']')) - .ok_or(ParseError {})?; - - let pattern = r#"(?:\\.|[^",])*"(?:\\.|[^"])*"|[^",]+"#; - - let regex = regex::Regex::new(pattern).unwrap(); - let inputs = regex.captures_iter(raw_input); - - let mut array: Vec = vec![]; - for part in inputs { - match part[0] - .trim() - .replace('\"', "") - .replace('\\', "\"") - .parse::() - { - Ok(value) => array.push(value), - Err(_) => return Err(ParseError {}), - } - } - Ok(array) -} - -fn try_into_data_value( - input: &str, - data_type: root::proto::v1::DataType, -) -> Result { - if input == "NotAvailable" { - #[cfg(feature = "feature_sdv")] - { - return Ok(root::proto::v1::datapoint::Value::FailureValue( - root::proto::v1::datapoint::Failure::NotAvailable as i32, - )); - } - #[cfg(feature = "feature_kuksa")] - { - return Ok(root::proto::v1::datapoint::Value::String(input.to_string())); - } - } - - #[cfg(feature = "feature_sdv")] - { - match data_type { - root::proto::v1::DataType::String => Ok( - root::proto::v1::datapoint::Value::StringValue(input.to_owned()), - ), - root::proto::v1::DataType::StringArray => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( - root::proto::v1::StringArray { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Bool => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolValue(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::BoolArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolArray( - root::proto::v1::BoolArray { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int8 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value as i32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int8Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int16 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value as i32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int16Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int32 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int32Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int64 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Value(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int64Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Array( - root::proto::v1::Int64Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Uint8 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value as u32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint8Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Uint16 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value as u32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint16Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Uint32 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint32Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Uint64 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Value(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint64Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( - root::proto::v1::Uint64Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Float => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatValue(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::FloatArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatArray( - root::proto::v1::FloatArray { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Double => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleValue(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::DoubleArray => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( - root::proto::v1::DoubleArray { values: value }, - )), - Err(err) => Err(err), - } - } - _ => Err(ParseError {}), - } - } - #[cfg(feature = "feature_kuksa")] - { - match data_type { - root::proto::v1::DataType::String => { - Ok(root::proto::v1::datapoint::Value::String(input.to_owned())) - } - root::proto::v1::DataType::StringArray => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( - root::proto::v1::StringArray { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Boolean => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Bool(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::BooleanArray => match get_array_from_input(input.to_owned()) - { - Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolArray( - root::proto::v1::BoolArray { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int8 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value as i32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int8Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int16 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value as i32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int16Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int32 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int32Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int64 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int64Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Array( - root::proto::v1::Int64Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Uint8 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value as u32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint8Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Uint16 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value as u32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint16Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Uint32 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint32Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Uint64 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint64Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( - root::proto::v1::Uint64Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Float => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Float(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::FloatArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatArray( - root::proto::v1::FloatArray { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Double => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Double(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::DoubleArray => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( - root::proto::v1::DoubleArray { values: value }, - )), - Err(err) => Err(err), - } - } - _ => Err(ParseError {}), - } - } -} - -#[cfg(test)] -mod test { - - use super::*; - - #[test] - fn test_parse_values() { - #[cfg(feature = "feature_sdv")] - { - // String - assert!(matches!( - try_into_data_value("test", root::proto::v1::DataType::String), - Ok(root::proto::v1::datapoint::Value::StringValue(value)) if value == "test" - )); - - // StringArray - assert!(matches!( - try_into_data_value("[test, test2, test4]", root::proto::v1::DataType::StringArray), - Ok(root::proto::v1::datapoint::Value::StringArray(value)) if value == root::proto::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} - )); - - // Bool - assert!(matches!( - try_into_data_value("true", root::proto::v1::DataType::Bool), - Ok(root::proto::v1::datapoint::Value::BoolValue(value)) if value - )); - - assert!(matches!( - try_into_data_value("false", root::proto::v1::DataType::Bool), - Ok(root::proto::v1::datapoint::Value::BoolValue(value)) if !value - )); - assert!(try_into_data_value("truefalse", root::proto::v1::DataType::Bool).is_err()); - // BoolArray - assert!(matches!( - try_into_data_value("[true, false, true]", root::proto::v1::DataType::BoolArray), - Ok(root::proto::v1::datapoint::Value::BoolArray(value)) if value == root::proto::v1::BoolArray{values: vec![true, false, true]} - )); - - // Int8 - assert!(matches!( - try_into_data_value("100", root::proto::v1::DataType::Int8), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 100 - )); - assert!(matches!( - try_into_data_value("-100", root::proto::v1::DataType::Int8), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -100 - )); - assert!(try_into_data_value("300", root::proto::v1::DataType::Int8).is_err()); - assert!(try_into_data_value("-300", root::proto::v1::DataType::Int8).is_err()); - assert!(try_into_data_value("-100.1", root::proto::v1::DataType::Int8).is_err()); - - // Int16 - assert!(matches!( - try_into_data_value("100", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 100 - )); - assert!(matches!( - try_into_data_value("-100", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -100 - )); - assert!(matches!( - try_into_data_value("32000", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 32000 - )); - assert!(matches!( - try_into_data_value("-32000", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -32000 - )); - assert!(try_into_data_value("33000", root::proto::v1::DataType::Int16).is_err()); - assert!(try_into_data_value("-33000", root::proto::v1::DataType::Int16).is_err()); - assert!(try_into_data_value("-32000.1", root::proto::v1::DataType::Int16).is_err()); - } - #[cfg(feature = "feature_kuksa")] - { - // String - assert!(matches!( - try_into_data_value("test", root::proto::v1::DataType::String), - Ok(root::proto::v1::datapoint::Value::String(value)) if value == "test" - )); - - // StringArray - assert!(matches!( - try_into_data_value("[test, test2, test4]", root::proto::v1::DataType::StringArray), - Ok(root::proto::v1::datapoint::Value::StringArray(value)) if value == root::proto::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} - )); - - // Bool - assert!(matches!( - try_into_data_value("true", root::proto::v1::DataType::Boolean), - Ok(root::proto::v1::datapoint::Value::Bool(value)) if value - )); - - assert!(matches!( - try_into_data_value("false", root::proto::v1::DataType::Boolean), - Ok(root::proto::v1::datapoint::Value::Bool(value)) if !value - )); - assert!(try_into_data_value("truefalse", root::proto::v1::DataType::Boolean).is_err()); - // BoolArray - assert!(matches!( - try_into_data_value("[true, false, true]", root::proto::v1::DataType::BooleanArray), - Ok(root::proto::v1::datapoint::Value::BoolArray(value)) if value == root::proto::v1::BoolArray{values: vec![true, false, true]} - )); - - // Int8 - assert!(matches!( - try_into_data_value("100", root::proto::v1::DataType::Int8), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 100 - )); - assert!(matches!( - try_into_data_value("-100", root::proto::v1::DataType::Int8), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -100 - )); - assert!(try_into_data_value("300", root::proto::v1::DataType::Int8).is_err()); - assert!(try_into_data_value("-300", root::proto::v1::DataType::Int8).is_err()); - assert!(try_into_data_value("-100.1", root::proto::v1::DataType::Int8).is_err()); - - // Int16 - assert!(matches!( - try_into_data_value("100", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 100 - )); - assert!(matches!( - try_into_data_value("-100", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -100 - )); - assert!(matches!( - try_into_data_value("32000", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 32000 - )); - assert!(matches!( - try_into_data_value("-32000", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -32000 - )); - assert!(try_into_data_value("33000", root::proto::v1::DataType::Int16).is_err()); - assert!(try_into_data_value("-33000", root::proto::v1::DataType::Int16).is_err()); - assert!(try_into_data_value("-32000.1", root::proto::v1::DataType::Int16).is_err()); - } - } - - #[test] - fn test_entry_path_completion() { - #[allow(unused_mut, unused_assignments)] - let mut metadata = Vec::new(); - #[cfg(feature = "feature_sdv")] - { - metadata = [ - root::proto::v1::Metadata { - id: 1, - name: "Vehicle.Test.Test1".into(), - data_type: root::proto::v1::DataType::Bool.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - change_type: root::proto::v1::ChangeType::OnChange.into(), - description: "".into(), - }, - root::proto::v1::Metadata { - id: 2, - name: "Vehicle.AnotherTest.AnotherTest1".into(), - data_type: root::proto::v1::DataType::Bool.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - change_type: root::proto::v1::ChangeType::OnChange.into(), - description: "".into(), - }, - root::proto::v1::Metadata { - id: 3, - name: "Vehicle.AnotherTest.AnotherTest2".into(), - data_type: root::proto::v1::DataType::Bool.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - change_type: root::proto::v1::ChangeType::OnChange.into(), - description: "".into(), - }, - ] - .to_vec(); - } - - #[cfg(feature = "feature_kuksa")] - { - metadata.push(root::proto::v1::DataEntry { - path: "Vehicle.Test.Test1".into(), - value: None, - actuator_target: None, - metadata: Some(root::proto::v1::Metadata { - data_type: root::proto::v1::DataType::Boolean.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - comment: None, - deprecation: None, - unit: None, - value_restriction: None, - entry_specific: None, - description: Some("".to_string()), - }), - }); - metadata.push(root::proto::v1::DataEntry { - path: "Vehicle.Test.AnotherTest1".into(), - value: None, - actuator_target: None, - metadata: Some(root::proto::v1::Metadata { - data_type: root::proto::v1::DataType::Boolean.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - comment: None, - deprecation: None, - unit: None, - value_restriction: None, - entry_specific: None, - description: Some("".to_string()), - }), - }); - metadata.push(root::proto::v1::DataEntry { - path: "Vehicle.Test.AnotherTest2".into(), - value: None, - actuator_target: None, - metadata: Some(root::proto::v1::Metadata { - data_type: root::proto::v1::DataType::Boolean.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - comment: None, - deprecation: None, - unit: None, - value_restriction: None, - entry_specific: None, - description: Some("".to_string()), - }), - }); - } - - let completer = CliCompleter::from_metadata(&metadata); - - assert_eq!(completer.paths.children.len(), 1); - assert_eq!(completer.paths.children["vehicle"].children.len(), 2); - - match completer.complete_entry_path("") { - Some(completions) => { - assert_eq!(completions.len(), 1); - assert_eq!(completions[0].display(), "Vehicle."); - } - None => panic!("expected completions, got None"), - } - - match completer.complete_entry_path("v") { - Some(completions) => { - assert_eq!(completions.len(), 1); - assert_eq!(completions[0].display(), "Vehicle."); - } - None => panic!("expected completions, got None"), - } - - match completer.complete_entry_path("vehicle.") { - Some(completions) => { - assert_eq!(completions.len(), 2); - assert_eq!(completions[0].display(), "AnotherTest."); - assert_eq!(completions[1].display(), "Test."); - } - None => panic!("expected completions, got None"), - } - - match completer.complete_entry_path("vehicle") { - Some(completions) => { - assert_eq!(completions.len(), 2); - assert_eq!(completions[0].display(), "AnotherTest."); - assert_eq!(completions[1].display(), "Test."); - } - None => panic!("expected completions, got None"), - } - } - - #[test] - fn test_alignment() { - let max = 7; - assert_eq!("hej 1 4", format!("{: [[PATH] ...]", "Get signal value(s)"), + ("set", " ", "Set actuator signal"), + ( + "subscribe", + "", + "Subscribe to signals with QUERY, if you use kuksa feature comma separated list", + ), + ("feed", " ", "Publish signal value"), + ( + "metadata", + "[PATTERN]", + "Fetch metadata. Provide PATTERN to list metadata of signals matching pattern.", + ), + ("token", "", "Use TOKEN as access token"), + ( + "token-file", + "", + "Use content of FILE as access token", + ), + ("help", "", "You're looking at it."), + ("quit", "", "Quit"), +]; + +fn print_usage(command: impl AsRef) { + for (cmd, usage, _) in CLI_COMMANDS { + if *cmd == command.as_ref() { + println!("Usage: {cmd} {usage}"); + } + } +} + +pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ + let mut _properties = Vec::::new(); + println!("Using {VERSION}"); + let mut cli = _cli; + + let mut subscription_nbr = 1; + + let completer = CliCompleter::new(); + let interface = Arc::new(Interface::new("client")?); + interface.set_completer(Arc::new(completer)); + + interface.define_function("enter-function", Arc::new(cli::EnterFunction)); + interface.bind_sequence("\r", Command::from_str("enter-function")); + interface.bind_sequence("\n", Command::from_str("enter-function")); + + cli::set_disconnected_prompt(&interface); + + let mut client = SDVClient::new(common::to_uri(cli.get_server())?); + + if let Some(token_filename) = cli.get_token_file() { + let token = std::fs::read_to_string(token_filename)?; + client.basic_client.set_access_token(token)?; + } + + #[cfg(feature = "tls")] + if let Some(ca_cert_filename) = cli.get_ca_cert() { + let pem = std::fs::read(ca_cert_filename)?; + let ca_cert = tonic::transport::Certificate::from_pem(pem); + + let tls_config = tonic::transport::ClientTlsConfig::new().ca_certificate(ca_cert); + + client.basic_client.set_tls_config(tls_config); + } + + let mut connection_state_subscription = client.basic_client.subscribe_to_connection_state(); + let interface_ref = interface.clone(); + + tokio::spawn(async move { + while let Some(state) = connection_state_subscription.next().await { + match state { + Ok(state) => match state { + common::ConnectionState::Connected => { + cli::set_connected_prompt(&interface_ref, VERSION.to_string()); + } + common::ConnectionState::Disconnected => { + cli::set_disconnected_prompt(&interface_ref); + } + }, + Err(err) => { + cli::print_error( + "connection", + format!("Connection state subscription failed: {err}"), + ) + .unwrap_or_default(); + } + } + } + }); + + match cli.get_command() { + Some(cli::Commands::Get { paths }) => { + match client.get_datapoints(paths).await { + Ok(datapoints) => { + for (name, datapoint) in datapoints { + println!("{}: {}", name, DisplayDatapoint(datapoint),); + } + } + Err(err) => { + eprintln!("{err}"); + } + } + return Ok(()); + } + None => { + // No subcommand => run interactive client + let version = match option_env!("CARGO_PKG_VERSION") { + Some(version) => format!("v{version}"), + None => String::new(), + }; + cli::print_logo(version); + + match client.basic_client.try_connect().await { + Ok(()) => { + cli::print_info(format!( + "Successfully connected to {}", + client.basic_client.get_uri() + ))?; + + let pattern = vec![]; + + match client.get_metadata(pattern).await { + Ok(metadata) => { + interface + .set_completer(Arc::new(CliCompleter::from_metadata(&metadata))); + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err("metadata", &status)?; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error("metadata", msg)?; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt("metadata", format_args!("Error {msg:?}"))?; + } + } + } + Err(err) => { + cli::print_error("connect", format!("{err}"))?; + } + } + } + }; + + loop { + if let Some(res) = interface.read_line_step(Some(TIMEOUT))? { + match res { + ReadResult::Input(line) => { + let (cmd, args) = cli::split_first_word(&line); + match cmd { + "help" => { + println!(); + for &(cmd, args, help) in CLI_COMMANDS { + println!(" {:24} {}", format!("{cmd} {args}"), help); + } + println!(); + } + "get" => { + interface.add_history_unique(line.clone()); + + if args.is_empty() { + print_usage(cmd); + continue; + } + let paths = args + .split_whitespace() + .map(|path| path.to_owned()) + .collect(); + + match client.get_datapoints(paths).await { + Ok(datapoints) => { + cli::print_resp_ok(cmd)?; + for (name, datapoint) in datapoints { + println!("{}: {}", name, DisplayDatapoint(datapoint),); + } + } + Err(common::ClientError::Status(err)) => { + cli::print_resp_err(cmd, &err)?; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error(cmd, msg)?; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } + } + } + "token" => { + interface.add_history_unique(line.clone()); + + if args.is_empty() { + print_usage(cmd); + continue; + } + + match client.basic_client.set_access_token(args) { + Ok(()) => { + cli::print_info("Access token set.")?; + match client.get_metadata(vec![]).await { + Ok(metadata) => { + interface.set_completer(Arc::new( + CliCompleter::from_metadata(&metadata), + )); + _properties = metadata; + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err("metadata", &status)?; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error("metadata", msg)?; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt( + "metadata", + format_args!("Error {msg:?}"), + )?; + } + } + } + Err(err) => cli::print_error(cmd, &format!("Malformed token: {err}"))?, + } + } + "token-file" => { + interface.add_history_unique(line.clone()); + + if args.is_empty() { + print_usage(cmd); + continue; + } + + let token_filename = args.trim(); + match std::fs::read_to_string(token_filename) { + Ok(token) => match client.basic_client.set_access_token(token) { + Ok(()) => { + cli::print_info("Access token set.")?; + match client.get_metadata(vec![]).await { + Ok(metadata) => { + interface.set_completer(Arc::new( + CliCompleter::from_metadata(&metadata), + )); + _properties = metadata; + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err("metadata", &status)?; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error("metadata", msg)?; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )?; + } + } + } + Err(err) => { + cli::print_error(cmd, &format!("Malformed token: {err}"))? + } + }, + Err(err) => cli::print_error( + cmd, + &format!( + "Failed to open token file \"{token_filename}\": {err}" + ), + )?, + } + } + "set" => { + interface.add_history_unique(line.clone()); + + let (path, value) = cli::split_first_word(args); + + if value.is_empty() { + print_usage(cmd); + continue; + } + + + let datapoint_metadata = { + let mut datapoint_metadata = None; + for metadata in _properties.iter() { + if metadata.name == path { + datapoint_metadata = Some(metadata) + } + } + datapoint_metadata + }; + + if datapoint_metadata.is_none() { + cli::print_info(format!( + "No metadata available for {path}. Needed to determine data type for serialization." + ))?; + continue; + } + + if let Some(metadata) = datapoint_metadata { + let data_value = try_into_data_value( + value, + proto::v1::DataType::from_i32(metadata.data_type) + .unwrap(), + ); + if data_value.is_err() { + println!( + "Could not parse \"{value}\" as {:?}", + proto::v1::DataType::from_i32(metadata.data_type) + .unwrap() + ); + continue; + } + + if metadata.entry_type + != proto::v1::EntryType::Actuator.into() + { + cli::print_error( + cmd, + format!("{} is not an actuator.", metadata.name), + )?; + cli::print_info( + "If you want to provide the signal value, use `feed`.", + )?; + continue; + } + + let ts = Timestamp::from(SystemTime::now()); + let datapoints = HashMap::from([( + metadata.name.clone(), + proto::v1::Datapoint { + timestamp: Some(ts), + value: Some(data_value.unwrap()), + }, + )]); + + match client.set_datapoints(datapoints).await { + Ok(message) => { + if message.errors.is_empty() { + cli::print_resp_ok(cmd)?; + } else { + for (id, error) in message.errors { + match proto::v1::DatapointError::from_i32( + error, + ) { + Some(error) => { + cli::print_resp_ok(cmd)?; + println!( + "Error setting {}: {}", + id, + Color::Red + .paint(format!("{error:?}")), + ); + } + None => cli::print_resp_ok_fmt( + cmd, + format_args!("Error setting id {id}"), + )?, + } + } + } + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err(cmd, &status)? + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error(cmd, msg)? + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } + } + } + } + "feed" => { + interface.add_history_unique(line.clone()); + + let (path, value) = cli::split_first_word(args); + + if value.is_empty() { + print_usage(cmd); + continue; + } + + + let datapoint_metadata = { + let mut datapoint_metadata = None; + for metadata in _properties.iter() { + if metadata.name == path { + datapoint_metadata = Some(metadata) + } + } + datapoint_metadata + }; + + if datapoint_metadata.is_none() { + cli::print_info( + format!("No metadata available for {path}. Needed to determine data type for serialization."), + )?; + continue; + } + + if let Some(metadata) = datapoint_metadata { + let data_value = try_into_data_value( + value, + proto::v1::DataType::from_i32(metadata.data_type) + .unwrap(), + ); + if data_value.is_err() { + println!( + "Could not parse \"{}\" as {:?}", + value, + proto::v1::DataType::from_i32(metadata.data_type) + .unwrap() + ); + continue; + } + let ts = Timestamp::from(SystemTime::now()); + let datapoints = HashMap::from([( + metadata.id, + proto::v1::Datapoint { + timestamp: Some(ts), + value: Some(data_value.unwrap()), + }, + )]); + + match client.update_datapoints(datapoints).await { + Ok(message) => { + if message.errors.is_empty() { + cli::print_resp_ok(cmd)? + } else { + for (id, error) in message.errors { + let identifier = if id == metadata.id { + metadata.name.to_string() + } else { + format!("id {id}") + }; + match proto::v1::DatapointError::from_i32(error) { + Some(error) => cli::print_resp_ok_fmt( + cmd, + format_args!( + "Error providing {identifier}: {error:?}", + ), + )?, + None => cli::print_resp_ok_fmt( + cmd, + format_args!("Error providing {identifier}",), + )?, + } + } + } + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err(cmd, &status)? + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error(cmd, msg)? + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } + } + } + } + "subscribe" => { + interface.add_history_unique(line.clone()); + + if args.is_empty() { + print_usage(cmd); + continue; + } + + let input = args.to_owned(); + + match client.subscribe(input).await { + Ok(mut subscription) => { + let iface = interface.clone(); + tokio::spawn(async move { + let sub_disp = format!("[{subscription_nbr}]"); + let sub_disp_pad = " ".repeat(sub_disp.len()); + let sub_disp_color = + format!("{}", Color::White.dimmed().paint(&sub_disp)); + + loop { + match subscription.message().await { + Ok(subscribe_resp) => { + if let Some(resp) = subscribe_resp { + // Build output before writing it + // (to avoid interleaving confusion) + use std::fmt::Write; + let mut output = String::new(); + let mut first_line = true; + for (name, value) in resp.fields { + if first_line { + first_line = false; + write!( + output, + "{} ", + &sub_disp_color, + ) + .unwrap(); + } else { + write!( + output, + "{} ", + &sub_disp_pad, + ) + .unwrap(); + } + writeln!( + output, + "{}: {}", + name, + DisplayDatapoint(value) + ) + .unwrap(); + } + write!(iface, "{output}").unwrap(); + } else { + writeln!( + iface, + "{} {}", + Color::Red.dimmed().paint(&sub_disp), + Color::White.dimmed().paint( + "Server gone. Subscription stopped" + ), + ) + .unwrap(); + break; + } + } + Err(err) => { + write!( + iface, + "{} {}", + &sub_disp_color, + Color::Red + .dimmed() + .paint(format!("Channel error: {err}")) + ) + .unwrap(); + break; + } + } + } + }); + + cli::print_resp_ok(cmd)?; + cli::print_info(format!( + "Subscription is now running in the background. Received data is identified by [{subscription_nbr}]." + ) + )?; + subscription_nbr += 1; + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err(cmd, &status)? + } + Err(common::ClientError::Connection(msg)) => cli::print_error(cmd, msg)?, + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))? + } + } + } + "connect" => { + interface.add_history_unique(line.clone()); + if !client.basic_client.is_connected() || !args.is_empty() { + if args.is_empty() { + match client.basic_client.try_connect().await { + Ok(()) => { + cli::print_info(format!( + "[{cmd}] Successfully connected to {}", + client.basic_client.get_uri() + ))?; + } + Err(err) => { + cli::print_error(cmd, format!("{err}"))?; + } + } + } else { + match cli::to_uri(args) { + Ok(valid_uri) => { + match client + .basic_client + .try_connect_to(valid_uri) + .await + { + Ok(()) => { + cli::print_info(format!( + "[{cmd}] Successfully connected to {}", + client.basic_client.get_uri() + ))?; + } + Err(err) => { + cli::print_error(cmd, format!("{err}"))?; + } + } + } + Err(err) => { + cli::print_error( + cmd, + format!("Failed to parse endpoint address: {err}"), + )?; + } + } + }; + if client.basic_client.is_connected() { + match client.get_metadata(vec![]).await { + Ok(metadata) => { + interface.set_completer(Arc::new( + CliCompleter::from_metadata(&metadata), + )); + _properties = metadata; + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err("metadata", &status)?; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error("metadata", msg)?; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + } + } + } + }; + } + "metadata" => { + interface.add_history_unique(line.clone()); + + let paths = args.split_whitespace().collect::>(); + + + match client.get_metadata(vec![]).await { + Ok(mut metadata) => { + metadata.sort_by(|a, b| a.name.cmp(&b.name)); + _properties = metadata; + interface.set_completer(Arc::new( + CliCompleter::from_metadata(&_properties), + )); + cli::print_resp_ok(cmd)?; + } + Err(common::ClientError::Status(status)) => { + cli::print_resp_err(cmd, &status)?; + continue; + } + Err(common::ClientError::Connection(msg)) => { + cli::print_error(cmd, msg)?; + continue; + } + Err(common::ClientError::Function(msg)) => { + cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + continue; + } + } + let mut filtered_metadata = Vec::new(); + if paths.is_empty() { + cli::print_info("If you want to list metadata of signals, use `metadata PATTERN`")?; + // filtered_metadata.extend(&_properties); + } else { + for path in &paths { + let path_re = path_to_regex(path); + let filtered = + _properties.iter().filter(|item| match &path_re { + Ok(re) => re.is_match(&item.name), + Err(err) => { + cli::print_info(format!("Invalid path: {err}")) + .unwrap_or_default(); + false + } + }); + filtered_metadata.extend(filtered); + } + } + + if !filtered_metadata.is_empty() { + let max_len_path = + filtered_metadata.iter().fold(0, |mut max_len, item| { + if item.name.len() > max_len { + max_len = item.name.len(); + } + max_len + }); + + cli::print_info(format!( + "{: { + println!("Bye bye!"); + break Ok(()); + } + "" => {} // Ignore empty input + _ => { + println!( + "Unknown command. See `help` for a list of available commands." + ); + interface.add_history_unique(line.clone()); + } + } + } + ReadResult::Eof => { + println!("Bye bye!"); + break Ok(()); + } + ReadResult::Signal(sig) => { + // println!("received signal: {:?}", sig); + if sig == linefeed::Signal::Interrupt { + interface.cancel_read_line()?; + } + + let _ = writeln!(interface, "signal received: {sig:?}"); + } + } + } + } +} + + +struct CliCompleter { + paths: PathPart, +} + +#[derive(Debug)] +struct PathPart { + rel_path: String, + full_path: String, + children: HashMap, +} + +impl PathPart { + fn new() -> Self { + PathPart { + rel_path: "".into(), + full_path: "".into(), + children: HashMap::new(), + } + } +} +impl CliCompleter { + fn new() -> CliCompleter { + CliCompleter { + paths: PathPart::new(), + } + } + + fn from_metadata(metadata: &[proto::v1::Metadata]) -> CliCompleter { + let mut root = PathPart::new(); + for entry in metadata { + let mut parent = &mut root; + let parts = entry.name.split('.'); + for part in parts { + let full_path = match parent.full_path.as_str() { + "" => part.to_owned(), + _ => format!("{}.{}", parent.full_path, part), + }; + let entry = parent + .children + .entry(part.to_lowercase()) + .or_insert(PathPart { + rel_path: part.to_owned(), + full_path, + children: HashMap::new(), + }); + + parent = entry; + } + } + CliCompleter { paths: root } + } + + fn complete_entry_path(&self, word: &str) -> Option> { + if !self.paths.children.is_empty() { + let mut res = Vec::new(); + + let lowercase_word = word.to_lowercase(); + let mut parts = lowercase_word.split('.'); + let mut path = &self.paths; + loop { + match parts.next() { + Some(part) => { + match path.children.get(part) { + Some(matching_path) => { + path = matching_path; + } + None => { + // match partial + for (path_part_lower, path_spec) in &path.children { + if path_part_lower.starts_with(part) { + if !path_spec.children.is_empty() { + // This is a branch + res.push(Completion { + completion: format!("{}.", path_spec.full_path), + display: Some(format!("{}.", path_spec.rel_path)), + suffix: Suffix::None, + }); + } else { + res.push(Completion { + completion: path_spec.full_path.to_owned(), + display: Some(path_spec.rel_path.to_owned()), + suffix: Suffix::Default, + }); + } + } + } + break; + } + } + } + None => { + for path_spec in path.children.values() { + if !path_spec.children.is_empty() { + // This is a branch + res.push(Completion { + completion: format!("{}.", path_spec.full_path), + display: Some(format!("{}.", path_spec.rel_path)), + suffix: Suffix::None, + }); + } else { + res.push(Completion { + completion: path_spec.full_path.to_owned(), + display: Some(path_spec.rel_path.to_owned()), + suffix: Suffix::Default, + }); + } + } + break; + } + } + } + + res.sort_by(|a, b| a.display().cmp(&b.display())); + Some(res) + } else { + None + } + } +} + +impl Completer for CliCompleter { + fn complete( + &self, + word: &str, + prompter: &Prompter, + start: usize, + _end: usize, + ) -> Option> { + let line = prompter.buffer(); + + let mut words = line[..start].split_whitespace(); + + match words.next() { + // Complete command name + None => { + let mut compls = Vec::new(); + + for &(cmd, _, _) in CLI_COMMANDS { + if cmd.starts_with(word) { + compls.push(Completion { + completion: cmd.to_owned(), + display: None, + suffix: Suffix::default(), //Suffix::Some('('), + }); + } + } + + Some(compls) + } + // Complete command parameters + Some("set") | Some("feed") => { + if words.count() == 0 { + self.complete_entry_path(word) + } else { + None + } + } + Some("get") | Some("metadata") => self.complete_entry_path(word), + Some("subscribe") => { + match words.next() { + None => Some(vec![Completion::simple("SELECT".to_owned())]), + Some(next) => { + if next == "SELECT" { + self.complete_entry_path(word) + } else { + None + } + } + } + } + Some("token-file") => { + let path_completer = linefeed::complete::PathCompleter; + path_completer.complete(word, prompter, start, _end) + } + _ => None, + } + } +} + +struct DisplayDataType(Option); +struct DisplayEntryType(Option); +// !!! ChangeType currently just exists in old API needs to be removed or added later !!! +struct DisplayChangeType(Option); +struct DisplayDatapoint(proto::v1::Datapoint); + +fn display_array(f: &mut fmt::Formatter<'_>, array: &[T]) -> fmt::Result +where + T: fmt::Display, +{ + f.write_str("[")?; + let real_delimiter = ", "; + let mut delimiter = ""; + for value in array { + write!(f, "{delimiter}")?; + delimiter = real_delimiter; + write!(f, "{value}")?; + } + f.write_str("]") +} + +impl fmt::Display for DisplayDatapoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match &self.0.value { + Some(value) => match value { + proto::v1::datapoint::Value::BoolValue(value) => { + f.pad(&format!("{value}")) + } + proto::v1::datapoint::Value::FailureValue(failure) => f.pad(&format!( + "( {:?} )", + proto::v1::datapoint::Failure::from_i32(*failure).unwrap() + )), + proto::v1::datapoint::Value::Int32Value(value) => { + f.pad(&format!("{value}")) + } + proto::v1::datapoint::Value::Int64Value(value) => { + f.pad(&format!("{value}")) + } + proto::v1::datapoint::Value::Uint32Value(value) => { + f.pad(&format!("{value}")) + } + proto::v1::datapoint::Value::Uint64Value(value) => { + f.pad(&format!("{value}")) + } + proto::v1::datapoint::Value::FloatValue(value) => { + f.pad(&format!("{value:.2}")) + } + proto::v1::datapoint::Value::DoubleValue(value) => { + f.pad(&format!("{value}")) + } + proto::v1::datapoint::Value::StringValue(value) => { + f.pad(&format!("'{value}'")) + } + proto::v1::datapoint::Value::StringArray(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::BoolArray(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::Int32Array(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::Int64Array(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::Uint32Array(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::Uint64Array(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::FloatArray(array) => { + display_array(f, &array.values) + } + proto::v1::datapoint::Value::DoubleArray(array) => { + display_array(f, &array.values) + } + }, + None => f.pad("None"), + } + } +} + +impl From> for DisplayEntryType { + fn from(input: Option) -> Self { + DisplayEntryType(input) + } +} + +impl fmt::Display for DisplayEntryType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Some(entry_type) => f.pad(&format!("{entry_type:?}")), + None => f.pad("Unknown"), + } + } +} + +impl From> for DisplayDataType { + fn from(input: Option) -> Self { + DisplayDataType(input) + } +} + +impl fmt::Display for DisplayDataType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Some(data_type) => f.pad(&format!("{data_type:?}")), + None => f.pad("Unknown"), + } + } +} + +impl From> for DisplayChangeType { + fn from(input: Option) -> Self { + DisplayChangeType(input) + } +} +impl fmt::Display for DisplayChangeType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0 { + Some(data_type) => f.pad(&format!("{data_type:?}")), + None => f.pad("Unknown"), + } + } +} + +fn try_into_data_value( + input: &str, + data_type: proto::v1::DataType, +) -> Result { + if input == "NotAvailable" { + return Ok(proto::v1::datapoint::Value::FailureValue( + proto::v1::datapoint::Failure::NotAvailable as i32, + )); + } + match data_type { + proto::v1::DataType::String => Ok( + proto::v1::datapoint::Value::StringValue(input.to_owned()), + ), + proto::v1::DataType::StringArray => { + match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::StringArray( + proto::v1::StringArray { values: value }, + )), + Err(err) => Err(err), + } + } + proto::v1::DataType::Bool => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::BoolValue(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::BoolArray => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::BoolArray( + proto::v1::BoolArray { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Int8 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32Value(value as i32)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Int8Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32Array( + proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Int16 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32Value(value as i32)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Int16Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32Array( + proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Int32 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32Value(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Int32Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Int32Array( + proto::v1::Int32Array { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Int64 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Int64Value(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Int64Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Int64Array( + proto::v1::Int64Array { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Uint8 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Value(value as u32)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Uint8Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( + proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Uint16 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Value(value as u32)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Uint16Array => { + match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( + proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } + proto::v1::DataType::Uint32 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Value(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Uint32Array => { + match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( + proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + } + } + proto::v1::DataType::Uint64 => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint64Value(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::Uint64Array => { + match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint64Array( + proto::v1::Uint64Array { values: value }, + )), + Err(err) => Err(err), + } + } + proto::v1::DataType::Float => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::FloatValue(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::FloatArray => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::FloatArray( + proto::v1::FloatArray { values: value }, + )), + Err(err) => Err(err), + }, + proto::v1::DataType::Double => match input.parse::() { + Ok(value) => Ok(proto::v1::datapoint::Value::DoubleValue(value)), + Err(_) => Err(ParseError {}), + }, + proto::v1::DataType::DoubleArray => { + match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::DoubleArray( + proto::v1::DoubleArray { values: value }, + )), + Err(err) => Err(err), + } + } + _ => Err(ParseError {}), + } +} + +fn path_to_regex(path: impl AsRef) -> Result { + let path_as_re = format!( + // Match the whole line (from left '^' to right '$') + "^{}$", + path.as_ref().replace('.', r"\.").replace('*', r"(.*)") + ); + regex::Regex::new(&path_as_re) +} + +#[cfg(test)] +mod test { + + use super::*; + + #[test] + fn test_parse_values() { + // String + assert!(matches!( + try_into_data_value("test", proto::v1::DataType::String), + Ok(proto::v1::datapoint::Value::StringValue(value)) if value == "test" + )); + + // StringArray + assert!(matches!( + try_into_data_value("[test, test2, test4]", proto::v1::DataType::StringArray), + Ok(proto::v1::datapoint::Value::StringArray(value)) if value == proto::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} + )); + + // Bool + assert!(matches!( + try_into_data_value("true", proto::v1::DataType::Bool), + Ok(proto::v1::datapoint::Value::BoolValue(value)) if value + )); + + assert!(matches!( + try_into_data_value("false", proto::v1::DataType::Bool), + Ok(proto::v1::datapoint::Value::BoolValue(value)) if !value + )); + assert!(try_into_data_value("truefalse", proto::v1::DataType::Bool).is_err()); + // BoolArray + assert!(matches!( + try_into_data_value("[true, false, true]", proto::v1::DataType::BoolArray), + Ok(proto::v1::datapoint::Value::BoolArray(value)) if value == proto::v1::BoolArray{values: vec![true, false, true]} + )); + + // Int8 + assert!(matches!( + try_into_data_value("100", proto::v1::DataType::Int8), + Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", proto::v1::DataType::Int8), + Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == -100 + )); + assert!(try_into_data_value("300", proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-300", proto::v1::DataType::Int8).is_err()); + assert!(try_into_data_value("-100.1", proto::v1::DataType::Int8).is_err()); + + // Int16 + assert!(matches!( + try_into_data_value("100", proto::v1::DataType::Int16), + Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == 100 + )); + assert!(matches!( + try_into_data_value("-100", proto::v1::DataType::Int16), + Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == -100 + )); + assert!(matches!( + try_into_data_value("32000", proto::v1::DataType::Int16), + Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == 32000 + )); + assert!(matches!( + try_into_data_value("-32000", proto::v1::DataType::Int16), + Ok(proto::v1::datapoint::Value::Int32Value(value)) if value == -32000 + )); + assert!(try_into_data_value("33000", proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-33000", proto::v1::DataType::Int16).is_err()); + assert!(try_into_data_value("-32000.1", proto::v1::DataType::Int16).is_err()); + } + + #[test] + fn test_entry_path_completion() { + #[allow(unused_mut, unused_assignments)] + let mut metadata = Vec::new(); + metadata = [ + proto::v1::Metadata { + id: 1, + name: "Vehicle.Test.Test1".into(), + data_type: proto::v1::DataType::Bool.into(), + entry_type: proto::v1::EntryType::Sensor.into(), + change_type: proto::v1::ChangeType::OnChange.into(), + description: "".into(), + }, + proto::v1::Metadata { + id: 2, + name: "Vehicle.AnotherTest.AnotherTest1".into(), + data_type: proto::v1::DataType::Bool.into(), + entry_type: proto::v1::EntryType::Sensor.into(), + change_type: proto::v1::ChangeType::OnChange.into(), + description: "".into(), + }, + proto::v1::Metadata { + id: 3, + name: "Vehicle.AnotherTest.AnotherTest2".into(), + data_type: proto::v1::DataType::Bool.into(), + entry_type: proto::v1::EntryType::Sensor.into(), + change_type: proto::v1::ChangeType::OnChange.into(), + description: "".into(), + }, + ].to_vec(); + + let completer = CliCompleter::from_metadata(&metadata); + + assert_eq!(completer.paths.children.len(), 1); + assert_eq!(completer.paths.children["vehicle"].children.len(), 2); + + match completer.complete_entry_path("") { + Some(completions) => { + assert_eq!(completions.len(), 1); + assert_eq!(completions[0].display(), "Vehicle."); + } + None => panic!("expected completions, got None"), + } + + match completer.complete_entry_path("v") { + Some(completions) => { + assert_eq!(completions.len(), 1); + assert_eq!(completions[0].display(), "Vehicle."); + } + None => panic!("expected completions, got None"), + } + + match completer.complete_entry_path("vehicle.") { + Some(completions) => { + assert_eq!(completions.len(), 2); + assert_eq!(completions[0].display(), "AnotherTest."); + assert_eq!(completions[1].display(), "Test."); + } + None => panic!("expected completions, got None"), + } + + match completer.complete_entry_path("vehicle") { + Some(completions) => { + assert_eq!(completions.len(), 2); + assert_eq!(completions[0].display(), "AnotherTest."); + assert_eq!(completions[1].display(), "Test."); + } + None => panic!("expected completions, got None"), + } + } + + #[test] + fn test_alignment() { + let max = 7; + assert_eq!("hej 1 4", format!("{:), + Function(Vec), } impl std::error::Error for ClientError {} @@ -49,7 +50,9 @@ impl std::fmt::Display for ClientError { ClientError::Function(err) => { let formatted_result: String = err .iter() - .map(|element| element.to_string()) + .map(|element| { + format!("code: {}, message: {}, reason: {}", element.code, element.message, element.reason) + }) .collect::>() .join(", "); // Join the elements with a comma and space diff --git a/kuksa_databroker/lib/kuksa/src/client.rs b/kuksa_databroker/lib/kuksa/src/client.rs index b59e9bf7a..7d6e2651d 100644 --- a/kuksa_databroker/lib/kuksa/src/client.rs +++ b/kuksa_databroker/lib/kuksa/src/client.rs @@ -12,57 +12,115 @@ ********************************************************************************/ use std::collections::HashMap; +use http::Uri; use databroker_proto::kuksa::val::{self as proto, v1::DataEntry}; use common::{Client, ClientError}; +#[derive(Debug)] pub struct KuksaClient { pub basic_client: Client, } impl KuksaClient { - pub fn new(basic_client: Client) -> Self { - KuksaClient { basic_client } + pub fn new(uri: Uri) -> Self { + KuksaClient { basic_client: Client::new(uri) } } - pub async fn get_metadata( - &mut self, - paths: Vec<&str>, - ) -> Result, ClientError> { + async fn set(&mut self, + entry: DataEntry, + fields: Vec, + ) -> Result<(), ClientError>{ let mut client = proto::v1::val_client::ValClient::with_interceptor( self.basic_client.get_channel().await?.clone(), self.basic_client.get_auth_interceptor(), ); + let set_request = proto::v1::SetRequest { + updates: vec![proto::v1::EntryUpdate { + entry: Some(entry), + fields: fields.into(), + }], + }; + match client.set(set_request).await { + Ok(response) => { + let message = response.into_inner(); + let mut errors: Vec = Vec::new(); + if let Some(err) = message.error{ + errors.push(err); + } + for error in message.errors { + if let Some(err) = error.error { + errors.push(err); + } + } + if errors.is_empty(){ - let mut metadata_result = Vec::new(); + return Ok(()) + } + else{ + return Err(ClientError::Function(errors)) + } + } + Err(err) => return Err(ClientError::Status(err)), + } + } - for path in paths { - let get_request = proto::v1::GetRequest { - entries: vec![proto::v1::EntryRequest { - path: path.to_string(), - view: proto::v1::View::Metadata.into(), - fields: vec![proto::v1::Field::Metadata.into()], - }], - }; - - match client.get(get_request).await { - Ok(response) => { - let message = response.into_inner(); - metadata_result = message.entries; - let mut errors = Vec::new(); - for error in message.errors { - if let Some(err) = error.error { - errors.push(err.code.to_string()); - errors.push(err.reason.to_string()); - errors.push(err.message.to_string()); - } - } - if !errors.is_empty() { - return Err(ClientError::Function(errors)); + async fn get( &mut self, + path: &str, + view: proto::v1::View, + fields: Vec + )-> Result, ClientError>{ + let mut client = proto::v1::val_client::ValClient::with_interceptor( + self.basic_client.get_channel().await?.clone(), + self.basic_client.get_auth_interceptor(), + ); + + + let get_request = proto::v1::GetRequest { + entries: vec![proto::v1::EntryRequest { + path: path.to_string(), + view: view.into(), + fields: fields, + }], + }; + + match client.get(get_request).await { + Ok(response) => { + let message = response.into_inner(); + let mut errors = Vec::new(); + if let Some(err) = message.error{ + errors.push(err); + } + for error in message.errors { + if let Some(err) = error.error { + errors.push(err); } } - Err(err) => return Err(ClientError::Status(err)), + if !errors.is_empty() { + return Err(ClientError::Function(errors)); + } + else{ + // since there is only one DataEntry in the vector return only the according DataEntry + Ok(message.entries.clone()) + } + } + Err(err) => { + return Err(ClientError::Status(err)) + } + } + } + + pub async fn get_metadata( + &mut self, + paths: Vec<&str>, + ) -> Result, ClientError> { + let mut metadata_result = Vec::new(); + + for path in paths { + match self.get(path, proto::v1::View::Metadata.into(), vec![proto::v1::Field::Metadata.into()]).await{ + Ok(mut entry) => metadata_result.append(&mut entry), + Err(err) => return Err(err) } } @@ -71,41 +129,14 @@ impl KuksaClient { pub async fn get_current_values( &mut self, - paths: Vec>, + paths: Vec, ) -> Result, ClientError> { - let mut client = proto::v1::val_client::ValClient::with_interceptor( - self.basic_client.get_channel().await?.clone(), - self.basic_client.get_auth_interceptor(), - ); - let mut get_result = Vec::new(); for path in paths { - let get_request = proto::v1::GetRequest { - entries: vec![proto::v1::EntryRequest { - path: path.as_ref().to_string(), - view: proto::v1::View::CurrentValue.into(), - fields: vec![proto::v1::Field::Value.into()], - }], - }; - - match client.get(get_request).await { - Ok(response) => { - let message = response.into_inner(); - get_result = message.entries; - let mut errors = Vec::new(); - for error in message.errors { - if let Some(err) = error.error { - errors.push(err.code.to_string()); - errors.push(err.reason.to_string()); - errors.push(err.message.to_string()); - } - } - if !errors.is_empty() { - return Err(ClientError::Function(errors)); - } - } - Err(err) => return Err(ClientError::Status(err)), + match self.get(&path, proto::v1::View::CurrentValue.into(), vec![proto::v1::Field::Value.into(), proto::v1::Field::Metadata.into()]).await{ + Ok(mut entry) => get_result.append(&mut entry), + Err(err) => return Err(err) } } @@ -114,41 +145,14 @@ impl KuksaClient { pub async fn get_target_values( &mut self, - paths: Vec>, + paths: Vec<&str>, ) -> Result, ClientError> { - let mut client = proto::v1::val_client::ValClient::with_interceptor( - self.basic_client.get_channel().await?.clone(), - self.basic_client.get_auth_interceptor(), - ); - let mut get_result = Vec::new(); for path in paths { - let get_request = proto::v1::GetRequest { - entries: vec![proto::v1::EntryRequest { - path: path.as_ref().to_string(), - view: proto::v1::View::TargetValue.into(), - fields: vec![proto::v1::Field::ActuatorTarget.into()], - }], - }; - - match client.get(get_request).await { - Ok(response) => { - let message = response.into_inner(); - get_result = message.entries; - let mut errors = Vec::new(); - for error in message.errors { - if let Some(err) = error.error { - errors.push(err.code.to_string()); - errors.push(err.reason.to_string()); - errors.push(err.message.to_string()); - } - } - if !errors.is_empty() { - return Err(ClientError::Function(errors)); - } - } - Err(err) => return Err(ClientError::Status(err)), + match self.get(path, proto::v1::View::TargetValue.into(), vec![proto::v1::Field::ActuatorTarget.into(), proto::v1::Field::Metadata.into()]).await{ + Ok(mut entry) => get_result.append(&mut entry), + Err(err) => return Err(err) } } @@ -158,112 +162,73 @@ impl KuksaClient { pub async fn set_current_values( &mut self, datapoints: HashMap, - ) -> Result, ClientError> { - let mut client = proto::v1::val_client::ValClient::with_interceptor( - self.basic_client.get_channel().await?.clone(), - self.basic_client.get_auth_interceptor(), - ); - - let mut set_result = Vec::new(); - + ) -> Result<(), ClientError> { for (path, datapoint) in datapoints { - let set_request = proto::v1::SetRequest { - updates: vec![proto::v1::EntryUpdate { - entry: Some(proto::v1::DataEntry { - path: path.clone(), - value: Some(datapoint), - actuator_target: None, - metadata: None, - }), - fields: vec![ - proto::v1::Field::Value.into(), - proto::v1::Field::Path.into(), - ], - }], - }; - match client.set(set_request).await { - Ok(response) => { - set_result.push(response.into_inner()); + match self.set(proto::v1::DataEntry { + path: path.clone(), + value: Some(datapoint), + actuator_target: None, + metadata: None, + }, vec![ + proto::v1::Field::Value.into(), + proto::v1::Field::Path.into(), + ]).await { + Ok(_) => { + continue; } - Err(err) => return Err(ClientError::Status(err)), + Err(err) => return Err(err), } } - Ok(set_result) + Ok(()) } pub async fn set_target_values( &mut self, datapoints: HashMap, - ) -> Result, ClientError> { - let mut client = proto::v1::val_client::ValClient::with_interceptor( - self.basic_client.get_channel().await?.clone(), - self.basic_client.get_auth_interceptor(), - ); - - let mut set_result = Vec::new(); - + ) -> Result<(), ClientError> { for (path, datapoint) in datapoints { - let set_request = proto::v1::SetRequest { - updates: vec![proto::v1::EntryUpdate { - entry: Some(proto::v1::DataEntry { - path: path.clone(), - value: None, - actuator_target: Some(datapoint), - metadata: None, - }), - fields: vec![ - proto::v1::Field::ActuatorTarget.into(), - proto::v1::Field::Path.into(), - ], - }], - }; - match client.set(set_request).await { - Ok(response) => { - set_result.push(response.into_inner()); + match self.set(proto::v1::DataEntry { + path: path.clone(), + value: None, + actuator_target: Some(datapoint), + metadata: None, + }, vec![ + proto::v1::Field::ActuatorTarget.into(), + proto::v1::Field::Path.into(), + ]).await { + Ok(_) => { + continue; } - Err(err) => return Err(ClientError::Status(err)), + Err(err) => return Err(err), } } - Ok(set_result) + Ok(()) } pub async fn set_metadata( &mut self, metadatas: HashMap, - ) -> Result, ClientError> { - let mut client = proto::v1::val_client::ValClient::with_interceptor( - self.basic_client.get_channel().await?.clone(), - self.basic_client.get_auth_interceptor(), - ); - - let mut set_result = Vec::new(); - + ) -> Result<(), ClientError> { for (path, metadata) in metadatas { - let set_request = proto::v1::SetRequest { - updates: vec![proto::v1::EntryUpdate { - entry: Some(proto::v1::DataEntry { - path: path.clone(), - value: None, - actuator_target: None, - metadata: Some(metadata), - }), - fields: vec![ - proto::v1::Field::Metadata.into(), - proto::v1::Field::Path.into(), - ], - }], - }; - match client.set(set_request).await { - Ok(response) => { - set_result.push(response.into_inner()); + match self.set(proto::v1::DataEntry { + path: path.clone(), + value: None, + actuator_target: None, + metadata: Some(metadata), + }, vec![ + proto::v1::Field::Metadata.into(), + proto::v1::Field::Path.into(), + ]).await { + Ok(_) => { + continue; } - Err(err) => return Err(ClientError::Status(err)), + Err(err) => return Err(err), } } - Ok(set_result) + Ok(()) } pub async fn subscribe_current_values( @@ -292,7 +257,7 @@ impl KuksaClient { } } - //masking subscribe curent values with subscribe + //masking subscribe curent values with subscribe due to plugability pub async fn subscribe( &mut self, paths: Vec<&str>, diff --git a/kuksa_databroker/lib/sdv/src/client.rs b/kuksa_databroker/lib/sdv/src/client.rs index 5d57c6a8b..1de74703f 100644 --- a/kuksa_databroker/lib/sdv/src/client.rs +++ b/kuksa_databroker/lib/sdv/src/client.rs @@ -15,14 +15,15 @@ use std::collections::HashMap; use common::{Client, ClientError}; use databroker_proto::sdv::databroker as proto; +use http::Uri; pub struct SDVClient { pub basic_client: Client, } impl SDVClient { - pub fn new(basic_client: Client) -> Self { - SDVClient { basic_client } + pub fn new(uri: Uri) -> Self { + SDVClient { basic_client: Client::new(uri) } } pub async fn get_metadata( From ae766831f929d8b45a5e45171422a609a3b191a4 Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Thu, 2 Nov 2023 13:24:22 +0100 Subject: [PATCH 07/14] Add missing header --- kuksa_databroker/databroker-cli/src/cli.rs | 2 +- kuksa_databroker/databroker-cli/src/help.rs | 13 +++++++++++++ kuksa_databroker/databroker-cli/src/kuksa_cli.rs | 2 +- kuksa_databroker/databroker-cli/src/main.rs | 2 +- kuksa_databroker/databroker-cli/src/sdv_cli.rs | 2 +- 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/kuksa_databroker/databroker-cli/src/cli.rs b/kuksa_databroker/databroker-cli/src/cli.rs index 5ed3116ec..a53ea6c42 100644 --- a/kuksa_databroker/databroker-cli/src/cli.rs +++ b/kuksa_databroker/databroker-cli/src/cli.rs @@ -1,5 +1,5 @@ /******************************************************************************** -* Copyright (c) 2022 Contributors to the Eclipse Foundation +* Copyright (c) 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/kuksa_databroker/databroker-cli/src/help.rs b/kuksa_databroker/databroker-cli/src/help.rs index 4a5503897..3349f1045 100644 --- a/kuksa_databroker/databroker-cli/src/help.rs +++ b/kuksa_databroker/databroker-cli/src/help.rs @@ -1,3 +1,16 @@ +/******************************************************************************** +* Copyright (c) 2023 Contributors to the Eclipse Foundation +* +* See the NOTICE file(s) distributed with this work for additional +* information regarding copyright ownership. +* +* This program and the accompanying materials are made available under the +* terms of the Apache License 2.0 which is available at +* http://www.apache.org/licenses/LICENSE-2.0 +* +* SPDX-License-Identifier: Apache-2.0 +********************************************************************************/ + fn set_connected_prompt(interface: &Arc>, text: str) { let mut _text; _text = text; diff --git a/kuksa_databroker/databroker-cli/src/kuksa_cli.rs b/kuksa_databroker/databroker-cli/src/kuksa_cli.rs index e91cbc809..9191a619a 100644 --- a/kuksa_databroker/databroker-cli/src/kuksa_cli.rs +++ b/kuksa_databroker/databroker-cli/src/kuksa_cli.rs @@ -1,5 +1,5 @@ /******************************************************************************** -* Copyright (c) 2022 Contributors to the Eclipse Foundation +* Copyright (c) 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/kuksa_databroker/databroker-cli/src/main.rs b/kuksa_databroker/databroker-cli/src/main.rs index 32bd73827..2e985de26 100644 --- a/kuksa_databroker/databroker-cli/src/main.rs +++ b/kuksa_databroker/databroker-cli/src/main.rs @@ -1,5 +1,5 @@ /******************************************************************************** -* Copyright (c) 2022 Contributors to the Eclipse Foundation +* Copyright (c) 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. diff --git a/kuksa_databroker/databroker-cli/src/sdv_cli.rs b/kuksa_databroker/databroker-cli/src/sdv_cli.rs index 7d79cc526..7027ac47d 100644 --- a/kuksa_databroker/databroker-cli/src/sdv_cli.rs +++ b/kuksa_databroker/databroker-cli/src/sdv_cli.rs @@ -1,5 +1,5 @@ /******************************************************************************** -* Copyright (c) 2022 Contributors to the Eclipse Foundation +* Copyright (c) 2023 Contributors to the Eclipse Foundation * * See the NOTICE file(s) distributed with this work for additional * information regarding copyright ownership. From e6da2e226b1a138a4957993606a6ff9238042fd1 Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Thu, 2 Nov 2023 13:47:23 +0100 Subject: [PATCH 08/14] Format everything correctly --- kuksa_databroker/databroker-cli/src/cli.rs | 14 +- .../databroker-cli/src/kuksa_cli.rs | 204 ++++++--------- kuksa_databroker/databroker-cli/src/main.rs | 16 +- .../databroker-cli/src/sdv_cli.rs | 241 +++++++----------- kuksa_databroker/lib/common.rs | 7 +- kuksa_databroker/lib/kuksa/src/client.rs | 156 +++++++----- kuksa_databroker/lib/sdv/src/client.rs | 4 +- 7 files changed, 297 insertions(+), 345 deletions(-) diff --git a/kuksa_databroker/databroker-cli/src/cli.rs b/kuksa_databroker/databroker-cli/src/cli.rs index a53ea6c42..593ead460 100644 --- a/kuksa_databroker/databroker-cli/src/cli.rs +++ b/kuksa_databroker/databroker-cli/src/cli.rs @@ -20,7 +20,7 @@ use ansi_term::Color; use clap::{Parser, Subcommand, ValueEnum}; -use linefeed::{DefaultTerminal, Interface, Terminal, Prompter, Function}; +use linefeed::{DefaultTerminal, Function, Interface, Prompter, Terminal}; #[derive(Debug)] pub struct ParseError {} @@ -60,24 +60,24 @@ pub struct Cli { command: Option, } -impl Cli{ - pub fn get_ca_cert(&mut self) -> Option{ +impl Cli { + pub fn get_ca_cert(&mut self) -> Option { return self.ca_cert.clone(); } - pub fn get_token_file(&mut self) -> Option{ + pub fn get_token_file(&mut self) -> Option { return self.token_file.clone(); } - pub fn get_command(&mut self) -> Option{ + pub fn get_command(&mut self) -> Option { return self.command.clone(); } - pub fn get_server(&mut self) -> String{ + pub fn get_server(&mut self) -> String { return self.server.clone(); } - pub fn get_protocol(&mut self) -> CliAPI{ + pub fn get_protocol(&mut self) -> CliAPI { return self.protocol; } } diff --git a/kuksa_databroker/databroker-cli/src/kuksa_cli.rs b/kuksa_databroker/databroker-cli/src/kuksa_cli.rs index 9191a619a..26193c3f1 100644 --- a/kuksa_databroker/databroker-cli/src/kuksa_cli.rs +++ b/kuksa_databroker/databroker-cli/src/kuksa_cli.rs @@ -11,8 +11,6 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ - - use databroker_proto::kuksa::val as proto; use kuksa::*; @@ -20,17 +18,17 @@ use prost_types::Timestamp; use tokio_stream::StreamExt; use std::collections::HashMap; +use std::fmt; use std::sync::Arc; use std::time::{Duration, SystemTime}; -use std::fmt; use ansi_term::Color; +use crate::cli::ParseError; +use crate::cli::{self, Cli}; use linefeed::complete::{Completer, Completion, Suffix}; use linefeed::terminal::Terminal; use linefeed::{Command, Interface, Prompter, ReadResult}; -use crate::cli::{self, Cli}; -use crate::cli::ParseError; const VERSION: &str = "kuksa.val.v1"; const TIMEOUT: Duration = Duration::from_millis(500); @@ -68,7 +66,7 @@ fn print_usage(command: impl AsRef) { } } -pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ +pub async fn kuksa_main(_cli: Cli) -> Result<(), Box> { println!("Using {VERSION}"); let mut subscription_nbr = 1; @@ -213,11 +211,7 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ cli::print_resp_ok(cmd)?; for entry in data_entries { if let Some(val) = entry.value { - println!( - "{}: {}", - entry.path, - DisplayDatapoint(val), - ); + println!("{}: {}", entry.path, DisplayDatapoint(val),); } else { println!("{}: NotAvailable", entry.path); } @@ -265,7 +259,9 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ } } } - Err(err) => cli::print_error(cmd, &format!("Malformed token: {err}"))?, + Err(err) => { + cli::print_error(cmd, &format!("Malformed token: {err}"))? + } } } "token-file" => { @@ -323,8 +319,7 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ continue; } - let datapoint_entries = match client.get_metadata(vec![path]).await - { + let datapoint_entries = match client.get_metadata(vec![path]).await { Ok(data_entries) => Some(data_entries), Err(common::ClientError::Status(status)) => { cli::print_resp_err("metadata", &status)?; @@ -345,18 +340,14 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ if let Some(metadata) = entry.metadata { let data_value = try_into_data_value( value, - proto::v1::DataType::from_i32( - metadata.data_type, - ) - .unwrap(), + proto::v1::DataType::from_i32(metadata.data_type) + .unwrap(), ); if data_value.is_err() { println!( "Could not parse \"{value}\" as {:?}", - proto::v1::DataType::from_i32( - metadata.data_type - ) - .unwrap() + proto::v1::DataType::from_i32(metadata.data_type) + .unwrap() ); continue; } @@ -412,8 +403,7 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ continue; } - let datapoint_entries = match client.get_metadata(vec![path]).await - { + let datapoint_entries = match client.get_metadata(vec![path]).await { Ok(data_entries) => Some(data_entries), Err(common::ClientError::Status(status)) => { cli::print_resp_err("metadata", &status)?; @@ -434,19 +424,15 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ if let Some(metadata) = entry.metadata { let data_value = try_into_data_value( value, - proto::v1::DataType::from_i32( - metadata.data_type, - ) - .unwrap(), + proto::v1::DataType::from_i32(metadata.data_type) + .unwrap(), ); if data_value.is_err() { println!( "Could not parse \"{}\" as {:?}", value, - proto::v1::DataType::from_i32( - metadata.data_type - ) - .unwrap() + proto::v1::DataType::from_i32(metadata.data_type) + .unwrap() ); continue; } @@ -526,8 +512,7 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ .unwrap(); } if let Some(entry) = update.entry { - if let Some(value) = entry.value - { + if let Some(value) = entry.value { writeln!( output, "{}: {}", @@ -578,7 +563,9 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ Err(common::ClientError::Status(status)) => { cli::print_resp_err(cmd, &status)? } - Err(common::ClientError::Connection(msg)) => cli::print_error(cmd, msg)?, + Err(common::ClientError::Connection(msg)) => { + cli::print_error(cmd, msg)? + } Err(common::ClientError::Function(msg)) => { cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))? } @@ -644,7 +631,10 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ cli::print_error("metadata", msg)?; } Err(common::ClientError::Function(msg)) => { - cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + cli::print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )?; } } } @@ -680,12 +670,16 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box>{ println!( "{: Result<(), Box>{ continue; } Err(common::ClientError::Function(msg)) => { - cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + cli::print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )?; } } } @@ -914,7 +911,6 @@ impl Completer for CliCompleter { } } - struct DisplayDataType(Option); struct DisplayEntryType(Option); struct DisplayDatapoint(proto::v1::Datapoint); @@ -943,37 +939,17 @@ impl fmt::Display for DisplayDatapoint { proto::v1::datapoint::Value::Int64(value) => f.pad(&format!("{value}")), proto::v1::datapoint::Value::Uint32(value) => f.pad(&format!("{value}")), proto::v1::datapoint::Value::Uint64(value) => f.pad(&format!("{value}")), - proto::v1::datapoint::Value::Float(value) => { - f.pad(&format!("{value:.2}")) - } + proto::v1::datapoint::Value::Float(value) => f.pad(&format!("{value:.2}")), proto::v1::datapoint::Value::Double(value) => f.pad(&format!("{value}")), - proto::v1::datapoint::Value::String(value) => { - f.pad(&format!("'{value}'")) - } - proto::v1::datapoint::Value::StringArray(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::BoolArray(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::Int32Array(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::Int64Array(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::Uint32Array(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::Uint64Array(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::FloatArray(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::DoubleArray(array) => { - display_array(f, &array.values) - } + proto::v1::datapoint::Value::String(value) => f.pad(&format!("'{value}'")), + proto::v1::datapoint::Value::StringArray(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::BoolArray(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::Int32Array(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::Int64Array(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::Uint32Array(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::Uint64Array(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::FloatArray(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::DoubleArray(array) => display_array(f, &array.values), }, None => f.pad("None"), } @@ -1019,23 +995,18 @@ fn try_into_data_value( } match data_type { - proto::v1::DataType::String => { - Ok(proto::v1::datapoint::Value::String(input.to_owned())) - } - proto::v1::DataType::StringArray => { - match cli::get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::StringArray( - proto::v1::StringArray { values: value }, - )), - Err(err) => Err(err), - } - } + proto::v1::DataType::String => Ok(proto::v1::datapoint::Value::String(input.to_owned())), + proto::v1::DataType::StringArray => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::StringArray( + proto::v1::StringArray { values: value }, + )), + Err(err) => Err(err), + }, proto::v1::DataType::Boolean => match input.parse::() { Ok(value) => Ok(proto::v1::datapoint::Value::Bool(value)), Err(_) => Err(ParseError {}), }, - proto::v1::DataType::BooleanArray => match cli::get_array_from_input(input.to_owned()) - { + proto::v1::DataType::BooleanArray => match cli::get_array_from_input(input.to_owned()) { Ok(value) => Ok(proto::v1::datapoint::Value::BoolArray( proto::v1::BoolArray { values: value }, )), @@ -1095,38 +1066,32 @@ fn try_into_data_value( Ok(value) => Ok(proto::v1::datapoint::Value::Uint32(value as u32)), Err(_) => Err(ParseError {}), }, - proto::v1::DataType::Uint16Array => { - match cli::get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( - proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } + proto::v1::DataType::Uint16Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( + proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, proto::v1::DataType::Uint32 => match input.parse::() { Ok(value) => Ok(proto::v1::datapoint::Value::Uint32(value)), Err(_) => Err(ParseError {}), }, - proto::v1::DataType::Uint32Array => { - match cli::get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( - proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } + proto::v1::DataType::Uint32Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( + proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, proto::v1::DataType::Uint64 => match input.parse::() { Ok(value) => Ok(proto::v1::datapoint::Value::Uint64(value)), Err(_) => Err(ParseError {}), }, - proto::v1::DataType::Uint64Array => { - match cli::get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint64Array( - proto::v1::Uint64Array { values: value }, - )), - Err(err) => Err(err), - } - } + proto::v1::DataType::Uint64Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint64Array( + proto::v1::Uint64Array { values: value }, + )), + Err(err) => Err(err), + }, proto::v1::DataType::Float => match input.parse::() { Ok(value) => Ok(proto::v1::datapoint::Value::Float(value)), Err(_) => Err(ParseError {}), @@ -1141,19 +1106,16 @@ fn try_into_data_value( Ok(value) => Ok(proto::v1::datapoint::Value::Double(value)), Err(_) => Err(ParseError {}), }, - proto::v1::DataType::DoubleArray => { - match cli::get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::DoubleArray( - proto::v1::DoubleArray { values: value }, - )), - Err(err) => Err(err), - } - } + proto::v1::DataType::DoubleArray => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::DoubleArray( + proto::v1::DoubleArray { values: value }, + )), + Err(err) => Err(err), + }, _ => Err(ParseError {}), } } - #[cfg(test)] mod test { @@ -1223,7 +1185,7 @@ mod test { assert!(try_into_data_value("33000", proto::v1::DataType::Int16).is_err()); assert!(try_into_data_value("-33000", proto::v1::DataType::Int16).is_err()); assert!(try_into_data_value("-32000.1", proto::v1::DataType::Int16).is_err()); -} + } #[test] fn test_entry_path_completion() { diff --git a/kuksa_databroker/databroker-cli/src/main.rs b/kuksa_databroker/databroker-cli/src/main.rs index 2e985de26..2ac1a83fd 100644 --- a/kuksa_databroker/databroker-cli/src/main.rs +++ b/kuksa_databroker/databroker-cli/src/main.rs @@ -14,28 +14,26 @@ use clap::Parser; use cli::CliAPI; -mod sdv_cli; -mod kuksa_cli; pub mod cli; +mod kuksa_cli; +mod sdv_cli; #[tokio::main] async fn main() { let mut cli = cli::Cli::parse(); - if cli.get_protocol() == CliAPI::SdvDatabrokerV1{ + if cli.get_protocol() == CliAPI::SdvDatabrokerV1 { let err = sdv_cli::sdv_main(cli.clone()).await; match err { Ok(_) => (), - Err(e) => eprintln!("Error: {}", e) + Err(e) => eprintln!("Error: {}", e), } - } - else if cli.get_protocol() == CliAPI::KuksaValV1{ + } else if cli.get_protocol() == CliAPI::KuksaValV1 { let err = kuksa_cli::kuksa_main(cli.clone()).await; match err { Ok(_) => (), - Err(e) => eprintln!("Error: {}", e) + Err(e) => eprintln!("Error: {}", e), } - } - else{ + } else { println!("Choose one protocol of either kuksa-val-v1 or sdv-databroker-v1") } } diff --git a/kuksa_databroker/databroker-cli/src/sdv_cli.rs b/kuksa_databroker/databroker-cli/src/sdv_cli.rs index 7027ac47d..3acca4133 100644 --- a/kuksa_databroker/databroker-cli/src/sdv_cli.rs +++ b/kuksa_databroker/databroker-cli/src/sdv_cli.rs @@ -11,8 +11,6 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ - - use databroker_proto::sdv::databroker as proto; use sdv::*; @@ -20,17 +18,17 @@ use prost_types::Timestamp; use tokio_stream::StreamExt; use std::collections::HashMap; +use std::fmt; use std::sync::Arc; use std::time::{Duration, SystemTime}; -use std::fmt; use ansi_term::Color; +use crate::cli::ParseError; +use crate::cli::{self, Cli}; use linefeed::complete::{Completer, Completion, Suffix}; use linefeed::terminal::Terminal; use linefeed::{Command, Interface, Prompter, ReadResult}; -use crate::cli::{self, Cli}; -use crate::cli::ParseError; const VERSION: &str = "sdv.databroker.v1"; const TIMEOUT: Duration = Duration::from_millis(500); @@ -68,7 +66,7 @@ fn print_usage(command: impl AsRef) { } } -pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ +pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { let mut _properties = Vec::::new(); println!("Using {VERSION}"); let mut cli = _cli; @@ -256,7 +254,9 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ } } } - Err(err) => cli::print_error(cmd, &format!("Malformed token: {err}"))?, + Err(err) => { + cli::print_error(cmd, &format!("Malformed token: {err}"))? + } } } "token-file" => { @@ -315,7 +315,6 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ continue; } - let datapoint_metadata = { let mut datapoint_metadata = None; for metadata in _properties.iter() { @@ -336,21 +335,17 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ if let Some(metadata) = datapoint_metadata { let data_value = try_into_data_value( value, - proto::v1::DataType::from_i32(metadata.data_type) - .unwrap(), + proto::v1::DataType::from_i32(metadata.data_type).unwrap(), ); if data_value.is_err() { println!( "Could not parse \"{value}\" as {:?}", - proto::v1::DataType::from_i32(metadata.data_type) - .unwrap() + proto::v1::DataType::from_i32(metadata.data_type).unwrap() ); continue; } - if metadata.entry_type - != proto::v1::EntryType::Actuator.into() - { + if metadata.entry_type != proto::v1::EntryType::Actuator.into() { cli::print_error( cmd, format!("{} is not an actuator.", metadata.name), @@ -376,16 +371,13 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ cli::print_resp_ok(cmd)?; } else { for (id, error) in message.errors { - match proto::v1::DatapointError::from_i32( - error, - ) { + match proto::v1::DatapointError::from_i32(error) { Some(error) => { cli::print_resp_ok(cmd)?; println!( "Error setting {}: {}", id, - Color::Red - .paint(format!("{error:?}")), + Color::Red.paint(format!("{error:?}")), ); } None => cli::print_resp_ok_fmt( @@ -403,7 +395,10 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ cli::print_error(cmd, msg)? } Err(common::ClientError::Function(msg)) => { - cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + cli::print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )?; } } } @@ -418,7 +413,6 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ continue; } - let datapoint_metadata = { let mut datapoint_metadata = None; for metadata in _properties.iter() { @@ -439,15 +433,13 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ if let Some(metadata) = datapoint_metadata { let data_value = try_into_data_value( value, - proto::v1::DataType::from_i32(metadata.data_type) - .unwrap(), + proto::v1::DataType::from_i32(metadata.data_type).unwrap(), ); if data_value.is_err() { println!( "Could not parse \"{}\" as {:?}", value, - proto::v1::DataType::from_i32(metadata.data_type) - .unwrap() + proto::v1::DataType::from_i32(metadata.data_type).unwrap() ); continue; } @@ -493,7 +485,10 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ cli::print_error(cmd, msg)? } Err(common::ClientError::Function(msg)) => { - cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + cli::print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )?; } } } @@ -591,7 +586,9 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ Err(common::ClientError::Status(status)) => { cli::print_resp_err(cmd, &status)? } - Err(common::ClientError::Connection(msg)) => cli::print_error(cmd, msg)?, + Err(common::ClientError::Connection(msg)) => { + cli::print_error(cmd, msg)? + } Err(common::ClientError::Function(msg)) => { cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))? } @@ -654,7 +651,10 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ cli::print_error("metadata", msg)?; } Err(common::ClientError::Function(msg)) => { - cli::print_resp_err_fmt(cmd, format_args!("Error {msg:?}"))?; + cli::print_resp_err_fmt( + cmd, + format_args!("Error {msg:?}"), + )?; } } } @@ -665,14 +665,13 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ let paths = args.split_whitespace().collect::>(); - match client.get_metadata(vec![]).await { Ok(mut metadata) => { metadata.sort_by(|a, b| a.name.cmp(&b.name)); _properties = metadata; - interface.set_completer(Arc::new( - CliCompleter::from_metadata(&_properties), - )); + interface.set_completer(Arc::new(CliCompleter::from_metadata( + &_properties, + ))); cli::print_resp_ok(cmd)?; } Err(common::ClientError::Status(status)) => { @@ -726,16 +725,12 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box>{ println!( "{: Result<(), Box>{ } } - struct CliCompleter { paths: PathPart, } @@ -929,18 +923,16 @@ impl Completer for CliCompleter { } } Some("get") | Some("metadata") => self.complete_entry_path(word), - Some("subscribe") => { - match words.next() { - None => Some(vec![Completion::simple("SELECT".to_owned())]), - Some(next) => { - if next == "SELECT" { - self.complete_entry_path(word) - } else { - None - } + Some("subscribe") => match words.next() { + None => Some(vec![Completion::simple("SELECT".to_owned())]), + Some(next) => { + if next == "SELECT" { + self.complete_entry_path(word) + } else { + None } } - } + }, Some("token-file") => { let path_completer = linefeed::complete::PathCompleter; path_completer.complete(word, prompter, start, _end) @@ -975,58 +967,26 @@ impl fmt::Display for DisplayDatapoint { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match &self.0.value { Some(value) => match value { - proto::v1::datapoint::Value::BoolValue(value) => { - f.pad(&format!("{value}")) - } + proto::v1::datapoint::Value::BoolValue(value) => f.pad(&format!("{value}")), proto::v1::datapoint::Value::FailureValue(failure) => f.pad(&format!( "( {:?} )", proto::v1::datapoint::Failure::from_i32(*failure).unwrap() )), - proto::v1::datapoint::Value::Int32Value(value) => { - f.pad(&format!("{value}")) - } - proto::v1::datapoint::Value::Int64Value(value) => { - f.pad(&format!("{value}")) - } - proto::v1::datapoint::Value::Uint32Value(value) => { - f.pad(&format!("{value}")) - } - proto::v1::datapoint::Value::Uint64Value(value) => { - f.pad(&format!("{value}")) - } - proto::v1::datapoint::Value::FloatValue(value) => { - f.pad(&format!("{value:.2}")) - } - proto::v1::datapoint::Value::DoubleValue(value) => { - f.pad(&format!("{value}")) - } - proto::v1::datapoint::Value::StringValue(value) => { - f.pad(&format!("'{value}'")) - } - proto::v1::datapoint::Value::StringArray(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::BoolArray(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::Int32Array(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::Int64Array(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::Uint32Array(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::Uint64Array(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::FloatArray(array) => { - display_array(f, &array.values) - } - proto::v1::datapoint::Value::DoubleArray(array) => { - display_array(f, &array.values) - } + proto::v1::datapoint::Value::Int32Value(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::Int64Value(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::Uint32Value(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::Uint64Value(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::FloatValue(value) => f.pad(&format!("{value:.2}")), + proto::v1::datapoint::Value::DoubleValue(value) => f.pad(&format!("{value}")), + proto::v1::datapoint::Value::StringValue(value) => f.pad(&format!("'{value}'")), + proto::v1::datapoint::Value::StringArray(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::BoolArray(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::Int32Array(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::Int64Array(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::Uint32Array(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::Uint64Array(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::FloatArray(array) => display_array(f, &array.values), + proto::v1::datapoint::Value::DoubleArray(array) => display_array(f, &array.values), }, None => f.pad("None"), } @@ -1087,17 +1047,15 @@ fn try_into_data_value( )); } match data_type { - proto::v1::DataType::String => Ok( - proto::v1::datapoint::Value::StringValue(input.to_owned()), - ), - proto::v1::DataType::StringArray => { - match cli::get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::StringArray( - proto::v1::StringArray { values: value }, - )), - Err(err) => Err(err), - } + proto::v1::DataType::String => { + Ok(proto::v1::datapoint::Value::StringValue(input.to_owned())) } + proto::v1::DataType::StringArray => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::StringArray( + proto::v1::StringArray { values: value }, + )), + Err(err) => Err(err), + }, proto::v1::DataType::Bool => match input.parse::() { Ok(value) => Ok(proto::v1::datapoint::Value::BoolValue(value)), Err(_) => Err(ParseError {}), @@ -1162,38 +1120,32 @@ fn try_into_data_value( Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Value(value as u32)), Err(_) => Err(ParseError {}), }, - proto::v1::DataType::Uint16Array => { - match cli::get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( - proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } + proto::v1::DataType::Uint16Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( + proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, proto::v1::DataType::Uint32 => match input.parse::() { Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Value(value)), Err(_) => Err(ParseError {}), }, - proto::v1::DataType::Uint32Array => { - match cli::get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( - proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } + proto::v1::DataType::Uint32Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint32Array( + proto::v1::Uint32Array { values: value }, + )), + Err(err) => Err(err), + }, proto::v1::DataType::Uint64 => match input.parse::() { Ok(value) => Ok(proto::v1::datapoint::Value::Uint64Value(value)), Err(_) => Err(ParseError {}), }, - proto::v1::DataType::Uint64Array => { - match cli::get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::Uint64Array( - proto::v1::Uint64Array { values: value }, - )), - Err(err) => Err(err), - } - } + proto::v1::DataType::Uint64Array => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::Uint64Array( + proto::v1::Uint64Array { values: value }, + )), + Err(err) => Err(err), + }, proto::v1::DataType::Float => match input.parse::() { Ok(value) => Ok(proto::v1::datapoint::Value::FloatValue(value)), Err(_) => Err(ParseError {}), @@ -1208,14 +1160,12 @@ fn try_into_data_value( Ok(value) => Ok(proto::v1::datapoint::Value::DoubleValue(value)), Err(_) => Err(ParseError {}), }, - proto::v1::DataType::DoubleArray => { - match cli::get_array_from_input(input.to_owned()) { - Ok(value) => Ok(proto::v1::datapoint::Value::DoubleArray( - proto::v1::DoubleArray { values: value }, - )), - Err(err) => Err(err), - } - } + proto::v1::DataType::DoubleArray => match cli::get_array_from_input(input.to_owned()) { + Ok(value) => Ok(proto::v1::datapoint::Value::DoubleArray( + proto::v1::DoubleArray { values: value }, + )), + Err(err) => Err(err), + }, _ => Err(ParseError {}), } } @@ -1329,7 +1279,8 @@ mod test { change_type: proto::v1::ChangeType::OnChange.into(), description: "".into(), }, - ].to_vec(); + ] + .to_vec(); let completer = CliCompleter::from_metadata(&metadata); diff --git a/kuksa_databroker/lib/common.rs b/kuksa_databroker/lib/common.rs index 9cef48df3..ac6a73905 100644 --- a/kuksa_databroker/lib/common.rs +++ b/kuksa_databroker/lib/common.rs @@ -13,10 +13,10 @@ use std::convert::TryFrom; +use databroker_proto::kuksa::val::v1::Error; use http::Uri; use tokio_stream::wrappers::BroadcastStream; use tonic::transport::Channel; -use databroker_proto::kuksa::val::v1::Error; #[derive(Debug)] pub struct Client { @@ -51,7 +51,10 @@ impl std::fmt::Display for ClientError { let formatted_result: String = err .iter() .map(|element| { - format!("code: {}, message: {}, reason: {}", element.code, element.message, element.reason) + format!( + "code: {}, message: {}, reason: {}", + element.code, element.message, element.reason + ) }) .collect::>() .join(", "); // Join the elements with a comma and space diff --git a/kuksa_databroker/lib/kuksa/src/client.rs b/kuksa_databroker/lib/kuksa/src/client.rs index 7d6e2651d..455b46461 100644 --- a/kuksa_databroker/lib/kuksa/src/client.rs +++ b/kuksa_databroker/lib/kuksa/src/client.rs @@ -11,8 +11,8 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -use std::collections::HashMap; use http::Uri; +use std::collections::HashMap; use databroker_proto::kuksa::val::{self as proto, v1::DataEntry}; @@ -25,13 +25,12 @@ pub struct KuksaClient { impl KuksaClient { pub fn new(uri: Uri) -> Self { - KuksaClient { basic_client: Client::new(uri) } + KuksaClient { + basic_client: Client::new(uri), + } } - async fn set(&mut self, - entry: DataEntry, - fields: Vec, - ) -> Result<(), ClientError>{ + async fn set(&mut self, entry: DataEntry, fields: Vec) -> Result<(), ClientError> { let mut client = proto::v1::val_client::ValClient::with_interceptor( self.basic_client.get_channel().await?.clone(), self.basic_client.get_auth_interceptor(), @@ -46,7 +45,7 @@ impl KuksaClient { Ok(response) => { let message = response.into_inner(); let mut errors: Vec = Vec::new(); - if let Some(err) = message.error{ + if let Some(err) = message.error { errors.push(err); } for error in message.errors { @@ -54,29 +53,27 @@ impl KuksaClient { errors.push(err); } } - if errors.is_empty(){ - - return Ok(()) - } - else{ - return Err(ClientError::Function(errors)) + if errors.is_empty() { + return Ok(()); + } else { + return Err(ClientError::Function(errors)); } } Err(err) => return Err(ClientError::Status(err)), } } - async fn get( &mut self, + async fn get( + &mut self, path: &str, view: proto::v1::View, - fields: Vec - )-> Result, ClientError>{ + fields: Vec, + ) -> Result, ClientError> { let mut client = proto::v1::val_client::ValClient::with_interceptor( self.basic_client.get_channel().await?.clone(), self.basic_client.get_auth_interceptor(), ); - let get_request = proto::v1::GetRequest { entries: vec![proto::v1::EntryRequest { path: path.to_string(), @@ -89,7 +86,7 @@ impl KuksaClient { Ok(response) => { let message = response.into_inner(); let mut errors = Vec::new(); - if let Some(err) = message.error{ + if let Some(err) = message.error { errors.push(err); } for error in message.errors { @@ -99,28 +96,29 @@ impl KuksaClient { } if !errors.is_empty() { return Err(ClientError::Function(errors)); - } - else{ + } else { // since there is only one DataEntry in the vector return only the according DataEntry Ok(message.entries.clone()) } } - Err(err) => { - return Err(ClientError::Status(err)) - } + Err(err) => return Err(ClientError::Status(err)), } } - pub async fn get_metadata( - &mut self, - paths: Vec<&str>, - ) -> Result, ClientError> { + pub async fn get_metadata(&mut self, paths: Vec<&str>) -> Result, ClientError> { let mut metadata_result = Vec::new(); for path in paths { - match self.get(path, proto::v1::View::Metadata.into(), vec![proto::v1::Field::Metadata.into()]).await{ + match self + .get( + path, + proto::v1::View::Metadata.into(), + vec![proto::v1::Field::Metadata.into()], + ) + .await + { Ok(mut entry) => metadata_result.append(&mut entry), - Err(err) => return Err(err) + Err(err) => return Err(err), } } @@ -134,9 +132,19 @@ impl KuksaClient { let mut get_result = Vec::new(); for path in paths { - match self.get(&path, proto::v1::View::CurrentValue.into(), vec![proto::v1::Field::Value.into(), proto::v1::Field::Metadata.into()]).await{ + match self + .get( + &path, + proto::v1::View::CurrentValue.into(), + vec![ + proto::v1::Field::Value.into(), + proto::v1::Field::Metadata.into(), + ], + ) + .await + { Ok(mut entry) => get_result.append(&mut entry), - Err(err) => return Err(err) + Err(err) => return Err(err), } } @@ -150,9 +158,19 @@ impl KuksaClient { let mut get_result = Vec::new(); for path in paths { - match self.get(path, proto::v1::View::TargetValue.into(), vec![proto::v1::Field::ActuatorTarget.into(), proto::v1::Field::Metadata.into()]).await{ + match self + .get( + path, + proto::v1::View::TargetValue.into(), + vec![ + proto::v1::Field::ActuatorTarget.into(), + proto::v1::Field::Metadata.into(), + ], + ) + .await + { Ok(mut entry) => get_result.append(&mut entry), - Err(err) => return Err(err) + Err(err) => return Err(err), } } @@ -164,15 +182,21 @@ impl KuksaClient { datapoints: HashMap, ) -> Result<(), ClientError> { for (path, datapoint) in datapoints { - match self.set(proto::v1::DataEntry { - path: path.clone(), - value: Some(datapoint), - actuator_target: None, - metadata: None, - }, vec![ - proto::v1::Field::Value.into(), - proto::v1::Field::Path.into(), - ]).await { + match self + .set( + proto::v1::DataEntry { + path: path.clone(), + value: Some(datapoint), + actuator_target: None, + metadata: None, + }, + vec![ + proto::v1::Field::Value.into(), + proto::v1::Field::Path.into(), + ], + ) + .await + { Ok(_) => { continue; } @@ -188,15 +212,21 @@ impl KuksaClient { datapoints: HashMap, ) -> Result<(), ClientError> { for (path, datapoint) in datapoints { - match self.set(proto::v1::DataEntry { - path: path.clone(), - value: None, - actuator_target: Some(datapoint), - metadata: None, - }, vec![ - proto::v1::Field::ActuatorTarget.into(), - proto::v1::Field::Path.into(), - ]).await { + match self + .set( + proto::v1::DataEntry { + path: path.clone(), + value: None, + actuator_target: Some(datapoint), + metadata: None, + }, + vec![ + proto::v1::Field::ActuatorTarget.into(), + proto::v1::Field::Path.into(), + ], + ) + .await + { Ok(_) => { continue; } @@ -212,15 +242,21 @@ impl KuksaClient { metadatas: HashMap, ) -> Result<(), ClientError> { for (path, metadata) in metadatas { - match self.set(proto::v1::DataEntry { - path: path.clone(), - value: None, - actuator_target: None, - metadata: Some(metadata), - }, vec![ - proto::v1::Field::Metadata.into(), - proto::v1::Field::Path.into(), - ]).await { + match self + .set( + proto::v1::DataEntry { + path: path.clone(), + value: None, + actuator_target: None, + metadata: Some(metadata), + }, + vec![ + proto::v1::Field::Metadata.into(), + proto::v1::Field::Path.into(), + ], + ) + .await + { Ok(_) => { continue; } diff --git a/kuksa_databroker/lib/sdv/src/client.rs b/kuksa_databroker/lib/sdv/src/client.rs index 1de74703f..6093da8c4 100644 --- a/kuksa_databroker/lib/sdv/src/client.rs +++ b/kuksa_databroker/lib/sdv/src/client.rs @@ -23,7 +23,9 @@ pub struct SDVClient { impl SDVClient { pub fn new(uri: Uri) -> Self { - SDVClient { basic_client: Client::new(uri) } + SDVClient { + basic_client: Client::new(uri), + } } pub async fn get_metadata( From e568ebaa484b1b932a68646ed7192a66917037eb Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Fri, 3 Nov 2023 08:23:37 +0100 Subject: [PATCH 09/14] Resolve missed conflicts in CArgo.lock and address clippy findings --- Cargo.lock | 401 +----------------- kuksa_databroker/databroker-cli/src/cli.rs | 10 +- .../databroker-cli/src/sdv_cli.rs | 2 + kuksa_databroker/lib/kuksa/src/client.rs | 24 +- 4 files changed, 28 insertions(+), 409 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 42c2db0fc..e0ef31dc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -19,11 +19,6 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "aho-corasick" -<<<<<<< HEAD -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" -======= version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" @@ -42,7 +37,6 @@ name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "libc", ] @@ -58,15 +52,9 @@ dependencies = [ [[package]] name = "anstream" -<<<<<<< HEAD -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f58811cfac344940f1a400b6e6231ce35171f614f26439e80f8c1465c5cc0c" -======= version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "anstyle", "anstyle-parse", @@ -78,15 +66,9 @@ dependencies = [ [[package]] name = "anstyle" -<<<<<<< HEAD -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b84bf0a05bbb2a83e5eb6fa36bb6e87baa08193c35ff52bbf6b38d8af2890e46" -======= version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a [[package]] name = "anstyle-parse" @@ -108,15 +90,9 @@ dependencies = [ [[package]] name = "anstyle-wincon" -<<<<<<< HEAD -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f54d10c6dfa51283a066ceab3ec1ab78d13fae00aa49243a45e4571fb79dfd" -======= version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "anstyle", "windows-sys 0.48.0", @@ -165,35 +141,11 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ "proc-macro2", "quote", -<<<<<<< HEAD - "syn 2.0.37", -======= "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] name = "async-trait" -<<<<<<< HEAD -version = "0.1.73" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc00ceb34980c03614e35a3a4e218276a0a824e911d07651cd0d858a51e8c0f0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.37", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -======= version = "0.1.74" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9" @@ -201,7 +153,6 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -294,15 +245,9 @@ checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "base64" -<<<<<<< HEAD -version = "0.21.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" -======= version = "0.21.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a [[package]] name = "bitflags" @@ -312,15 +257,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -<<<<<<< HEAD -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" -======= version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a [[package]] name = "blake2b_simd" @@ -344,21 +283,12 @@ dependencies = [ [[package]] name = "bstr" -<<<<<<< HEAD -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" -dependencies = [ - "memchr", - "regex-automata 0.3.8", -======= version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c79ad7fb2dd38f3dabd76b09c6a5a20c038fc0213ef1e9afd30eb777f120f019" dependencies = [ "memchr", "regex-automata 0.4.3", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "serde", ] @@ -426,15 +356,9 @@ dependencies = [ [[package]] name = "clap" -<<<<<<< HEAD -version = "4.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d7b8d5ec32af0fadc644bf1fd509a688c2103b185644bb1e29d164e0703136" -======= version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "clap_builder", "clap_derive", @@ -442,15 +366,9 @@ dependencies = [ [[package]] name = "clap_builder" -<<<<<<< HEAD -version = "4.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5179bb514e4d7c2051749d8fcefa2ed6d06a9f4e6d69faf3805f5d80b8cf8d56" -======= version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "anstream", "anstyle", @@ -461,37 +379,21 @@ dependencies = [ [[package]] name = "clap_derive" -<<<<<<< HEAD -version = "4.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" -======= version = "4.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "heck", "proc-macro2", "quote", -<<<<<<< HEAD - "syn 2.0.37", -======= "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] name = "clap_lex" -<<<<<<< HEAD -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" -======= version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a [[package]] name = "clru" @@ -721,18 +623,12 @@ dependencies = [ [[package]] name = "deranged" -<<<<<<< HEAD -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" -======= version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" dependencies = [ "powerfmt", ] ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a [[package]] name = "derive_more" @@ -818,15 +714,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -<<<<<<< HEAD -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136526188508e25c6fef639d7927dfb3e0e3084488bf202267829cf7fc23dbdd" -======= version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3e13f66a2f95e32a39eaa81f6b95d42878ca0e1db0c7543723dfe12557e860" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "libc", "windows-sys 0.48.0", @@ -841,15 +731,6 @@ dependencies = [ "serde", ] -[[package]] -name = "faster-hex" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a" -dependencies = [ - "serde", -] - [[package]] name = "fastrand" version = "2.0.1" @@ -876,15 +757,9 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" [[package]] name = "flate2" -<<<<<<< HEAD -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" -======= version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "crc32fast", "miniz_oxide", @@ -961,11 +836,7 @@ checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" dependencies = [ "proc-macro2", "quote", -<<<<<<< HEAD - "syn 2.0.37", -======= "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -1169,11 +1040,7 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea7505b97f4d8e7933e29735a568ba2f86d8de466669d9f0e8321384f9972f47" dependencies = [ -<<<<<<< HEAD - "bitflags 2.4.0", -======= "bitflags 2.4.1", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "bstr", "gix-path", "libc", @@ -1251,11 +1118,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3ac79c444193b0660fe0c0925d338bd338bd643e32138784dccfb12c628b892" dependencies = [ -<<<<<<< HEAD - "bitflags 2.4.0", -======= "bitflags 2.4.1", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "bstr", "gix-features", "gix-path", @@ -1263,15 +1126,9 @@ dependencies = [ [[package]] name = "gix-hash" -<<<<<<< HEAD -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ccf425543779cddaa4a7c62aba3fa9d90ea135b160be0a72dd93c063121ad4a" -======= version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1884c7b41ea0875217c1be9ce91322f90bde433e91d374d0e1276073a51ccc60" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "faster-hex", "thiserror", @@ -1294,11 +1151,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e9599fc30b3d6aad231687a403f85dfa36ae37ccf1b68ee1f621ad5b7fc7a0d" dependencies = [ -<<<<<<< HEAD - "bitflags 2.4.0", -======= "bitflags 2.4.1", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "bstr", "btoi", "filetime", @@ -1334,11 +1187,7 @@ checksum = "9d8acb5ee668d55f0f2d19a320a3f9ef67a6999ad483e11135abcc2464ed18b6" dependencies = [ "proc-macro2", "quote", -<<<<<<< HEAD - "syn 2.0.37", -======= "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -1495,11 +1344,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92b9542ac025a8c02ed5d17b3fc031a111a384e859d0be3532ec4d58c40a0f28" dependencies = [ -<<<<<<< HEAD - "bitflags 2.4.0", -======= "bitflags 2.4.1", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "gix-path", "libc", "windows", @@ -1638,18 +1483,6 @@ checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hermit-abi" -<<<<<<< HEAD -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -======= ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" @@ -1720,11 +1553,7 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", -<<<<<<< HEAD - "socket2 0.4.9", -======= "socket2 0.4.10", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "tokio", "tower-service", "tracing", @@ -1805,21 +1634,12 @@ dependencies = [ [[package]] name = "indexmap" -<<<<<<< HEAD -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown 0.14.0", -======= version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8adf3ddd720272c6ea8bf59463c04e0f93d0bbf7c5439b691bca2987e0270897" dependencies = [ "equivalent", "hashbrown 0.14.2", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -1833,8 +1653,6 @@ name = "inventory" version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1be380c410bf0595e94992a648ea89db4dd3f3354ba54af206fd2a68cf5ac8e" -<<<<<<< HEAD -======= [[package]] name = "itertools" @@ -1844,7 +1662,6 @@ checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a [[package]] name = "itertools" @@ -1896,11 +1713,7 @@ version = "9.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "155c4d7e39ad04c172c5e3a99c434ea3b4a7ba7960b38ecd562b270b097cce09" dependencies = [ -<<<<<<< HEAD - "base64 0.21.4", -======= "base64 0.21.5", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "pem", "ring", "serde", @@ -1909,7 +1722,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD name = "kuksa" version = "0.1.0" dependencies = [ @@ -1919,7 +1731,9 @@ dependencies = [ "tokio", "tokio-stream", "tonic", -======= +] + +[[package]] name = "lazy-regex" version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1940,7 +1754,6 @@ dependencies = [ "quote", "regex", "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -1951,15 +1764,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -<<<<<<< HEAD -version = "0.2.148" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" -======= version = "0.2.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a [[package]] name = "linefeed" @@ -1980,15 +1787,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -<<<<<<< HEAD -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a9bad9f94746442c783ca431b22403b519cd7fbeed0533fdd6328b2f2212128" -======= version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a [[package]] name = "lock_api" @@ -2023,15 +1824,9 @@ checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" [[package]] name = "memchr" -<<<<<<< HEAD -version = "2.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" -======= version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a [[package]] name = "memmap2" @@ -2080,11 +1875,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c624fa1b7aab6bd2aff6e9b18565cc0363b6d45cbcd7465c9ed5e3740ebf097" dependencies = [ -<<<<<<< HEAD - "bitflags 2.4.0", -======= "bitflags 2.4.1", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "libc", "nix", "smallstr", @@ -2178,11 +1969,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ -<<<<<<< HEAD - "hermit-abi 0.3.3", -======= "hermit-abi", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "libc", ] @@ -2289,11 +2076,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" dependencies = [ "fixedbitset", -<<<<<<< HEAD - "indexmap 2.0.0", -======= "indexmap 2.0.2", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -2351,11 +2134,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" dependencies = [ "proc-macro2", "quote", -<<<<<<< HEAD - "syn 2.0.37", -======= "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -2394,15 +2173,9 @@ dependencies = [ [[package]] name = "proc-macro2" -<<<<<<< HEAD -version = "1.0.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" -======= version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "unicode-ident", ] @@ -2572,16 +2345,6 @@ dependencies = [ [[package]] name = "regex" -<<<<<<< HEAD -version = "1.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "697061221ea1b4a94a624f67d0ae2bfe4e22b8a17b6a192afb11046542cc8c47" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.3.8", - "regex-syntax 0.7.5", -======= version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" @@ -2590,7 +2353,6 @@ dependencies = [ "memchr", "regex-automata 0.4.3", "regex-syntax 0.8.2", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -2604,15 +2366,6 @@ dependencies = [ [[package]] name = "regex-automata" -<<<<<<< HEAD -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f401f4955220693b56f8ec66ee9c78abffd8d1c4f23dc41a23839eb88f0795" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax 0.7.5", -======= version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" @@ -2620,7 +2373,6 @@ dependencies = [ "aho-corasick", "memchr", "regex-syntax 0.8.2", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -2634,15 +2386,12 @@ name = "regex-syntax" version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" -<<<<<<< HEAD -======= [[package]] name = "regex-syntax" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a [[package]] name = "ring" @@ -2678,19 +2427,11 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -<<<<<<< HEAD -version = "0.38.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747c788e9ce8e92b12cd485c49ddf90723550b654b32508f979b71a7b1ecda4f" -dependencies = [ - "bitflags 2.4.0", -======= version = "0.38.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b426b0506e5d50a7d8dafcf2e81471400deb602392c7dd110815afb4eaf02a3" dependencies = [ "bitflags 2.4.1", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "errno", "libc", "linux-raw-sys", @@ -2699,15 +2440,9 @@ dependencies = [ [[package]] name = "rustls" -<<<<<<< HEAD -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" -======= version = "0.21.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "446e14c5cda4f3f30fe71863c34ec70f5ac79d6087097ad0bb433e1be5edf04c" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "log", "ring", @@ -2721,24 +2456,14 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" dependencies = [ -<<<<<<< HEAD - "base64 0.21.4", -======= "base64 0.21.5", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] name = "rustls-webpki" -<<<<<<< HEAD -version = "0.101.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" -======= version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "ring", "untrusted", @@ -2826,17 +2551,10 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -name = "serde" -version = "1.0.188" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" -======= name = "serde_json" version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "itoa", "ryu", @@ -2844,23 +2562,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD -name = "serde_derive" -version = "1.0.188" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.37", -] - -[[package]] -name = "serde_json" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" -======= name = "serde_path_to_error" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -2875,7 +2576,6 @@ name = "serde_urlencoded" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "form_urlencoded", "itoa", @@ -2999,15 +2699,9 @@ dependencies = [ [[package]] name = "socket2" -<<<<<<< HEAD -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" -======= version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "libc", "windows-sys 0.48.0", @@ -3047,15 +2741,9 @@ dependencies = [ [[package]] name = "syn" -<<<<<<< HEAD -version = "2.0.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7303ef2c05cd654186cb250d29049a24840ca25d2747c25c0381c8d9e2f582e8" -======= version = "2.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "proc-macro2", "quote", @@ -3103,15 +2791,6 @@ dependencies = [ [[package]] name = "tempfile" -<<<<<<< HEAD -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" -dependencies = [ - "cfg-if", - "fastrand", - "redox_syscall 0.3.5", -======= version = "3.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" @@ -3119,7 +2798,6 @@ dependencies = [ "cfg-if", "fastrand", "redox_syscall 0.4.1", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "rustix", "windows-sys 0.48.0", ] @@ -3160,30 +2838,15 @@ dependencies = [ [[package]] name = "thiserror" -<<<<<<< HEAD -version = "1.0.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" -======= version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -<<<<<<< HEAD -version = "1.0.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.37", -======= version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" @@ -3191,7 +2854,6 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -3206,15 +2868,9 @@ dependencies = [ [[package]] name = "time" -<<<<<<< HEAD -version = "0.3.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" -======= version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "deranged", "itoa", @@ -3258,15 +2914,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -<<<<<<< HEAD -version = "1.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" -======= version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "backtrace", "bytes", @@ -3275,11 +2925,7 @@ dependencies = [ "num_cpus", "pin-project-lite", "signal-hook-registry", -<<<<<<< HEAD - "socket2 0.5.4", -======= "socket2 0.5.5", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "tokio-macros", "windows-sys 0.48.0", ] @@ -3302,11 +2948,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", -<<<<<<< HEAD - "syn 2.0.37", -======= "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -3345,15 +2987,9 @@ dependencies = [ [[package]] name = "tokio-util" -<<<<<<< HEAD -version = "0.7.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d" -======= version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "bytes", "futures-core", @@ -3372,11 +3008,7 @@ dependencies = [ "async-stream", "async-trait", "axum", -<<<<<<< HEAD - "base64 0.21.4", -======= "base64 0.21.5", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "bytes", "futures-core", "futures-util", @@ -3463,11 +3095,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", -<<<<<<< HEAD - "syn 2.0.37", -======= "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -3635,6 +3263,12 @@ dependencies = [ "time", ] +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + [[package]] name = "walkdir" version = "2.4.0" @@ -3687,11 +3321,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", -<<<<<<< HEAD - "syn 2.0.37", -======= "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "wasm-bindgen-shared", ] @@ -3713,11 +3343,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", -<<<<<<< HEAD - "syn 2.0.37", -======= "syn 2.0.38", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3778,8 +3404,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" dependencies = [ "windows-targets 0.48.5", -<<<<<<< HEAD -======= ] [[package]] @@ -3789,7 +3413,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" dependencies = [ "windows-targets 0.48.5", ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a ] [[package]] @@ -3926,15 +3549,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "winnow" -<<<<<<< HEAD -version = "0.5.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" -======= version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3b801d0e0a6726477cc207f60162da452f3a95adb368399bef20a946e06f65c" ->>>>>>> cfbca8da84f7cb0b3005503a378a53d2c0a27b9a dependencies = [ "memchr", ] diff --git a/kuksa_databroker/databroker-cli/src/cli.rs b/kuksa_databroker/databroker-cli/src/cli.rs index 593ead460..fc26fc459 100644 --- a/kuksa_databroker/databroker-cli/src/cli.rs +++ b/kuksa_databroker/databroker-cli/src/cli.rs @@ -62,23 +62,23 @@ pub struct Cli { impl Cli { pub fn get_ca_cert(&mut self) -> Option { - return self.ca_cert.clone(); + self.ca_cert.clone() } pub fn get_token_file(&mut self) -> Option { - return self.token_file.clone(); + self.token_file.clone() } pub fn get_command(&mut self) -> Option { - return self.command.clone(); + self.command.clone() } pub fn get_server(&mut self) -> String { - return self.server.clone(); + self.server.clone() } pub fn get_protocol(&mut self) -> CliAPI { - return self.protocol; + self.protocol } } diff --git a/kuksa_databroker/databroker-cli/src/sdv_cli.rs b/kuksa_databroker/databroker-cli/src/sdv_cli.rs index 3acca4133..1c8ec9180 100644 --- a/kuksa_databroker/databroker-cli/src/sdv_cli.rs +++ b/kuksa_databroker/databroker-cli/src/sdv_cli.rs @@ -1046,6 +1046,8 @@ fn try_into_data_value( proto::v1::datapoint::Failure::NotAvailable as i32, )); } + + #[allow(unreachable_patterns)] match data_type { proto::v1::DataType::String => { Ok(proto::v1::datapoint::Value::StringValue(input.to_owned())) diff --git a/kuksa_databroker/lib/kuksa/src/client.rs b/kuksa_databroker/lib/kuksa/src/client.rs index 455b46461..e8c098611 100644 --- a/kuksa_databroker/lib/kuksa/src/client.rs +++ b/kuksa_databroker/lib/kuksa/src/client.rs @@ -30,7 +30,7 @@ impl KuksaClient { } } - async fn set(&mut self, entry: DataEntry, fields: Vec) -> Result<(), ClientError> { + async fn set(&mut self, entry: DataEntry, _fields: Vec) -> Result<(), ClientError> { let mut client = proto::v1::val_client::ValClient::with_interceptor( self.basic_client.get_channel().await?.clone(), self.basic_client.get_auth_interceptor(), @@ -38,7 +38,7 @@ impl KuksaClient { let set_request = proto::v1::SetRequest { updates: vec![proto::v1::EntryUpdate { entry: Some(entry), - fields: fields.into(), + fields: _fields, }], }; match client.set(set_request).await { @@ -54,12 +54,12 @@ impl KuksaClient { } } if errors.is_empty() { - return Ok(()); + Ok(()) } else { - return Err(ClientError::Function(errors)); + Err(ClientError::Function(errors)) } } - Err(err) => return Err(ClientError::Status(err)), + Err(err) => Err(ClientError::Status(err)), } } @@ -67,7 +67,7 @@ impl KuksaClient { &mut self, path: &str, view: proto::v1::View, - fields: Vec, + _fields: Vec, ) -> Result, ClientError> { let mut client = proto::v1::val_client::ValClient::with_interceptor( self.basic_client.get_channel().await?.clone(), @@ -78,7 +78,7 @@ impl KuksaClient { entries: vec![proto::v1::EntryRequest { path: path.to_string(), view: view.into(), - fields: fields, + fields: _fields, }], }; @@ -95,13 +95,13 @@ impl KuksaClient { } } if !errors.is_empty() { - return Err(ClientError::Function(errors)); + Err(ClientError::Function(errors)) } else { // since there is only one DataEntry in the vector return only the according DataEntry Ok(message.entries.clone()) } } - Err(err) => return Err(ClientError::Status(err)), + Err(err) => Err(ClientError::Status(err)), } } @@ -112,7 +112,7 @@ impl KuksaClient { match self .get( path, - proto::v1::View::Metadata.into(), + proto::v1::View::Metadata, vec![proto::v1::Field::Metadata.into()], ) .await @@ -135,7 +135,7 @@ impl KuksaClient { match self .get( &path, - proto::v1::View::CurrentValue.into(), + proto::v1::View::CurrentValue, vec![ proto::v1::Field::Value.into(), proto::v1::Field::Metadata.into(), @@ -161,7 +161,7 @@ impl KuksaClient { match self .get( path, - proto::v1::View::TargetValue.into(), + proto::v1::View::TargetValue, vec![ proto::v1::Field::ActuatorTarget.into(), proto::v1::Field::Metadata.into(), From 9591865bc994260685b629bc6df3f6ad7ae0452e Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Fri, 3 Nov 2023 08:50:24 +0100 Subject: [PATCH 10/14] Fix tests and address findings --- kuksa_databroker/databroker-cli/src/help.rs | 1029 ----------------- .../databroker-cli/src/kuksa_cli.rs | 4 +- kuksa_databroker/lib/kuksa/Cargo.toml | 2 +- kuksa_databroker/lib/sdv/Cargo.toml | 2 +- 4 files changed, 4 insertions(+), 1033 deletions(-) delete mode 100644 kuksa_databroker/databroker-cli/src/help.rs diff --git a/kuksa_databroker/databroker-cli/src/help.rs b/kuksa_databroker/databroker-cli/src/help.rs deleted file mode 100644 index 3349f1045..000000000 --- a/kuksa_databroker/databroker-cli/src/help.rs +++ /dev/null @@ -1,1029 +0,0 @@ -/******************************************************************************** -* Copyright (c) 2023 Contributors to the Eclipse Foundation -* -* See the NOTICE file(s) distributed with this work for additional -* information regarding copyright ownership. -* -* This program and the accompanying materials are made available under the -* terms of the Apache License 2.0 which is available at -* http://www.apache.org/licenses/LICENSE-2.0 -* -* SPDX-License-Identifier: Apache-2.0 -********************************************************************************/ - -fn set_connected_prompt(interface: &Arc>, text: str) { - let mut _text; - _text = text; - let connected_prompt = format!( - "\x01{prefix}\x02{text}\x01{suffix}\x02 > ", - prefix = Color::Green.prefix(), - text = _text, - suffix = Color::Green.suffix() - ); - interface.set_prompt(&connected_prompt).unwrap(); -} - -fn set_disconnected_prompt(interface: &Arc>) { - let disconnected_prompt = format!( - "\x01{prefix}\x02{text}\x01{suffix}\x02 > ", - prefix = Color::Red.prefix(), - text = "not connected", - suffix = Color::Red.suffix() - ); - interface.set_prompt(&disconnected_prompt).unwrap(); -} - -fn to_uri(uri: impl AsRef) -> Result { - let uri = uri - .as_ref() - .parse::() - .map_err(|err| format!("{err}"))?; - let mut parts = uri.into_parts(); - - if parts.scheme.is_none() { - parts.scheme = Some("http".parse().expect("http should be valid scheme")); - } - - match &parts.authority { - Some(_authority) => { - // match (authority.port_u16(), port) { - // (Some(uri_port), Some(port)) => { - // if uri_port != port { - // parts.authority = format!("{}:{}", authority.host(), port) - // .parse::() - // .map_err(|err| format!("{}", err)) - // .ok(); - // } - // } - // (_, _) => {} - // } - } - None => return Err("No server uri specified".to_owned()), - } - parts.path_and_query = Some("".parse().expect("uri path should be empty string")); - tonic::transport::Uri::from_parts(parts).map_err(|err| format!("{err}")) -} - -fn print_logo(version: impl fmt::Display) { - let mut output = io::stderr().lock(); - writeln!(output).unwrap(); - writeln!( - output, - " {}{}", - Color::Fixed(23).paint("⠀⠀⠀⢀⣤⣶⣾⣿"), - Color::White.dimmed().paint("⢸⣿⣿⣷⣶⣤⡀") - ) - .unwrap(); - writeln!( - output, - " {}{}", - Color::Fixed(23).paint("⠀⠀⣴⣿⡿⠋⣿⣿"), - Color::White.dimmed().paint("⠀⠀⠀⠈⠙⢿⣿⣦⠀") - ) - .unwrap(); - writeln!( - output, - " {}{}", - Color::Fixed(23).paint("⠀⣾⣿⠋⠀⠀⣿⣿"), - Color::White.dimmed().paint("⠀⠀⣶⣿⠀⠀⠙⣿⣷ ") - ) - .unwrap(); - writeln!( - output, - " {}{}", - Color::Fixed(23).paint("⣸⣿⠇⠀⠀⠀⣿⣿"), - Color::White - .dimmed() - .paint("⠠⣾⡿⠃⠀⠀⠀⠸⣿⣇⠀⠀⣶⠀⣠⡶⠂⠀⣶⠀⠀⢰⡆⠀⢰⡆⢀⣴⠖⠀⢠⡶⠶⠶⡦⠀⠀⠀⣰⣶⡀") - ) - .unwrap(); - writeln!( - output, - " {}{}", - Color::Fixed(23).paint("⣿⣿⠀⠀⠀⠀⠿⢿⣷⣦⡀"), - Color::White - .dimmed() - .paint("⠀⠀⠀⠀⠀⣿⣿⠀⠀⣿⢾⣏⠀⠀⠀⣿⠀⠀⢸⡇⠀⢸⡷⣿⡁⠀⠀⠘⠷⠶⠶⣦⠀⠀⢠⡟⠘⣷") - ) - .unwrap(); - writeln!( - output, - " {}{}{}{}", - Color::Fixed(23).paint("⢹⣿⡆⠀⠀⠀"), - Color::White.dimmed().paint("⣿⣶"), - Color::Fixed(23).paint("⠈⢻⣿⡆"), - Color::White - .dimmed() - .paint("⠀⠀⠀⢰⣿⡏⠀⠀⠿⠀⠙⠷⠄⠀⠙⠷⠶⠟⠁⠀⠸⠇⠈⠻⠦⠀⠐⠷⠶⠶⠟⠀⠠⠿⠁⠀⠹⠧") - ) - .unwrap(); - writeln!( - output, - " {}{}{}{}", - Color::Fixed(23).paint("⠀⢿⣿⣄⠀⠀"), - Color::White.dimmed().paint("⣿⣿"), - Color::Fixed(23).paint("⠀⠀⠿⣿"), - Color::White.dimmed().paint("⠀⠀⣠⣿⡿"), - ) - .unwrap(); - writeln!( - output, - " {}{} {}", - Color::Fixed(23).paint("⠀⠀⠻⣿⣷⡄"), - Color::White.dimmed().paint("⣿⣿⠀⠀⠀⢀⣠⣾⣿⠟"), - Color::White - .dimmed() - .paint(format!("{:<30}", "databroker-cli")), - ) - .unwrap(); - writeln!( - output, - " {}{} {}", - Color::Fixed(23).paint("⠀⠀⠀⠈⠛⠇"), - Color::White.dimmed().paint("⢿⣿⣿⣿⣿⡿⠿⠛⠁"), - Color::White.dimmed().paint(format!("{version:<30}")), - ) - .unwrap(); - writeln!(output).unwrap(); -} - -#[allow(dead_code)] -fn path_to_regex(path: impl AsRef) -> Result { - let path_as_re = format!( - // Match the whole line (from left '^' to right '$') - "^{}$", - path.as_ref().replace('.', r"\.").replace('*', r"(.*)") - ); - regex::Regex::new(&path_as_re) -} - -fn print_resp_err(operation: impl AsRef, err: &tonic::Status) -> io::Result<()> { - let mut output = io::stderr().lock(); - output.write_fmt(format_args!( - "{} {} {}", - Color::White - .dimmed() - .paint(format!("[{}]", operation.as_ref())), - Color::White - .on(Color::Red) - .paint(format!(" {} ", code_to_text(&err.code()))), - err.message(), - ))?; - output.write_all(b"\n")?; - output.flush() -} - -fn print_resp_err_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { - let mut stderr = io::stderr().lock(); - let mut stdout = io::stdout().lock(); - write_resp_ok(&mut stderr, operation)?; - stdout.write_fmt(fmt)?; - stdout.write_all(b"\n")?; - stdout.flush() -} - -#[allow(dead_code)] -fn print_resp_ok_fmt(operation: impl AsRef, fmt: fmt::Arguments<'_>) -> io::Result<()> { - let mut stderr = io::stderr().lock(); - let mut stdout = io::stdout().lock(); - write_resp_ok(&mut stderr, operation)?; - stdout.write_fmt(fmt)?; - stdout.write_all(b"\n")?; - stdout.flush() -} - -fn print_resp_ok(operation: impl AsRef) -> io::Result<()> { - let mut output = io::stderr().lock(); - write_resp_ok(&mut output, operation)?; - output.write_all(b"\n")?; - output.flush() -} - -fn write_resp_ok(output: &mut impl Write, operation: impl AsRef) -> io::Result<()> { - output.write_fmt(format_args!( - "{} {} ", - Color::White - .dimmed() - .paint(format!("[{}]", operation.as_ref())), - Color::Black.on(Color::Green).paint(" OK "), - )) -} - -fn print_info(info: impl AsRef) -> io::Result<()> { - let mut output = io::stderr().lock(); - output.write_fmt(format_args!( - "{}\n", - Color::White.dimmed().paint(info.as_ref()), - ))?; - output.flush() -} - -fn print_error(operation: impl AsRef, msg: impl AsRef) -> io::Result<()> { - let mut output = io::stderr().lock(); - output.write_fmt(format_args!( - "{} {} {}\n", - Color::White - .dimmed() - .paint(format!("[{}]", operation.as_ref())), - Color::White.on(Color::Red).paint(" Error "), - msg.as_ref(), - ))?; - output.flush() -} - -struct EnterFunction; - -impl Function for EnterFunction { - fn execute(&self, prompter: &mut Prompter, count: i32, _ch: char) -> io::Result<()> { - if prompter - .buffer() - .trim() - // .to_lowercase() - .starts_with("subscribe") - { - if prompter.buffer().ends_with('\n') { - let len = prompter.buffer().len(); - prompter.delete_range(len - 1..len)?; - prompter.accept_input() - } else if count > 0 { - // Start multiline - prompter.insert_str("\n") - } else { - Ok(()) - } - } else { - prompter.accept_input() - } - } -} - -struct DisplayDataType(Option); -struct DisplayEntryType(Option); -// !!! ChangeType currently just exists in old API needs to be removed or added later !!! -struct DisplayChangeType(Option); -struct DisplayDatapoint(root::proto::v1::Datapoint); - -fn display_array(f: &mut fmt::Formatter<'_>, array: &[T]) -> fmt::Result -where - T: fmt::Display, -{ - f.write_str("[")?; - let real_delimiter = ", "; - let mut delimiter = ""; - for value in array { - write!(f, "{delimiter}")?; - delimiter = real_delimiter; - write!(f, "{value}")?; - } - f.write_str("]") -} - -impl fmt::Display for DisplayDatapoint { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - #[cfg(feature = "feature_sdv")] - { - match &self.0.value { - Some(value) => match value { - root::proto::v1::datapoint::Value::BoolValue(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::FailureValue(failure) => f.pad(&format!( - "( {:?} )", - root::proto::v1::datapoint::Failure::from_i32(*failure).unwrap() - )), - root::proto::v1::datapoint::Value::Int32Value(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::Int64Value(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::Uint32Value(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::Uint64Value(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::FloatValue(value) => { - f.pad(&format!("{value:.2}")) - } - root::proto::v1::datapoint::Value::DoubleValue(value) => { - f.pad(&format!("{value}")) - } - root::proto::v1::datapoint::Value::StringValue(value) => { - f.pad(&format!("'{value}'")) - } - root::proto::v1::datapoint::Value::StringArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::BoolArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Int32Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Int64Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Uint32Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Uint64Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::FloatArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::DoubleArray(array) => { - display_array(f, &array.values) - } - }, - None => f.pad("None"), - } - } - #[cfg(feature = "feature_kuksa")] - { - match &self.0.value { - Some(value) => match value { - root::proto::v1::datapoint::Value::Bool(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Int32(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Int64(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Uint32(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Uint64(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::Float(value) => { - f.pad(&format!("{value:.2}")) - } - root::proto::v1::datapoint::Value::Double(value) => f.pad(&format!("{value}")), - root::proto::v1::datapoint::Value::String(value) => { - f.pad(&format!("'{value}'")) - } - root::proto::v1::datapoint::Value::StringArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::BoolArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Int32Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Int64Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Uint32Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::Uint64Array(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::FloatArray(array) => { - display_array(f, &array.values) - } - root::proto::v1::datapoint::Value::DoubleArray(array) => { - display_array(f, &array.values) - } - }, - None => f.pad("None"), - } - } - } -} - -impl From> for DisplayEntryType { - fn from(input: Option) -> Self { - DisplayEntryType(input) - } -} - -impl fmt::Display for DisplayEntryType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - Some(entry_type) => f.pad(&format!("{entry_type:?}")), - None => f.pad("Unknown"), - } - } -} - -impl From> for DisplayDataType { - fn from(input: Option) -> Self { - DisplayDataType(input) - } -} - -impl fmt::Display for DisplayDataType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - Some(data_type) => f.pad(&format!("{data_type:?}")), - None => f.pad("Unknown"), - } - } -} - -impl From> for DisplayChangeType { - fn from(input: Option) -> Self { - DisplayChangeType(input) - } -} -impl fmt::Display for DisplayChangeType { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.0 { - Some(data_type) => f.pad(&format!("{data_type:?}")), - None => f.pad("Unknown"), - } - } -} - -#[derive(Debug)] -struct ParseError {} - -impl std::error::Error for ParseError {} - -impl fmt::Display for ParseError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("parse error") - } -} - -fn get_array_from_input(values: String) -> Result, ParseError> { - let raw_input = values - .strip_prefix('[') - .and_then(|s| s.strip_suffix(']')) - .ok_or(ParseError {})?; - - let pattern = r#"(?:\\.|[^",])*"(?:\\.|[^"])*"|[^",]+"#; - - let regex = regex::Regex::new(pattern).unwrap(); - let inputs = regex.captures_iter(raw_input); - - let mut array: Vec = vec![]; - for part in inputs { - match part[0] - .trim() - .replace('\"', "") - .replace('\\', "\"") - .parse::() - { - Ok(value) => array.push(value), - Err(_) => return Err(ParseError {}), - } - } - Ok(array) -} - -fn try_into_data_value( - input: &str, - data_type: root::proto::v1::DataType, -) -> Result { - if input == "NotAvailable" { - #[cfg(feature = "feature_sdv")] - { - return Ok(root::proto::v1::datapoint::Value::FailureValue( - root::proto::v1::datapoint::Failure::NotAvailable as i32, - )); - } - #[cfg(feature = "feature_kuksa")] - { - return Ok(root::proto::v1::datapoint::Value::String(input.to_string())); - } - } - - #[cfg(feature = "feature_sdv")] - { - match data_type { - root::proto::v1::DataType::String => Ok( - root::proto::v1::datapoint::Value::StringValue(input.to_owned()), - ), - root::proto::v1::DataType::StringArray => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( - root::proto::v1::StringArray { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Bool => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolValue(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::BoolArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolArray( - root::proto::v1::BoolArray { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int8 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value as i32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int8Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int16 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value as i32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int16Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int32 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Value(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int32Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int64 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Value(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int64Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Array( - root::proto::v1::Int64Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Uint8 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value as u32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint8Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Uint16 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value as u32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint16Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Uint32 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Value(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint32Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Uint64 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Value(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint64Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( - root::proto::v1::Uint64Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Float => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatValue(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::FloatArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatArray( - root::proto::v1::FloatArray { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Double => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleValue(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::DoubleArray => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( - root::proto::v1::DoubleArray { values: value }, - )), - Err(err) => Err(err), - } - } - _ => Err(ParseError {}), - } - } - #[cfg(feature = "feature_kuksa")] - { - match data_type { - root::proto::v1::DataType::String => { - Ok(root::proto::v1::datapoint::Value::String(input.to_owned())) - } - root::proto::v1::DataType::StringArray => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::StringArray( - root::proto::v1::StringArray { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Boolean => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Bool(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::BooleanArray => match get_array_from_input(input.to_owned()) - { - Ok(value) => Ok(root::proto::v1::datapoint::Value::BoolArray( - root::proto::v1::BoolArray { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int8 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value as i32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int8Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int16 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value as i32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int16Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int32 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int32Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int32Array( - root::proto::v1::Int32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Int64 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Int64Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Int64Array( - root::proto::v1::Int64Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Uint8 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value as u32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint8Array => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Uint16 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value as u32)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint16Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Uint32 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint32Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint32Array( - root::proto::v1::Uint32Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Uint64 => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::Uint64Array => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Uint64Array( - root::proto::v1::Uint64Array { values: value }, - )), - Err(err) => Err(err), - } - } - root::proto::v1::DataType::Float => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Float(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::FloatArray => match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::FloatArray( - root::proto::v1::FloatArray { values: value }, - )), - Err(err) => Err(err), - }, - root::proto::v1::DataType::Double => match input.parse::() { - Ok(value) => Ok(root::proto::v1::datapoint::Value::Double(value)), - Err(_) => Err(ParseError {}), - }, - root::proto::v1::DataType::DoubleArray => { - match get_array_from_input(input.to_owned()) { - Ok(value) => Ok(root::proto::v1::datapoint::Value::DoubleArray( - root::proto::v1::DoubleArray { values: value }, - )), - Err(err) => Err(err), - } - } - _ => Err(ParseError {}), - } - } -} - -#[cfg(test)] -mod test { - - use super::*; - - #[test] - fn test_parse_values() { - #[cfg(feature = "feature_sdv")] - { - // String - assert!(matches!( - try_into_data_value("test", root::proto::v1::DataType::String), - Ok(root::proto::v1::datapoint::Value::StringValue(value)) if value == "test" - )); - - // StringArray - assert!(matches!( - try_into_data_value("[test, test2, test4]", root::proto::v1::DataType::StringArray), - Ok(root::proto::v1::datapoint::Value::StringArray(value)) if value == root::proto::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} - )); - - // Bool - assert!(matches!( - try_into_data_value("true", root::proto::v1::DataType::Bool), - Ok(root::proto::v1::datapoint::Value::BoolValue(value)) if value - )); - - assert!(matches!( - try_into_data_value("false", root::proto::v1::DataType::Bool), - Ok(root::proto::v1::datapoint::Value::BoolValue(value)) if !value - )); - assert!(try_into_data_value("truefalse", root::proto::v1::DataType::Bool).is_err()); - // BoolArray - assert!(matches!( - try_into_data_value("[true, false, true]", root::proto::v1::DataType::BoolArray), - Ok(root::proto::v1::datapoint::Value::BoolArray(value)) if value == root::proto::v1::BoolArray{values: vec![true, false, true]} - )); - - // Int8 - assert!(matches!( - try_into_data_value("100", root::proto::v1::DataType::Int8), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 100 - )); - assert!(matches!( - try_into_data_value("-100", root::proto::v1::DataType::Int8), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -100 - )); - assert!(try_into_data_value("300", root::proto::v1::DataType::Int8).is_err()); - assert!(try_into_data_value("-300", root::proto::v1::DataType::Int8).is_err()); - assert!(try_into_data_value("-100.1", root::proto::v1::DataType::Int8).is_err()); - - // Int16 - assert!(matches!( - try_into_data_value("100", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 100 - )); - assert!(matches!( - try_into_data_value("-100", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -100 - )); - assert!(matches!( - try_into_data_value("32000", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == 32000 - )); - assert!(matches!( - try_into_data_value("-32000", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32Value(value)) if value == -32000 - )); - assert!(try_into_data_value("33000", root::proto::v1::DataType::Int16).is_err()); - assert!(try_into_data_value("-33000", root::proto::v1::DataType::Int16).is_err()); - assert!(try_into_data_value("-32000.1", root::proto::v1::DataType::Int16).is_err()); - } - #[cfg(feature = "feature_kuksa")] - { - // String - assert!(matches!( - try_into_data_value("test", root::proto::v1::DataType::String), - Ok(root::proto::v1::datapoint::Value::String(value)) if value == "test" - )); - - // StringArray - assert!(matches!( - try_into_data_value("[test, test2, test4]", root::proto::v1::DataType::StringArray), - Ok(root::proto::v1::datapoint::Value::StringArray(value)) if value == root::proto::v1::StringArray{values: vec!["test".to_string(), "test2".to_string(), "test4".to_string()]} - )); - - // Bool - assert!(matches!( - try_into_data_value("true", root::proto::v1::DataType::Boolean), - Ok(root::proto::v1::datapoint::Value::Bool(value)) if value - )); - - assert!(matches!( - try_into_data_value("false", root::proto::v1::DataType::Boolean), - Ok(root::proto::v1::datapoint::Value::Bool(value)) if !value - )); - assert!(try_into_data_value("truefalse", root::proto::v1::DataType::Boolean).is_err()); - // BoolArray - assert!(matches!( - try_into_data_value("[true, false, true]", root::proto::v1::DataType::BooleanArray), - Ok(root::proto::v1::datapoint::Value::BoolArray(value)) if value == root::proto::v1::BoolArray{values: vec![true, false, true]} - )); - - // Int8 - assert!(matches!( - try_into_data_value("100", root::proto::v1::DataType::Int8), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 100 - )); - assert!(matches!( - try_into_data_value("-100", root::proto::v1::DataType::Int8), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -100 - )); - assert!(try_into_data_value("300", root::proto::v1::DataType::Int8).is_err()); - assert!(try_into_data_value("-300", root::proto::v1::DataType::Int8).is_err()); - assert!(try_into_data_value("-100.1", root::proto::v1::DataType::Int8).is_err()); - - // Int16 - assert!(matches!( - try_into_data_value("100", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 100 - )); - assert!(matches!( - try_into_data_value("-100", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -100 - )); - assert!(matches!( - try_into_data_value("32000", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == 32000 - )); - assert!(matches!( - try_into_data_value("-32000", root::proto::v1::DataType::Int16), - Ok(root::proto::v1::datapoint::Value::Int32(value)) if value == -32000 - )); - assert!(try_into_data_value("33000", root::proto::v1::DataType::Int16).is_err()); - assert!(try_into_data_value("-33000", root::proto::v1::DataType::Int16).is_err()); - assert!(try_into_data_value("-32000.1", root::proto::v1::DataType::Int16).is_err()); - } - } - - #[test] - fn test_entry_path_completion() { - #[allow(unused_mut, unused_assignments)] - let mut metadata = Vec::new(); - #[cfg(feature = "feature_sdv")] - { - metadata = [ - root::proto::v1::Metadata { - id: 1, - name: "Vehicle.Test.Test1".into(), - data_type: root::proto::v1::DataType::Bool.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - change_type: root::proto::v1::ChangeType::OnChange.into(), - description: "".into(), - }, - root::proto::v1::Metadata { - id: 2, - name: "Vehicle.AnotherTest.AnotherTest1".into(), - data_type: root::proto::v1::DataType::Bool.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - change_type: root::proto::v1::ChangeType::OnChange.into(), - description: "".into(), - }, - root::proto::v1::Metadata { - id: 3, - name: "Vehicle.AnotherTest.AnotherTest2".into(), - data_type: root::proto::v1::DataType::Bool.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - change_type: root::proto::v1::ChangeType::OnChange.into(), - description: "".into(), - }, - ] - .to_vec(); - } - - #[cfg(feature = "feature_kuksa")] - { - metadata.push(root::proto::v1::DataEntry { - path: "Vehicle.Test.Test1".into(), - value: None, - actuator_target: None, - metadata: Some(root::proto::v1::Metadata { - data_type: root::proto::v1::DataType::Boolean.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - comment: None, - deprecation: None, - unit: None, - value_restriction: None, - entry_specific: None, - description: Some("".to_string()), - }), - }); - metadata.push(root::proto::v1::DataEntry { - path: "Vehicle.Test.AnotherTest1".into(), - value: None, - actuator_target: None, - metadata: Some(root::proto::v1::Metadata { - data_type: root::proto::v1::DataType::Boolean.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - comment: None, - deprecation: None, - unit: None, - value_restriction: None, - entry_specific: None, - description: Some("".to_string()), - }), - }); - metadata.push(root::proto::v1::DataEntry { - path: "Vehicle.Test.AnotherTest2".into(), - value: None, - actuator_target: None, - metadata: Some(root::proto::v1::Metadata { - data_type: root::proto::v1::DataType::Boolean.into(), - entry_type: root::proto::v1::EntryType::Sensor.into(), - comment: None, - deprecation: None, - unit: None, - value_restriction: None, - entry_specific: None, - description: Some("".to_string()), - }), - }); - } - - let completer = CliCompleter::from_metadata(&metadata); - - assert_eq!(completer.paths.children.len(), 1); - assert_eq!(completer.paths.children["vehicle"].children.len(), 2); - - match completer.complete_entry_path("") { - Some(completions) => { - assert_eq!(completions.len(), 1); - assert_eq!(completions[0].display(), "Vehicle."); - } - None => panic!("expected completions, got None"), - } - - match completer.complete_entry_path("v") { - Some(completions) => { - assert_eq!(completions.len(), 1); - assert_eq!(completions[0].display(), "Vehicle."); - } - None => panic!("expected completions, got None"), - } - - match completer.complete_entry_path("vehicle.") { - Some(completions) => { - assert_eq!(completions.len(), 2); - assert_eq!(completions[0].display(), "AnotherTest."); - assert_eq!(completions[1].display(), "Test."); - } - None => panic!("expected completions, got None"), - } - - match completer.complete_entry_path("vehicle") { - Some(completions) => { - assert_eq!(completions.len(), 2); - assert_eq!(completions[0].display(), "AnotherTest."); - assert_eq!(completions[1].display(), "Test."); - } - None => panic!("expected completions, got None"), - } - } - - #[test] - fn test_alignment() { - let max = 7; - assert_eq!("hej 1 4", format!("{: Date: Fri, 3 Nov 2023 09:06:57 +0100 Subject: [PATCH 11/14] Minor fix --- kuksa_databroker/README.md | 2 +- .../databroker-cli/src/kuksa_cli.rs | 33 ++++++++++--------- 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/kuksa_databroker/README.md b/kuksa_databroker/README.md index 801a52ad2..08d12c726 100644 --- a/kuksa_databroker/README.md +++ b/kuksa_databroker/README.md @@ -118,7 +118,7 @@ Currently, to run databroker-cli (see below), you do need to change the port it ## Test the Databroker using CLI -The databroker supports both of ```sdv.databroker.v1``` and ```kuksa.val.v1``` as an API. Per default the databroker-cli uses the ```sdv.databroker.v1``` interface. To change it use ```--protocol``` option when starting. Chosse eihter one of ```kuksa-val-v1``` and ```sdv-databroker-v1```. +The databroker supports both of ```sdv.databroker.v1``` and ```kuksa.val.v1``` as an API. Per default the databroker-cli uses the ```sdv.databroker.v1``` interface. To change it use ```--protocol``` option when starting. Chosse either one of ```kuksa-val-v1``` and ```sdv-databroker-v1```. Run the cli with: diff --git a/kuksa_databroker/databroker-cli/src/kuksa_cli.rs b/kuksa_databroker/databroker-cli/src/kuksa_cli.rs index 478cd0c6e..ff5507852 100644 --- a/kuksa_databroker/databroker-cli/src/kuksa_cli.rs +++ b/kuksa_databroker/databroker-cli/src/kuksa_cli.rs @@ -1190,22 +1190,23 @@ mod test { #[test] fn test_entry_path_completion() { #[allow(unused_mut, unused_assignments)] - let mut metadata = Vec::new(); - metadata.push(proto::v1::DataEntry { - path: "Vehicle.Test.Test1".into(), - value: None, - actuator_target: None, - metadata: Some(proto::v1::Metadata { - data_type: proto::v1::DataType::Boolean.into(), - entry_type: proto::v1::EntryType::Sensor.into(), - comment: None, - deprecation: None, - unit: None, - value_restriction: None, - entry_specific: None, - description: Some("".to_string()), - }), - }); + let mut metadata = vec![ + proto::v1::DataEntry { + path: "Vehicle.Test.Test1".into(), + value: None, + actuator_target: None, + metadata: Some(proto::v1::Metadata { + data_type: proto::v1::DataType::Boolean.into(), + entry_type: proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + }), + } + ]; metadata.push(proto::v1::DataEntry { path: "Vehicle.AnotherTest.AnotherTest1".into(), value: None, From ac04b0eff5e51f610e7a7cca26a94f8237cf07a3 Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Fri, 3 Nov 2023 09:15:56 +0100 Subject: [PATCH 12/14] Formatting --- .../databroker-cli/src/kuksa_cli.rs | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/kuksa_databroker/databroker-cli/src/kuksa_cli.rs b/kuksa_databroker/databroker-cli/src/kuksa_cli.rs index ff5507852..0c84bffa0 100644 --- a/kuksa_databroker/databroker-cli/src/kuksa_cli.rs +++ b/kuksa_databroker/databroker-cli/src/kuksa_cli.rs @@ -1190,23 +1190,21 @@ mod test { #[test] fn test_entry_path_completion() { #[allow(unused_mut, unused_assignments)] - let mut metadata = vec![ - proto::v1::DataEntry { - path: "Vehicle.Test.Test1".into(), - value: None, - actuator_target: None, - metadata: Some(proto::v1::Metadata { - data_type: proto::v1::DataType::Boolean.into(), - entry_type: proto::v1::EntryType::Sensor.into(), - comment: None, - deprecation: None, - unit: None, - value_restriction: None, - entry_specific: None, - description: Some("".to_string()), - }), - } - ]; + let mut metadata = vec![proto::v1::DataEntry { + path: "Vehicle.Test.Test1".into(), + value: None, + actuator_target: None, + metadata: Some(proto::v1::Metadata { + data_type: proto::v1::DataType::Boolean.into(), + entry_type: proto::v1::EntryType::Sensor.into(), + comment: None, + deprecation: None, + unit: None, + value_restriction: None, + entry_specific: None, + description: Some("".to_string()), + }), + }]; metadata.push(proto::v1::DataEntry { path: "Vehicle.AnotherTest.AnotherTest1".into(), value: None, From 09d8359a8118b0bd2a5d970cc1863d64eb186b9d Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Fri, 3 Nov 2023 09:51:09 +0100 Subject: [PATCH 13/14] Another minor fix --- kuksa_databroker/databroker-cli/src/kuksa_cli.rs | 2 +- kuksa_databroker/databroker-cli/src/sdv_cli.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kuksa_databroker/databroker-cli/src/kuksa_cli.rs b/kuksa_databroker/databroker-cli/src/kuksa_cli.rs index 0c84bffa0..e5f2cdfa3 100644 --- a/kuksa_databroker/databroker-cli/src/kuksa_cli.rs +++ b/kuksa_databroker/databroker-cli/src/kuksa_cli.rs @@ -353,7 +353,7 @@ pub async fn kuksa_main(_cli: Cli) -> Result<(), Box> { } if metadata.entry_type - != proto::v1::EntryType::Actuator.into() + != proto::v1::EntryType::Actuator as i32 { cli::print_error( cmd, diff --git a/kuksa_databroker/databroker-cli/src/sdv_cli.rs b/kuksa_databroker/databroker-cli/src/sdv_cli.rs index 1c8ec9180..c2a57064d 100644 --- a/kuksa_databroker/databroker-cli/src/sdv_cli.rs +++ b/kuksa_databroker/databroker-cli/src/sdv_cli.rs @@ -345,7 +345,7 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { continue; } - if metadata.entry_type != proto::v1::EntryType::Actuator.into() { + if metadata.entry_type != proto::v1::EntryType::Actuator as i32 { cli::print_error( cmd, format!("{} is not an actuator.", metadata.name), From 67010abeb1cc896fb9b0d4244e89861812810812 Mon Sep 17 00:00:00 2001 From: lukasmittag Date: Wed, 15 Nov 2023 14:34:21 +0100 Subject: [PATCH 14/14] Fix metadata properties in sdv cli --- .../databroker-cli/src/sdv_cli.rs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/kuksa_databroker/databroker-cli/src/sdv_cli.rs b/kuksa_databroker/databroker-cli/src/sdv_cli.rs index c2a57064d..ba2f84d92 100644 --- a/kuksa_databroker/databroker-cli/src/sdv_cli.rs +++ b/kuksa_databroker/databroker-cli/src/sdv_cli.rs @@ -67,7 +67,7 @@ fn print_usage(command: impl AsRef) { } pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { - let mut _properties = Vec::::new(); + let mut properties = Vec::::new(); println!("Using {VERSION}"); let mut cli = _cli; @@ -160,6 +160,7 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { Ok(metadata) => { interface .set_completer(Arc::new(CliCompleter::from_metadata(&metadata))); + properties = metadata; } Err(common::ClientError::Status(status)) => { cli::print_resp_err("metadata", &status)?; @@ -238,7 +239,7 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { interface.set_completer(Arc::new( CliCompleter::from_metadata(&metadata), )); - _properties = metadata; + properties = metadata; } Err(common::ClientError::Status(status)) => { cli::print_resp_err("metadata", &status)?; @@ -277,7 +278,7 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { interface.set_completer(Arc::new( CliCompleter::from_metadata(&metadata), )); - _properties = metadata; + properties = metadata; } Err(common::ClientError::Status(status)) => { cli::print_resp_err("metadata", &status)?; @@ -317,7 +318,7 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { let datapoint_metadata = { let mut datapoint_metadata = None; - for metadata in _properties.iter() { + for metadata in properties.iter() { if metadata.name == path { datapoint_metadata = Some(metadata) } @@ -415,7 +416,7 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { let datapoint_metadata = { let mut datapoint_metadata = None; - for metadata in _properties.iter() { + for metadata in properties.iter() { if metadata.name == path { datapoint_metadata = Some(metadata) } @@ -642,7 +643,7 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { interface.set_completer(Arc::new( CliCompleter::from_metadata(&metadata), )); - _properties = metadata; + properties = metadata; } Err(common::ClientError::Status(status)) => { cli::print_resp_err("metadata", &status)?; @@ -668,9 +669,9 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { match client.get_metadata(vec![]).await { Ok(mut metadata) => { metadata.sort_by(|a, b| a.name.cmp(&b.name)); - _properties = metadata; + properties = metadata; interface.set_completer(Arc::new(CliCompleter::from_metadata( - &_properties, + &properties, ))); cli::print_resp_ok(cmd)?; } @@ -690,12 +691,12 @@ pub async fn sdv_main(_cli: Cli) -> Result<(), Box> { let mut filtered_metadata = Vec::new(); if paths.is_empty() { cli::print_info("If you want to list metadata of signals, use `metadata PATTERN`")?; - // filtered_metadata.extend(&_properties); + // filtered_metadata.extend(&properties); } else { for path in &paths { let path_re = path_to_regex(path); let filtered = - _properties.iter().filter(|item| match &path_re { + properties.iter().filter(|item| match &path_re { Ok(re) => re.is_match(&item.name), Err(err) => { cli::print_info(format!("Invalid path: {err}"))