use std::fmt::Write;
use anyhow::Result;
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use clap::Parser;
use colored::Colorize;
use cronback_client::{
Pagination,
Recurring,
RunAt,
Schedule,
TriggerStatus,
TriggersFilter,
};
use prettytable::{row, Table};
use crate::args::CommonOptions;
use crate::ui::FancyToString;
use crate::{emitln, Command};
#[derive(Clone, Debug, Parser)]
pub struct List {
#[clap(long)]
cursor: Option<String>,
#[clap(long, default_value = "20")]
limit: Option<i32>,
#[clap(long)]
status: Option<Vec<TriggerStatus>>,
#[clap(long, short)]
all: bool,
}
#[async_trait]
impl Command for List {
async fn run<
A: tokio::io::AsyncWrite + Send + Sync + Unpin,
B: tokio::io::AsyncWrite + Send + Sync + Unpin,
>(
&self,
out: &mut tokio::io::BufWriter<A>,
err: &mut tokio::io::BufWriter<B>,
common_options: &CommonOptions,
) -> Result<()> {
let client = common_options.new_client()?;
let status: Vec<TriggerStatus> = match self.status {
| Some(ref statuses) => statuses.to_vec(),
| None => {
vec![
TriggerStatus::Scheduled,
TriggerStatus::OnDemand,
TriggerStatus::Paused,
]
}
};
let filter = if self.all {
None
} else {
Some(TriggersFilter { status })
};
let pagination = Some(Pagination {
cursor: self.cursor.clone(),
limit: self.limit,
});
let response =
cronback_client::triggers::list(&client, pagination, filter)
.await?;
let response = response.into_inner()?;
if !response.data.is_empty() {
let len = response.data.len();
let mut table = Table::new();
table.set_titles(row![
"Name",
"Status",
"Schedule",
"Runs",
"End Point",
"Payload Size",
"Created At",
]);
for trigger in response.data {
let endpoint = trigger
.webhook()
.and_then(|e| e.url.clone())
.unwrap_or_default();
table.add_row(row![
trigger.name.expect("name should be present"),
trigger.status.fancy(),
trigger.schedule.map(fancy_schedule).unwrap_or_default(),
fancy_runs(
trigger.last_ran_at,
trigger.estimated_future_runs
),
endpoint,
trigger
.payload
.map(|x| x.body)
.map(|y| format!("{} bytes", y.as_bytes().len()))
.unwrap_or_default(),
trigger
.created_at
.expect("created_at should be present")
.to_rfc2822(),
]);
}
emitln!(out, "{}", table);
emitln!(err, "{len} Triggers Shown");
if let Some(next_page_cursor) = response.meta.next_cursor {
emitln!(
err,
"View next page by {}{}",
"--cursor=".bold(),
next_page_cursor.bold()
);
}
}
Ok(())
}
}
fn fancy_runs(
last_ran_at: Option<DateTime<Utc>>,
estimated_future_runs: Vec<DateTime<Utc>>,
) -> String {
let mut runs = String::new();
let last_ran_at = last_ran_at.map(|x| x.to_rfc2822());
if let Some(last_ran_at) = last_ran_at {
writeln!(&mut runs, "Last run was at:").unwrap();
writeln!(&mut runs, " - {}", last_ran_at).unwrap();
};
let future_runs: Vec<_> = estimated_future_runs
.into_iter()
.map(|x| x.to_rfc2822())
.collect();
if !future_runs.is_empty() {
writeln!(&mut runs, "Next runs:").unwrap();
for run in future_runs {
writeln!(&mut runs, " - {}", run).unwrap();
}
};
runs
}
fn fancy_schedule(schedule: Schedule) -> String {
fn fancy_recurring(r: Recurring) -> String {
let mut buf = String::new();
writeln!(buf, "{}", r.cron.unwrap_or_default()).unwrap();
if let Some(tz) = r.timezone {
writeln!(buf, "{}", tz).unwrap();
}
if let Some(limit) = r.limit {
write!(buf, "{} limit", limit).unwrap();
if let Some(remaining) = r.remaining {
write!(buf, " ({} remaining)", remaining).unwrap();
}
writeln!(buf).unwrap();
}
buf
}
fn fancy_run_at(r: RunAt) -> String {
let mut buf = String::new();
let len = r.timepoints.len();
let mut done_timepoints = r.timepoints.clone();
done_timepoints.sort();
let remaining_timepoints;
if let Some(remaining) = r.remaining {
writeln!(buf, "({} remaining)", remaining).unwrap();
remaining_timepoints =
done_timepoints.split_off(len - remaining as usize);
} else {
remaining_timepoints = done_timepoints.clone();
done_timepoints.clear();
}
for t in done_timepoints {
writeln!(buf, " - {}", t.to_rfc2822().strikethrough()).unwrap();
}
for t in remaining_timepoints {
writeln!(buf, " - {}", t.to_rfc2822()).unwrap();
}
buf
}
match schedule {
| Schedule::Recurring(s) => fancy_recurring(s),
| Schedule::RunAt(s) => fancy_run_at(s),
| _ => "Unknown Schedule Type!".to_string(),
}
}