Types, and testing formatted view

This commit is contained in:
2026-07-15 11:42:00 +02:00
parent c773bfb2be
commit d2cc33d517
2 changed files with 131 additions and 13 deletions
+37 -13
View File
@@ -1,3 +1,5 @@
pub mod types;
use std::any::Any; use std::any::Any;
use pulse_ui::{ use pulse_ui::{
@@ -13,12 +15,38 @@ use pulse_ui::{
}, },
}; };
use crate::types::{Formatted, WatchListItem, space_out};
pub struct PulseTradeApp { pub struct PulseTradeApp {
command: State<InputState>, command: State<InputState>,
watch_list: State<Vec<WatchListItem>>,
} }
impl App for PulseTradeApp { impl App for PulseTradeApp {
async fn init(&mut self, ctx: &pulse_ui::state::Context) {} async fn init(&mut self, ctx: &pulse_ui::state::Context) {
let mut watch_list = self.watch_list.lock().await;
watch_list.push(WatchListItem {
symbol: "BTC".to_string(),
price: 118_402.12,
trend: 0.82,
});
watch_list.push(WatchListItem {
symbol: "ETH".to_string(),
price: 3_912.48,
trend: 0.41,
});
watch_list.push(WatchListItem {
symbol: "SOL".to_string(),
price: 182.91,
trend: 2.18,
});
watch_list.push(WatchListItem {
symbol: "XRP".to_string(),
price: 2.84,
trend: 1.22,
});
}
async fn update(&mut self, ctx: &pulse_ui::state::Context, event: Box<dyn Any + Send + Sync>) { async fn update(&mut self, ctx: &pulse_ui::state::Context, event: Box<dyn Any + Send + Sync>) {
if let Some(Refresh) = event.downcast_ref() { if let Some(Refresh) = event.downcast_ref() {
@@ -77,16 +105,10 @@ impl App for PulseTradeApp {
SpacedColumns(vec![ SpacedColumns(vec![
( (
LayoutItem::Widget(Size::Flex(1)), LayoutItem::Widget(Size::Flex(1)),
Box::new( Box::new(format!(
vec![ " WATCHLIST\n{}",
" WATCHLIST", space_out(self.watch_list.lock().await.get_formatted()).join("\n")
" BTC 118,402.12 ▲ 0.82%", )),
" ETH 3,912.48 ▼ 0.41%",
" SOL 182.91 ▲ 2.18%",
" XRP 2.84 ▲ 1.22%",
]
.join("\n"),
),
), ),
( (
LayoutItem::Widget(Size::Flex(1)), LayoutItem::Widget(Size::Flex(1)),
@@ -129,7 +151,8 @@ impl App for PulseTradeApp {
vec![ vec![
" SIGNALS", " SIGNALS",
" BUY BTC LIM 118,800.12", " BUY BTC LIM 118,800.12",
" STL BTC 120,00.00", " BUY BTC STL 118,000.00",
" BUY BTC TAP 120,000.00",
] ]
.join("\n"), .join("\n"),
), ),
@@ -150,7 +173,7 @@ impl App for PulseTradeApp {
]), ]),
); );
layout.draw(6, Input("> ", &*self.command.lock().await)); layout.draw(6, Input(" > ", &*self.command.lock().await));
} }
} }
@@ -158,6 +181,7 @@ impl App for PulseTradeApp {
async fn main() { async fn main() {
pulse_ui::run(|ctx| PulseTradeApp { pulse_ui::run(|ctx| PulseTradeApp {
command: ctx.use_state(InputState::new()), command: ctx.use_state(InputState::new()),
watch_list: ctx.use_state(Vec::new()),
}) })
.await; .await;
} }
+94
View File
@@ -0,0 +1,94 @@
pub trait Formatted {
fn get_formatted(&self) -> Vec<String>;
}
pub struct WatchListItem {
pub symbol: String,
pub price: f64,
pub trend: f64,
}
pub struct ActivePosition {
pub symbol: String,
pub profit: f64,
pub lot: f64,
}
pub struct Account {
pub equity: f64,
pub liquid: f64,
pub unreal: f64,
pub realized: f64,
pub margin: f64,
}
impl<T: Formatted> Formatted for Vec<T> {
fn get_formatted(&self) -> Vec<String> {
let mut output = Vec::new();
let rows: Vec<Vec<String>> = self.iter().map(|v| v.get_formatted()).collect();
if rows.is_empty() {
return output;
}
let column_count = rows.iter().map(|r| r.len()).max().unwrap_or(0);
let mut widths = vec![0; column_count];
// Find the largest width per column
for row in &rows {
for (i, col) in row.iter().enumerate() {
widths[i] = widths[i].max(col.len());
}
}
// Render rows
for (row_idx, row) in rows.iter().enumerate() {
let mut row_out = String::new();
for (col_idx, col) in row.iter().enumerate() {
let width = widths[col_idx];
// Right-align numbers, left-align text could be customized here
row_out.push_str(&format!("{:>width$}", col, width = width));
if col_idx + 1 != row.len() {
row_out.push_str(" ");
}
}
if row_idx + 1 != rows.len() {
output.push(row_out);
}
}
output
}
}
impl Formatted for WatchListItem {
fn get_formatted(&self) -> Vec<String> {
vec![
self.symbol.to_string(),
self.price.to_string(),
format!(
"{} {}",
if self.trend.is_sign_positive() {
"▲"
} else {
"▼"
},
self.trend.abs()
),
]
}
}
pub fn space_out(mut items: Vec<String>) -> Vec<String> {
for item in &mut items {
item.insert(0, ' ');
}
items
}