From 8881d0fa598c8f819719ffb32272c4eade3acddb Mon Sep 17 00:00:00 2001 From: gak Date: Tue, 29 Aug 2023 13:11:28 +1000 Subject: [PATCH] feat: cli only prints json, cli-pretty-json feature --- Cargo.toml | 6 ++++-- src/cli.rs | 16 ++++++++++++++++ src/cli/energy.rs | 4 ++-- src/cli/powerwall.rs | 9 +++++---- src/cli/vehicle.rs | 21 +++++++++------------ src/lib.rs | 44 +++++++++++++++++++++++++++++++++++++------- src/main.rs | 7 ++++--- 7 files changed, 77 insertions(+), 30 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5fc22bf..8d6436d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,10 +6,11 @@ edition = "2021" license = "MIT OR Apache-2.0" [features] -default = ["cli", "fancy"] +default = ["cli", "cli-pretty-json", "fancy-errors"] -fancy = ["miette/fancy"] +fancy-errors = ["miette/fancy"] cli = ["dep:clap", "dep:tracing-subscriber"] +cli-pretty-json = ["dep:colored_json"] [dependencies] miette = { version = "5.10.0", features = ["fancy"] } @@ -30,6 +31,7 @@ pkce = "0.2.0" clap = { version = "4.3.19", features = ["derive", "env"], optional = true } tracing-subscriber = { version = "0.3.17", optional = true } +colored_json = { version = "3.2.0", optional = true } [dev-dependencies] test-log = { version = "0.2.12", default-features = false, features = ["trace"] } diff --git a/src/cli.rs b/src/cli.rs index faafcd9..48f1f53 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,4 +1,20 @@ +use crate::Data; + pub mod calendar_history; pub mod energy; pub mod powerwall; pub mod vehicle; + +pub fn print_json(data: Data) { + #[cfg(feature = "cli-pretty-json")] + { + use colored_json::prelude::*; + println!("{}", data.body().to_colored_json_auto().unwrap()); + } + + #[cfg(not(feature = "cli-pretty-json"))] + { + println!("{:#?}", data.body()); + panic!(); + } +} diff --git a/src/cli/energy.rs b/src/cli/energy.rs index 36420b1..e3c2c1d 100644 --- a/src/cli/energy.rs +++ b/src/cli/energy.rs @@ -1,5 +1,6 @@ use crate::calendar_history::CalendarHistoryValues; use crate::cli::calendar_history::CalendarHistoryArgs; +use crate::cli::print_json; use crate::energy::EnergySiteId; use crate::Api; use chrono::DateTime; @@ -42,8 +43,7 @@ impl EnergySiteArgs { start_date, end_date, }; - let history = api.energy_sites_calendar_history(&values).await?; - println!("{:#?}", history); + print_json(api.energy_sites_calendar_history(&values).await?); } } Ok(()) diff --git a/src/cli/powerwall.rs b/src/cli/powerwall.rs index b13f1e6..207cb57 100644 --- a/src/cli/powerwall.rs +++ b/src/cli/powerwall.rs @@ -1,4 +1,5 @@ use crate::calendar_history::{HistoryKind, HistoryPeriod}; +use crate::cli::print_json; use crate::powerwall::{PowerwallEnergyHistoryValues, PowerwallId}; use crate::Api; use clap::{Args, Subcommand}; @@ -23,18 +24,18 @@ impl PowerwallArgs { pub async fn run(&self, api: &Api) -> miette::Result<()> { match self.command { PowerwallCommand::Status => { - dbg!(api.powerwall_status(&self.id).await?); + print_json(api.powerwall_status(&self.id).await?); } PowerwallCommand::History => { - dbg!( + print_json( api.powerwall_energy_history(&PowerwallEnergyHistoryValues { powerwall_id: self.id.clone(), period: HistoryPeriod::Day, kind: HistoryKind::Power, start_date: None, - end_date: None + end_date: None, }) - .await? + .await?, ); } } diff --git a/src/cli/vehicle.rs b/src/cli/vehicle.rs index 7cb6d2a..84aa309 100644 --- a/src/cli/vehicle.rs +++ b/src/cli/vehicle.rs @@ -1,3 +1,4 @@ +use crate::cli::print_json; use crate::vehicles::{SetChargeLimit, SetChargingAmps}; use crate::{Api, VehicleId}; use clap::{Args, Subcommand}; @@ -35,28 +36,24 @@ impl VehicleArgs { pub async fn run(self, api: &Api) -> miette::Result<()> { match self.command { VehicleCommand::Data => { - dbg!(api.vehicle_data(&self.id).await?); + print_json(api.vehicle_data(&self.id).await?); } VehicleCommand::ChargeState => { - dbg!(api.charge_state(&self.id).await?); + print_json(api.charge_state(&self.id).await?); } VehicleCommand::SetChargeLimit { percent } => { - dbg!( - api.set_charge_limit(&self.id, &SetChargeLimit { percent }) - .await? - ); + api.set_charge_limit(&self.id, &SetChargeLimit { percent }) + .await?; } VehicleCommand::SetChargingAmps { charging_amps } => { - dbg!( - api.set_charging_amps(&self.id, &SetChargingAmps { charging_amps }) - .await? - ); + api.set_charging_amps(&self.id, &SetChargingAmps { charging_amps }) + .await?; } VehicleCommand::ChargeStart => { - dbg!(api.charge_start(&self.id).await?); + api.charge_start(&self.id).await?; } VehicleCommand::ChargeStop => { - dbg!(api.charge_stop(&self.id).await?); + api.charge_stop(&self.id).await?; } } Ok(()) diff --git a/src/lib.rs b/src/lib.rs index 3a23beb..657a851 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -55,7 +55,7 @@ impl Api { } #[instrument(skip(self))] - async fn get(&self, url: &str) -> Result + async fn get(&self, url: &str) -> Result, TeslatteError> where D: for<'de> Deserialize<'de> + Debug, { @@ -82,10 +82,10 @@ impl Api { })?; trace!(?body); - let json = Self::parse_json::(&body, request)?; - trace!(?json); + let data = Self::parse_json::(&body, request)?; + trace!(?data); - Ok(json) + Ok(Data { data, body }) } #[instrument(skip(self))] @@ -201,10 +201,40 @@ struct ResponseError { #[derive(Debug, Serialize)] struct Empty {} +/// Data and body from a request. The body can be used for debugging. The CLI can optionally +/// print the raw JSON so the user can manipulate it. +/// +/// This struct will automatically deref to the data type for better ergonomics. +#[derive(Debug)] +pub struct Data { + data: T, + body: String, +} + +impl Data { + pub fn data(&self) -> &T { + &self.data + } + + pub fn body(&self) -> &str { + &self.body + } +} + +impl std::ops::Deref for Data { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.data + } +} + /// GET /api/1/[url] macro_rules! get { ($name:ident, $return_type:ty, $url:expr) => { - pub async fn $name(&self) -> Result<$return_type, crate::error::TeslatteError> { + pub async fn $name( + &self, + ) -> Result, crate::error::TeslatteError> { let url = format!("{}{}", crate::API_URL, $url); self.get(&url).await } @@ -220,7 +250,7 @@ macro_rules! get_arg { pub async fn $name( &self, arg: &$arg_type, - ) -> miette::Result<$return_type, crate::error::TeslatteError> { + ) -> miette::Result, crate::error::TeslatteError> { let url = format!($url, arg); let url = format!("{}{}", crate::API_URL, url); self.get(&url).await @@ -235,7 +265,7 @@ macro_rules! get_args { pub async fn $name( &self, values: &$args, - ) -> miette::Result<$return_type, crate::error::TeslatteError> { + ) -> miette::Result, crate::error::TeslatteError> { let url = values.format($url); let url = format!("{}{}", crate::API_URL, url); self.get(&url).await diff --git a/src/main.rs b/src/main.rs index 645a69b..d3e8106 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize}; use teslatte::auth::{AccessToken, RefreshToken}; use teslatte::cli::energy::EnergySiteArgs; use teslatte::cli::powerwall::PowerwallArgs; +use teslatte::cli::print_json; use teslatte::cli::vehicle::VehicleArgs; use teslatte::Api; @@ -10,7 +11,7 @@ use teslatte::Api; /// /// A command line interface for the Tesla API. #[derive(Parser, Debug)] -#[clap(author, version, about, long_about = None)] +#[clap(author, version)] struct Cli { #[clap(subcommand)] command: Command, @@ -105,13 +106,13 @@ async fn main() -> miette::Result<()> { let api = Api::new(access_token, refresh_token); match api_args.command { ApiCommand::Vehicles => { - dbg!(api.vehicles().await?); + print_json(api.vehicles().await?); } ApiCommand::Vehicle(v) => { v.run(&api).await?; } ApiCommand::EnergySites => { - dbg!(api.energy_sites().await?); + print_json(api.energy_sites().await?); } ApiCommand::EnergySite(e) => { e.run(&api).await?;