feat: cli only prints json, cli-pretty-json feature

This commit is contained in:
gak 2023-08-29 13:11:28 +10:00
parent 44fe9bc48e
commit 8881d0fa59
No known key found for this signature in database
7 changed files with 77 additions and 30 deletions

View file

@ -6,10 +6,11 @@ edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
[features] [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 = ["dep:clap", "dep:tracing-subscriber"]
cli-pretty-json = ["dep:colored_json"]
[dependencies] [dependencies]
miette = { version = "5.10.0", features = ["fancy"] } 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 } clap = { version = "4.3.19", features = ["derive", "env"], optional = true }
tracing-subscriber = { version = "0.3.17", optional = true } tracing-subscriber = { version = "0.3.17", optional = true }
colored_json = { version = "3.2.0", optional = true }
[dev-dependencies] [dev-dependencies]
test-log = { version = "0.2.12", default-features = false, features = ["trace"] } test-log = { version = "0.2.12", default-features = false, features = ["trace"] }

View file

@ -1,4 +1,20 @@
use crate::Data;
pub mod calendar_history; pub mod calendar_history;
pub mod energy; pub mod energy;
pub mod powerwall; pub mod powerwall;
pub mod vehicle; pub mod vehicle;
pub fn print_json<T>(data: Data<T>) {
#[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!();
}
}

View file

@ -1,5 +1,6 @@
use crate::calendar_history::CalendarHistoryValues; use crate::calendar_history::CalendarHistoryValues;
use crate::cli::calendar_history::CalendarHistoryArgs; use crate::cli::calendar_history::CalendarHistoryArgs;
use crate::cli::print_json;
use crate::energy::EnergySiteId; use crate::energy::EnergySiteId;
use crate::Api; use crate::Api;
use chrono::DateTime; use chrono::DateTime;
@ -42,8 +43,7 @@ impl EnergySiteArgs {
start_date, start_date,
end_date, end_date,
}; };
let history = api.energy_sites_calendar_history(&values).await?; print_json(api.energy_sites_calendar_history(&values).await?);
println!("{:#?}", history);
} }
} }
Ok(()) Ok(())

View file

