diff --git a/Cargo.lock b/Cargo.lock index 063d428..8203337 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,30 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.15" @@ -53,7 +77,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -63,9 +87,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "async-trait" version = "0.1.80" @@ -98,6 +134,12 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "bitflags" version = "1.3.2" @@ -110,6 +152,12 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +[[package]] +name = "bumpalo" +version = "3.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" + [[package]] name = "bytes" version = "1.6.0" @@ -128,6 +176,20 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.4", +] + [[package]] name = "clap" version = "4.5.20" @@ -190,6 +252,16 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "ctor" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "deranged" version = "0.3.11" @@ -200,6 +272,35 @@ dependencies = [ "serde", ] +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "env_filter" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2c92ceda6ceec50f43169f9ee8424fe2db276791afde7b2cd8bc084cb376ab" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13fa619b91fb2381732789fc5de83b45675e882f66623b7d8cb4f643017018d" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -213,7 +314,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -332,9 +433,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.3.3" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "http" @@ -370,6 +471,12 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + [[package]] name = "hyper" version = "0.14.28" @@ -407,6 +514,29 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "indexmap" version = "2.2.6" @@ -429,15 +559,30 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +[[package]] +name = "js-sys" +version = "0.3.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "kordophone" version = "0.1.0" dependencies = [ "async-trait", + "base64", + "chrono", + "ctor", + "env_logger", "hyper", "hyper-tls", + "log", "serde", "serde_json", + "serde_plain", "time", "tokio", "uuid", @@ -447,8 +592,13 @@ dependencies = [ name = "kpcli" version = "0.1.0" dependencies = [ + "anyhow", "clap", + "dotenv", "kordophone", + "log", + "pretty", + "tokio", ] [[package]] @@ -481,9 +631,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] name = "memchr" @@ -502,13 +652,14 @@ dependencies = [ [[package]] name = "mio" -version = "0.8.11" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" dependencies = [ + "hermit-abi", "libc", "wasi", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] @@ -536,13 +687,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] -name = "num_cpus" -version = "1.16.0" +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "hermit-abi", - "libc", + "autocfg", ] [[package]] @@ -657,6 +807,18 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "pretty" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55c4d17d994b637e2f4daf6e5dc5d660d209d5642377d675d7a1c3ab69fa579" +dependencies = [ + "arrayvec", + "termcolor", + "typed-arena", + "unicode-width", +] + [[package]] name = "proc-macro2" version = "1.0.81" @@ -714,6 +876,35 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -730,7 +921,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -745,7 +936,7 @@ version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -808,6 +999,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_plain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" +dependencies = [ + "serde", +] + [[package]] name = "signal-hook-registry" version = "1.4.1" @@ -839,7 +1039,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -868,7 +1068,16 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys 0.52.0", + "windows-sys", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", ] [[package]] @@ -903,28 +1112,27 @@ dependencies = [ [[package]] name = "tokio" -version = "1.37.0" +version = "1.41.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "22cfb5bee7a6a52939ca9224d6ac897bb669134078daa8735560897f69de4d33" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", @@ -986,12 +1194,24 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "typed-arena" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" + [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1042,12 +1262,76 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "windows-sys" -version = "0.48.0" +name = "wasm-bindgen" +version = "0.2.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" dependencies = [ - "windows-targets 0.48.5", + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.4", ] [[package]] diff --git a/kordophone/Cargo.toml b/kordophone/Cargo.toml index cc21210..68fbf2b 100644 --- a/kordophone/Cargo.toml +++ b/kordophone/Cargo.toml @@ -10,10 +10,10 @@ async-trait = "0.1.80" base64 = "0.22.1" chrono = "0.4.38" ctor = "0.2.8" +env_logger = "0.11.5" hyper = { version = "0.14", features = ["full"] } hyper-tls = "0.5.0" log = { version = "0.4.21", features = [] } -pretty_env_logger = "0.5.0" serde = { version = "1.0.152", features = ["derive"] } serde_json = "1.0.91" serde_plain = "1.0.2" diff --git a/kordophone/src/api/http_client.rs b/kordophone/src/api/http_client.rs index 06df77f..eb1a5bc 100644 --- a/kordophone/src/api/http_client.rs +++ b/kordophone/src/api/http_client.rs @@ -1,7 +1,7 @@ extern crate hyper; extern crate serde; -use std::{path::PathBuf, str}; +use std::{ffi::OsString, path::PathBuf, str}; use log::{error}; use hyper::{Body, Client, Method, Request, Uri}; @@ -34,6 +34,21 @@ pub enum Error { DecodeError, } +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + Error::HTTPError(ref err) => Some(err), + _ => None, + } + } +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + impl From for Error { fn from(err: hyper::Error) -> Error { Error::HTTPError(err) @@ -75,12 +90,12 @@ impl APIInterface for HTTPAPIClient { type Error = Error; async fn get_version(&mut self) -> Result { - let version: String = self.request("/version", Method::GET).await?; + let version: String = self.request("version", Method::GET).await?; Ok(version) } async fn get_conversations(&mut self) -> Result, Self::Error> { - let conversations: Vec = self.request("/conversations", Method::GET).await?; + let conversations: Vec = self.request("conversations", Method::GET).await?; Ok(conversations) } @@ -91,7 +106,7 @@ impl APIInterface for HTTPAPIClient { } let body = || -> Body { serde_json::to_string(&credentials).unwrap().into() }; - let token: AuthResponse = self.request_with_body_retry("/authenticate", Method::POST, body, false).await?; + let token: AuthResponse = self.request_with_body_retry("authenticate", Method::POST, body, false).await?; let token = JwtToken::new(&token.jwt).map_err(|_| Error::DecodeError)?; self.auth_token = Some(token.clone()); Ok(token) @@ -140,6 +155,8 @@ impl HTTPAPIClient { use hyper::StatusCode; let uri = self.uri_for_endpoint(endpoint); + log::debug!("Requesting {:?} {:?}", method, uri); + let build_request = move |auth: &Option| { let body = body_fn(); Request::builder() @@ -152,6 +169,9 @@ impl HTTPAPIClient { let request = build_request(&self.auth_token); let mut response = self.client.request(request).await?; + + log::debug!("-> Response: {:}", response.status()); + match response.status() { StatusCode::OK => { /* cool */ }, @@ -194,13 +214,6 @@ impl HTTPAPIClient { mod test { use super::*; - use ctor::ctor; - - #[ctor] - fn init() { - pretty_env_logger::init(); - log::set_max_level(log::LevelFilter::Trace); - } fn local_mock_client() -> HTTPAPIClient { let base_url = "http://localhost:5738".parse().unwrap(); diff --git a/kordophone/src/lib.rs b/kordophone/src/lib.rs index c0ba5bc..bb69a38 100644 --- a/kordophone/src/lib.rs +++ b/kordophone/src/lib.rs @@ -1,7 +1,21 @@ -mod api; +pub mod api; pub mod model; pub use self::api::APIInterface; +use ctor::ctor; #[cfg(test)] pub mod tests; + +extern crate env_logger; + +fn initialize_logging() { + env_logger::Builder::from_default_env() + .format_timestamp_secs() + .init(); +} + +#[ctor] +fn init() { + initialize_logging(); +} diff --git a/kpcli/.gitignore b/kpcli/.gitignore new file mode 100644 index 0000000..fb016c7 --- /dev/null +++ b/kpcli/.gitignore @@ -0,0 +1,4 @@ +.env +.env.* + + diff --git a/kpcli/Cargo.toml b/kpcli/Cargo.toml index 010939f..20f4d4d 100644 --- a/kpcli/Cargo.toml +++ b/kpcli/Cargo.toml @@ -6,5 +6,10 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +anyhow = "1.0.93" clap = { version = "4.5.20", features = ["derive"] } +dotenv = "0.15.0" kordophone = { path = "../kordophone" } +log = "0.4.22" +pretty = { version = "0.12.3", features = ["termcolor"] } +tokio = "1.41.1" diff --git a/kpcli/src/client_cli.rs b/kpcli/src/client_cli.rs new file mode 100644 index 0000000..33a56ac --- /dev/null +++ b/kpcli/src/client_cli.rs @@ -0,0 +1,48 @@ +use kordophone::APIInterface; +use kordophone::api::http_client::HTTPAPIClient; +use kordophone::api::http_client::Credentials; + +use dotenv; +use crate::printers::ConversationPrinter; + +pub struct ClientCli { + api: HTTPAPIClient, +} + +impl ClientCli { + pub fn new() -> Self { + dotenv::dotenv().ok(); + + // read from env + let base_url = std::env::var("KORDOPHONE_API_URL") + .expect("KORDOPHONE_API_URL must be set"); + + let credentials = Credentials { + username: std::env::var("KORDOPHONE_USERNAME") + .expect("KORDOPHONE_USERNAME must be set"), + + password: std::env::var("KORDOPHONE_PASSWORD") + .expect("KORDOPHONE_PASSWORD must be set"), + }; + + let api = HTTPAPIClient::new(base_url.parse().unwrap(), credentials.into()); + Self { api: api } + } + + pub async fn print_version(&mut self) -> Result<(), Box> { + let version = self.api.get_version().await?; + println!("Version: {}", version); + Ok(()) + } + + pub async fn print_conversations(&mut self) -> Result<(), Box> { + let conversations = self.api.get_conversations().await?; + for conversation in conversations { + println!("{}", ConversationPrinter::new(&conversation)); + } + + Ok(()) + } +} + + diff --git a/kpcli/src/main.rs b/kpcli/src/main.rs index 0eea7ab..c1ff3bb 100644 --- a/kpcli/src/main.rs +++ b/kpcli/src/main.rs @@ -1,9 +1,12 @@ -use clap::{Parser, Subcommand}; -use kordophone::APIInterface; +mod client_cli; +mod printers; +use clap::{Parser, Subcommand}; +use client_cli::ClientCli; + +/// A command line interface for the Kordophone library and daemon #[derive(Parser)] #[command(name = "kpcli")] -#[command(about = "CLI tool for the Kordophone daemon")] struct Cli { #[command(subcommand)] command: Commands, @@ -20,24 +23,28 @@ enum Commands { #[derive(Subcommand)] enum ClientCommands { - ListConversations, + /// Prints all known conversations on the server. + Conversations, + + /// Prints the server Kordophone version. Version, } -fn main() { - let cli = Cli::parse(); - - match cli.command { +async fn run_command(command: Commands) -> Result<(), Box> { + let mut client = ClientCli::new(); + match command { Commands::Client { command } => match command { - ClientCommands::ListConversations => { - println!("Listing conversations..."); - // TODO: Implement conversation listing - }, - - ClientCommands::Version => { - println!("Getting version..."); - // TODO: Implement version getting - }, + ClientCommands::Version => client.print_version().await, + ClientCommands::Conversations => client.print_conversations().await, }, } } + +#[tokio::main] +async fn main() { + let cli = Cli::parse(); + + run_command(cli.command).await + .map_err(|e| log::error!("Error: {}", e)) + .err(); +} diff --git a/kpcli/src/printers.rs b/kpcli/src/printers.rs new file mode 100644 index 0000000..2ed1bb0 --- /dev/null +++ b/kpcli/src/printers.rs @@ -0,0 +1,58 @@ +use std::fmt::Display; + +use pretty::RcDoc; +use kordophone::model::Conversation; + +pub struct ConversationPrinter<'a> { + doc: RcDoc<'a, Conversation> +} + +impl<'a> ConversationPrinter<'a> { + pub fn new(conversation: &'a Conversation) -> Self { + let preview = conversation.last_message_preview + .as_deref() + .unwrap_or("") + .replace('\n', " "); + + let doc = RcDoc::text(format!("")) + .append(RcDoc::line()) + .append("Date: ") + .append(conversation.date.to_string()) + .append(RcDoc::line()) + .append("Participants: ") + .append("[") + .append(RcDoc::line() + .append( + conversation.participant_display_names + .iter() + .map(|name| + RcDoc::text(name) + .append(",") + .append(RcDoc::line()) + ) + .fold(RcDoc::nil(), |acc, x| acc.append(x)) + ) + .nest(4) + ) + .append("]") + .append(RcDoc::line()) + .append("Last Message Preview: ") + .append(preview) + .nest(4) + ) + .append(RcDoc::line()) + .append(">"); + + ConversationPrinter { doc } + } +} + +impl<'a> Display for ConversationPrinter<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.doc.render_fmt(180, f) + } +} \ No newline at end of file