Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions crates/cli-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ bincode = "2"
toml = { version = "0.8" }
hex = { version = "0.4" }
dotenvy = { version = "0.15" }
comfy-table = { version = "7.2.1" }

nostr = "0.44.2"
nostr-sdk = "0.44.1"
Expand Down
4 changes: 2 additions & 2 deletions crates/cli-client/src/cli/browse.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::cli::Cli;
use crate::cli::interactive::{
SwapDisplay, TokenDisplay, display_swap_table, display_token_table, format_relative_time, format_settlement_asset,
truncate_with_ellipsis,
SwapDisplay, TokenDisplay, format_relative_time, format_settlement_asset, truncate_with_ellipsis,
};
use crate::cli::tables::{display_swap_table, display_token_table};
use crate::config::Config;
use crate::error::Error;

Expand Down
41 changes: 1 addition & 40 deletions crates/cli-client/src/cli/interactive.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::cli::tables::display_token_table;
use crate::error::Error;

use std::io::{self, Write};
Expand Down Expand Up @@ -116,46 +117,6 @@ pub fn format_relative_time(expiry_timestamp: i64) -> String {
}
}

pub fn display_token_table(tokens: &[TokenDisplay]) {
if tokens.is_empty() {
println!(" (No tokens found)");
return;
}

println!(
" {:<3} | {:<18} | {:<14} | {:<18} | Contract",
"#", "Collateral/Token", "Strike/Token", "Expires"
);
println!("{}", "-".repeat(80));

for token in tokens {
println!(
" {:<3} | {:<18} | {:<14} | {:<18} | {}",
token.index, token.collateral, token.settlement, token.expires, token.status
);
}
}

pub fn display_swap_table(swaps: &[SwapDisplay]) {
if swaps.is_empty() {
println!(" (No swaps found)");
return;
}

println!(
" {:<3} | {:<20} | {:<14} | {:<15} | Seller",
"#", "Price", "Wants", "Expires"
);
println!("{}", "-".repeat(80));

for swap in swaps {
println!(
" {:<3} | {:<20} | {:<14} | {:<15} | {}",
swap.index, swap.offering, swap.wants, swap.expires, swap.seller
);
}
}

