feat: cli only prints json, cli-pretty-json feature
This commit is contained in:
parent
44fe9bc48e
commit
8881d0fa59
|
@ -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"] }
|
||||||
|
|
16
src/cli.rs
16
src/cli.rs
|
@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
|
@ -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?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(())
|
||||||
|
|
44
src/lib.rs
44
src/lib.rs
|
@ -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
|
||||||
|
|
|
@ -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?;
|
||||||
|
|
Loading…
Reference in a new issue