@ -1,4 +1,5 @@
use crate::calendar_history::{HistoryKind, HistoryPeriod}; use crate::calendar_history::{HistoryKind, HistoryPeriod};
use crate::cli::print_json;
use crate::powerwall::{PowerwallEnergyHistoryValues, PowerwallId}; use crate::powerwall::{PowerwallEnergyHistoryValues, PowerwallId};
use crate::Api; use crate::Api;
use clap::{Args, Subcommand}; use clap::{Args, Subcommand};
@ -23,18 +24,18 @@ impl PowerwallArgs {
pub async fn run(&self, api: &Api) -> miette::Result<()> { pub async fn run(&self, api: &Api) -> miette::Result<()> {
match self.command { match self.command {
PowerwallCommand::Status => { PowerwallCommand::Status => {
dbg!(api.powerwall_status(&self.id).await?); print_json(api.powerwall_status(&self.id).await?);
} }
PowerwallCommand::History => { PowerwallCommand::History => {
dbg!( print_json(
api.powerwall_energy_history(&PowerwallEnergyHistoryValues { api.powerwall_energy_history(&PowerwallEnergyHistoryValues {
powerwall_id: self.id.clone(), powerwall_id: self.id.clone(),
period: HistoryPeriod::Day, period: HistoryPeriod::Day,
kind: HistoryKind::Power, kind: HistoryKind::Power,
start_date: None, start_date: None,
end_date: None end_date: None,
}) })
.await? .await?,
); );
} }
} }

View file

@ -1,3 +1,4 @@
use crate::cli::print_json;
use crate::vehicles::{SetChargeLimit, SetChargingAmps}; use crate::vehicles::{SetChargeLimit, SetChargingAmps};
use crate::{Api, VehicleId}; use crate::{Api, VehicleId};
use clap::{Args, Subcommand}; use clap::{Args, Subcommand};
@ -35,28 +36,24 @@ impl VehicleArgs {
pub async fn run(self, api: &Api) -> miette::Result<()> { pub async fn run(self, api: &Api) -> miette::Result<()> {
match self.command { match self.command {
VehicleCommand::Data => { VehicleCommand::Data => {
dbg!(api.vehicle_data(&self.id).await?); print_json(api.vehicle_data(&self.id).await?);
} }
VehicleCommand::ChargeState => { VehicleCommand::ChargeState => {
dbg!(api.charge_state(&self.id).await?); print_json(api.charge_state(&self.id).await?);
} }
VehicleCommand::SetChargeLimit { percent } => { VehicleCommand::SetChargeLimit { percent } => {
dbg!(
api.set_charge_limit(&self.id, &SetChargeLimit { percent }) api.set_charge_limit(&self.id, &SetChargeLimit { percent })
.await? .await?;
);
} }
VehicleCommand::SetChargingAmps { charging_amps } => { VehicleCommand::SetChargingAmps { charging_amps } => {
dbg!(
api.set_charging_amps(&self.id, &SetChargingAmps { charging_amps }) api.set_charging_amps(&self.id, &SetChargingAmps { charging_amps })
.await? .await?;
);
} }
VehicleCommand::ChargeStart => { VehicleCommand::ChargeStart => {
dbg!(api.charge_start(&self.id).await?); api.charge_start(&self.id).await?;
} }
VehicleCommand::ChargeStop => { VehicleCommand::ChargeStop => {
dbg!(api.charge_stop(&self.id).await?); api.charge_stop(&self.id).await?;
} }
} }
Ok(()) Ok(())

View file

@ -55,7 +55,7 @@ impl Api {
} }
#[instrument(skip(self))] #[instrument(skip(self))]
async fn get<D>(&self, url: &str) -> Result<D, TeslatteError> async fn get<D>(&self, url: &str) -> Result<Data<D>, TeslatteError>
where where
D: for<'de> Deserialize<'de> + Debug, D: for<'de> Deserialize<'de> + Debug,
{ {
@ -82,10 +82,10 @@ impl Api {
})?; })?;
trace!(?body); trace!(?body);
let json = Self::parse_json::<D, _>(&body, request)?; let data = Self::parse_json::<D, _>(&body, request)?;
trace!(?json); trace!(?data);
Ok(json) Ok(Data { data, body })
} }
#[instrument(skip(self))] #[instrument(skip(self))]
@ -201,10 +201,40 @@ struct ResponseError {
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
struct Empty {} 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<T> {
data: T,
body: String,
}
impl<T> Data<T> {
pub fn data(&self) -> &T {
&self.data
}
pub fn body(&self) -> &str {
&self.body
}
}
impl<T> std::ops::Deref for Data<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.data
}
}
/// GET /api/1/[url] /// GET /api/1/[url]
macro_rules! get { macro_rules! get {
($name:ident, $return_type:ty, $url:expr) => { ($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::Data<$return_type>, crate::error::TeslatteError> {
let url = format!("{}{}", crate::API_URL, $url); let url = format!("{}{}", crate::API_URL, $url);
self.get(&url).await self.get(&url).await
} }
@ -220,7 +250,7 @@ macro_rules! get_arg {
pub async fn $name( pub async fn $name(
&self, &self,
arg: &$arg_type, arg: &$arg_type,
) -> miette::Result<$return_type, crate::error::TeslatteError> { ) -> miette::Result<crate::Data<$return_type>, crate::error::TeslatteError> {
let url = format!($url, arg); let url = format!($url, arg);
let url = format!("{}{}", crate::API_URL, url); let url = format!("{}{}", crate::API_URL, url);
self.get(&url).await self.get(&url).await
@ -235,7 +265,7 @@ macro_rules! get_args {
pub async fn $name( pub async fn $name(
&self, &self,
values: &$args, values: &$args,
) -> miette::Result<$return_type, crate::error::TeslatteError> { ) -> miette::Result<crate::Data<$return_type>, crate::error::TeslatteError> {
let url = values.format($url); let url = values.format($url);
let url = format!("{}{}", crate::API_URL, url); let url = format!("{}{}", crate::API_URL, url);
self.get(&url).await self.get(&url).await

View file

@ -3,6 +3,7 @@ use serde::{Deserialize, Serialize};
use teslatte::auth::{AccessToken, RefreshToken}; use teslatte::auth::{AccessToken, RefreshToken};
use teslatte::cli::energy::EnergySiteArgs; use teslatte::cli::energy::EnergySiteArgs;
use teslatte::cli::powerwall::PowerwallArgs; use teslatte::cli::powerwall::PowerwallArgs;
use teslatte::cli::print_json;
use teslatte::cli::vehicle::VehicleArgs; use teslatte::cli::vehicle::VehicleArgs;
use teslatte::Api; use teslatte::Api;
@ -10,7 +11,7 @@ use teslatte::Api;
/// ///
/// A command line interface for the Tesla API. /// A command line interface for the Tesla API.
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[clap(author, version, about, long_about = None)] #[clap(author, version)]
struct Cli { struct Cli {
#[clap(subcommand)] #[clap(subcommand)]
command: Command, command: Command,
@ -105,13 +106,13 @@ async fn main() -> miette::Result<()> {
let api = Api::new(access_token, refresh_token); let api = Api::new(access_token, refresh_token);
match api_args.command { match api_args.command {
ApiCommand::Vehicles => { ApiCommand::Vehicles => {
dbg!(api.vehicles().await?); print_json(api.vehicles().await?);
} }
ApiCommand::Vehicle(v) => { ApiCommand::Vehicle(v) => {
v.run(&api).await?; v.run(&api).await?;
} }
ApiCommand::EnergySites => { ApiCommand::EnergySites => {
dbg!(api.energy_sites().await?); print_json(api.energy_sites().await?);
} }
ApiCommand::EnergySite(e) => { ApiCommand::EnergySite(e) => {
e.run(&api).await?; e.run(&api).await?;