Skip to content

Commit

Permalink
Reformat history table
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasPickering committed Feb 27, 2025
1 parent 879ff40 commit 313ff22
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
### Changed

- Upgrade to Rust 1.85 (2024 edition!)
- Improve format of `slumber history list` table output

## [3.0.1] - 2025-02-19

Expand Down
54 changes: 42 additions & 12 deletions crates/cli/src/commands/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ use crate::{
use anyhow::{anyhow, bail};
use clap::Parser;
use clap_complete::ArgValueCompleter;
use itertools::Itertools;
use slumber_core::{
collection::{ProfileId, RecipeId},
db::{Database, DatabaseMode, ProfileFilter},
http::{ExchangeSummary, RequestId},
http::RequestId,
util::{confirm, format_time_iso},
};
use std::{process::ExitCode, str::FromStr};
use std::{iter, process::ExitCode, str::FromStr};

/// View and modify request history
///
Expand Down Expand Up @@ -90,7 +91,25 @@ impl Subcommand for HistoryCommand {
.into_collection(&global.collection_path()?)?;
let exchanges =
database.get_recipe_requests(profile.into(), &recipe)?;
print_list(exchanges);
print_table(
["Recipe", "Profile", "Time", "Status", "Request ID"],
&exchanges
.into_iter()
.map(|exchange| {
[
exchange.recipe_id.to_string(),
exchange
.profile_id
.map(ProfileId::into)
.unwrap_or_default(),
format_time_iso(&exchange.start_time)
.to_string(),
exchange.status.as_u16().to_string(),
exchange.id.to_string(),
]
})
.collect_vec(),
);
}

HistorySubcommand::Get { request, display } => {
Expand Down Expand Up @@ -224,14 +243,25 @@ enum RequestSelection {
}

/// Print request history as a table
fn print_list(exchanges: Vec<ExchangeSummary>) {
for exchange in exchanges {
println!(
"{}\t{}\t{}\t{}",
exchange.profile_id.as_deref().unwrap_or_default(),
exchange.id,
exchange.status.as_str(),
format_time_iso(&exchange.start_time),
);
fn print_table<const N: usize>(header: [&str; N], rows: &[[String; N]]) {
// For each column, find the largest width of any cell
let mut widths = [0; N];
for column in 0..N {
widths[column] = iter::once(header[column].len())
.chain(rows.iter().map(|row| row[column].len()))
.max()
.unwrap_or_default()
+ 1; // Min width, for spacing
}

for (header, width) in header.into_iter().zip(widths.iter()) {
print!("{:<width$}", header, width = width);
}
println!();
for row in rows {
for (cell, width) in row.iter().zip(widths) {
print!("{:<width$}", cell, width = width);
}
println!();
}
}
2 changes: 1 addition & 1 deletion crates/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ anyhow = {workspace = true}
async-trait = "0.1.81"
bytes = {workspace = true, features = ["serde"]}
chrono = {workspace = true, features = ["clock", "serde", "std"]}
derive_more = {workspace = true, features = ["debug", "deref", "deref_mut", "display", "from", "from_str"]}
derive_more = {workspace = true, features = ["debug", "deref", "deref_mut", "display", "from", "from_str", "into"]}
dialoguer = {workspace = true}
dirs = {workspace = true}
futures = {workspace = true}
Expand Down
4 changes: 3 additions & 1 deletion crates/core/src/collection/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
util::{ResultTraced, parse_yaml},
};
use anyhow::Context;
use derive_more::{Deref, Display, From, FromStr};
use derive_more::{Deref, Display, From, FromStr, Into};
use indexmap::IndexMap;
use mime::Mime;
use reqwest::header;
Expand Down Expand Up @@ -109,6 +109,7 @@ impl crate::test_util::Factory for Profile {
Eq,
From,
Hash,
Into,
PartialEq,
Serialize,
Deserialize,
Expand Down Expand Up @@ -246,6 +247,7 @@ impl crate::test_util::Factory<&str> for Recipe {
Eq,
From,
Hash,
Into,
PartialEq,
Serialize,
Deserialize,
Expand Down
4 changes: 2 additions & 2 deletions crates/core/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ impl Database {
trace!("Fetching requests for all collections");
self.connection()
.prepare(
"SELECT id, profile_id, start_time, end_time, status_code
"SELECT id, recipe_id, profile_id, start_time, end_time, status_code
FROM requests_v2 ORDER BY start_time DESC",
)?
.query_map((), |row| row.try_into())
Expand Down Expand Up @@ -388,7 +388,7 @@ impl CollectionDatabase {
// is asking for all profiles. Dynamically modifying the query
// is really ugly so the easiest thing is to use an additional
// parameter to bypass the filter
"SELECT id, profile_id, start_time, end_time, status_code
"SELECT id, recipe_id, profile_id, start_time, end_time, status_code
FROM requests_v2
WHERE collection_id = :collection_id
AND (:ignore_profile_id OR profile_id IS :profile_id)
Expand Down
1 change: 1 addition & 0 deletions crates/core/src/db/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,7 @@ impl<'a, 'b> TryFrom<&'a Row<'b>> for ExchangeSummary {
fn try_from(row: &'a Row<'b>) -> Result<Self, Self::Error> {
Ok(Self {
id: row.get("id")?,
recipe_id: row.get("recipe_id")?,
profile_id: row.get("profile_id")?,
start_time: row.get("start_time")?,
end_time: row.get("end_time")?,
Expand Down
24 changes: 12 additions & 12 deletions crates/core/src/http/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,31 +379,31 @@ impl Exchange {
pub fn duration(&self) -> Duration {
self.end_time - self.start_time
}

pub fn summary(&self) -> ExchangeSummary {
ExchangeSummary {
id: self.id,
recipe_id: self.request.recipe_id.clone(),
profile_id: self.request.profile_id.clone(),
start_time: self.start_time,
end_time: self.end_time,
status: self.response.status,
}
}
}

/// Metadata about an exchange. Useful in lists where request/response content
/// isn't needed.
#[derive(Clone, Debug)]
pub struct ExchangeSummary {
pub id: RequestId,
pub recipe_id: RecipeId,
pub profile_id: Option<ProfileId>,
pub start_time: DateTime<Utc>,
pub end_time: DateTime<Utc>,
pub status: StatusCode,
}

impl From<&Exchange> for ExchangeSummary {
fn from(exchange: &Exchange) -> Self {
Self {
id: exchange.id,
profile_id: exchange.request.profile_id.clone(),
start_time: exchange.start_time,
end_time: exchange.end_time,
status: exchange.response.status,
}
}
}

/// Data for an HTTP request. This is similar to [reqwest::Request], but differs
/// in some key ways:
/// - Each [reqwest::Request] can only exist once (from creation to sending),
Expand Down
2 changes: 1 addition & 1 deletion crates/tui/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ impl From<&RequestState> for RequestStateSummary {
end_time: *end_time,
},
RequestState::Response { exchange } => {
Self::Response(exchange.into())
Self::Response(exchange.summary())
}
RequestState::RequestError { error } => Self::RequestError {
id: error.request.id,
Expand Down

0 comments on commit 313ff22

Please sign in to comment.