pub fn prompt_selection(prompt: &str, max: usize) -> io::Result<Option<usize>> {
print!("{prompt} (1-{max}, or 'q' to quit): ");
io::stdout().flush()?;
Expand Down
1 change: 1 addition & 0 deletions crates/cli-client/src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ mod option;
mod positions;
mod swap;
mod sync;
mod tables;
mod tx;
mod wallet;

Expand Down
45 changes: 3 additions & 42 deletions crates/cli-client/src/cli/positions.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
use crate::cli::Cli;
use crate::cli::interactive::{
EnrichedTokenEntry, GRANTOR_TOKEN_TAG, OPTION_TOKEN_TAG, TokenDisplay, display_token_table,
format_asset_value_with_tag, format_asset_with_tag, format_relative_time, format_settlement_asset, format_time_ago,
EnrichedTokenEntry, GRANTOR_TOKEN_TAG, OPTION_TOKEN_TAG, TokenDisplay, format_asset_value_with_tag,
format_asset_with_tag, format_relative_time, format_settlement_asset, format_time_ago,
get_grantor_tokens_from_wallet, get_option_tokens_from_wallet, truncate_with_ellipsis,
};
use crate::cli::tables::{display_collateral_table, display_token_table, display_user_token_table};
use crate::config::Config;
use crate::error::Error;
use crate::metadata::ContractMetadata;
Expand Down Expand Up @@ -167,46 +168,6 @@ pub struct UserTokenDisplay {
pub contract: String,
}

fn display_collateral_table(displays: &[CollateralDisplay]) {
if displays.is_empty() {
println!(" (No locked assets found)");
return;
}

println!(
" {:<3} | {:<18} | {:<14} | {:<18} | Contract",
"#", "Locked Assets", "Settlement", "Expires"
);
println!("{}", "-".repeat(80));

for display in displays {
println!(
" {:<3} | {:<18} | {:<14} | {:<18} | {}",
display.index, display.collateral, display.settlement, display.expires, display.contract
);
}
}

fn display_user_token_table(displays: &[UserTokenDisplay]) {
if displays.is_empty() {
println!(" (No option/grantor tokens found)");
return;
}

println!(
" {:<3} | {:<8} | {:<10} | {:<14} | {:<18} | Contract",
"#", "Type", "Amount", "Strike/Token", "Expires"
);
println!("{}", "-".repeat(90));

for display in displays {
println!(
" {:<3} | {:<8} | {:<10} | {:<14} | {:<18} | {}",
display.index, display.token_type, display.amount, display.strike, display.expires, display.contract
);
}
}

/// Build locked asset displays, filtering to only show collateral or settlement assets (not reissuance tokens)
async fn build_collateral_displays(
wallet: &crate::wallet::Wallet,
Expand Down
123 changes: 123 additions & 0 deletions crates/cli-client/src/cli/tables.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
use crate::cli::interactive::{SwapDisplay, TokenDisplay};
use crate::cli::positions::{CollateralDisplay, UserTokenDisplay};
use comfy_table::presets::UTF8_FULL;
use comfy_table::{Attribute, Cell, Table};

trait TableData {
fn get_header() -> Vec<String>;
fn to_row(&self) -> Vec<String>;
}

impl TableData for TokenDisplay {
fn get_header() -> Vec<String> {
vec!["#", "Collateral/Token", "Strike/Token", "Expires", "Contract"]
.into_iter()
.map(String::from)
.collect()
}
fn to_row(&self) -> Vec<String> {
vec![
self.index.to_string(),
self.collateral.clone(),
self.settlement.clone(),
self.expires.clone(),
self.status.clone(),
]
}
}

impl TableData for SwapDisplay {
fn get_header() -> Vec<String> {
vec!["#", "Price", "Wants", "Expires", "Seller"]
.into_iter()
.map(String::from)
.collect()
}
fn to_row(&self) -> Vec<String> {
vec![
self.index.to_string(),
self.offering.clone(),
self.wants.clone(),
self.expires.clone(),
self.seller.clone(),
]
}
}

impl TableData for CollateralDisplay {
fn get_header() -> Vec<String> {
vec!["#", "Locked Assets", "Settlement", "Expires", "Contract"]
.into_iter()
.map(String::from)
.collect()
}
fn to_row(&self) -> Vec<String> {
vec![
self.index.to_string(),
self.collateral.clone(),
self.settlement.clone(),
self.expires.clone(),
self.contract.clone(),
]
}
}

impl TableData for UserTokenDisplay {
fn get_header() -> Vec<String> {
vec!["#", "Type", "Amount", "Strike/Token", "Expires", "Contract"]
.into_iter()
.map(String::from)
.collect()
}
fn to_row(&self) -> Vec<String> {
vec![
self.index.to_string(),
self.token_type.clone(),
self.amount.clone(),
self.strike.clone(),
self.expires.clone(),
self.contract.clone(),
]
}
}

fn render_table<T: TableData>(items: &[T], empty_msg: &str) {
if items.is_empty() {
println!(" ({empty_msg})");
return;
}

let mut table = Table::new();

table.load_preset(UTF8_FULL);

let header_cells: Vec<Cell> = T::get_header()
.into_iter()
.map(|h| Cell::new(h).add_attribute(Attribute::Bold))
.collect();
table.set_header(header_cells);

for item in items {
table.add_row(item.to_row());
}

for line in table.to_string().lines() {
println!(" {line}");
}
}

pub fn display_token_table(tokens: &[TokenDisplay]) {
render_table(tokens, "No tokens found");
}

pub fn display_swap_table(swaps: &[SwapDisplay]) {
render_table(swaps, "No swaps found");
}

pub fn display_collateral_table(displays: &[CollateralDisplay]) {
render_table(displays, "No locked assets found");
}

pub fn display_user_token_table(displays: &[UserTokenDisplay]) {
render_table(displays, "No option/grantor tokens found");
}
Loading