diff --git a/servers/src/common/stats.rs b/servers/src/common/stats.rs index 2ca1743f7..3c23406ad 100644 --- a/servers/src/common/stats.rs +++ b/servers/src/common/stats.rs @@ -193,6 +193,24 @@ pub struct PeerStats { pub received_bytes_per_sec: u64, } +impl PartialEq for PeerStats { + fn eq(&self, other: &PeerStats) -> bool { + *self.addr == other.addr + } +} + +impl PartialEq for WorkerStats { + fn eq(&self, other: &WorkerStats) -> bool { + *self.id == other.id + } +} + +impl PartialEq for DiffBlock { + fn eq(&self, other: &DiffBlock) -> bool { + self.block_height == other.block_height + } +} + impl StratumStats { /// Calculate network hashrate pub fn network_hashrate(&self, height: u64) -> f64 { diff --git a/src/bin/tui/peers.rs b/src/bin/tui/peers.rs index 19cd6a27b..6e42c8aac 100644 --- a/src/bin/tui/peers.rs +++ b/src/bin/tui/peers.rs @@ -33,7 +33,7 @@ use crate::tui::table::{TableView, TableViewItem}; use crate::tui::types::TUIStatusListener; #[derive(Copy, Clone, PartialEq, Eq, Hash)] -enum PeerColumn { +pub enum PeerColumn { Address, State, UsedBandwidth, diff --git a/src/bin/tui/table.rs b/src/bin/tui/table.rs index 76db2f0a6..a3a01de08 100644 --- a/src/bin/tui/table.rs +++ b/src/bin/tui/table.rs @@ -145,7 +145,7 @@ where /// .default_column(BasicColumn::Name); /// # } /// ``` -pub struct TableView, H: Eq + Hash + Copy + Clone + 'static> { +pub struct TableView + PartialEq, H: Eq + Hash + Copy + Clone + 'static> { enabled: bool, scrollbase: ScrollBase, last_size: Vec2, @@ -165,7 +165,7 @@ pub struct TableView, H: Eq + Hash + Copy + Clone + 'static> on_select: Option>, } -impl, H: Eq + Hash + Copy + Clone + 'static> TableView { +impl + PartialEq, H: Eq + Hash + Copy + Clone + 'static> TableView { /// Creates a new empty `TableView` without any columns. /// /// A TableView should be accompanied by a enum of type `H` representing @@ -233,8 +233,7 @@ impl, H: Eq + Hash + Copy + Clone + 'static> TableView pub fn sort_by(&mut self, column: H, order: Ordering) { if self.column_indices.contains_key(&column) { for c in &mut self.columns { - c.selected = c.column == column; - if c.selected { + if c.column == column { c.order = order; } else { c.order = Ordering::Equal; @@ -437,8 +436,19 @@ impl, H: Eq + Hash + Copy + Clone + 'static> TableView /// Sets the contained items of the table. /// /// The currently active sort order is preserved and will be applied to all - /// items. + /// items. The selected item will also be preserved. pub fn set_items(&mut self, items: Vec) { + let mut new_location = 0; + if let Some(old_item_location) = self.item() { + let old_item = self.items.get(old_item_location).unwrap(); + for (i, new_item) in items.iter().enumerate() { + if old_item == new_item { + new_location = i; + break; + } + } + } + self.items = items; self.rows_to_items = Vec::with_capacity(self.items.len()); @@ -453,7 +463,7 @@ impl, H: Eq + Hash + Copy + Clone + 'static> TableView self.scrollbase .set_heights(self.last_size.y.saturating_sub(2), self.rows_to_items.len()); - self.set_selected_row(0); + self.set_selected_item(new_location); } /// Sets the contained items of the table. @@ -580,7 +590,7 @@ impl, H: Eq + Hash + Copy + Clone + 'static> TableView } } -impl, H: Eq + Hash + Copy + Clone + 'static> TableView { +impl + PartialEq, H: Eq + Hash + Copy + Clone + 'static> TableView { fn draw_columns, &TableColumn)>( &self, printer: &Printer<'_, '_>, @@ -689,7 +699,7 @@ impl, H: Eq + Hash + Copy + Clone + 'static> TableView } } -impl + 'static, H: Eq + Hash + Copy + Clone + 'static> View +impl + PartialEq + 'static, H: Eq + Hash + Copy + Clone + 'static> View for TableView { fn draw(&self, printer: &Printer<'_, '_>) { @@ -981,3 +991,75 @@ impl TableColumn { printer.print((0, 0), value.as_str()); } } + +#[cfg(test)] +mod test { + use crate::tui::peers::PeerColumn; + use crate::tui::table::TableView; + use chrono::Utc; + use grin_core::ser::ProtocolVersion; + use grin_servers::PeerStats; + use std::cmp::Ordering; + + #[test] + pub fn test_set_items_preserves_selected_item() { + let mut table = TableView::::new(); + let ps1 = PeerStats { + addr: "123.0.0.1".to_string(), + ..TestPeerStats::default() + }; + let ps2 = PeerStats { + addr: "123.0.0.2".to_string(), + ..TestPeerStats::default() + }; + + let mut items = vec![ps1, ps2]; + table.set_items(items.clone()); + assert_eq!(table.item().unwrap(), 0); + + items.reverse(); + table.set_items(items); + assert_eq!(table.item().unwrap(), 1); + } + + #[test] + pub fn test_set_items_preserves_order() { + let mut table = TableView::::new(); + let ps1 = PeerStats { + addr: "123.0.0.1".to_string(), + received_bytes_per_sec: 10, + ..TestPeerStats::default() + }; + let ps2 = PeerStats { + addr: "123.0.0.2".to_string(), + received_bytes_per_sec: 80, + ..TestPeerStats::default() + }; + + let items = vec![ps1, ps2]; + table.set_items(items); + assert_eq!(table.rows_to_items[0], 0); + table.sort_by(PeerColumn::UsedBandwidth, Ordering::Greater); + + assert_eq!(table.rows_to_items[0], 1); + } + + struct TestPeerStats(PeerStats); + + impl TestPeerStats { + fn default() -> PeerStats { + PeerStats { + state: "Connected".to_string(), + addr: "127.0.0.1".to_string(), + version: ProtocolVersion::local(), + user_agent: "".to_string(), + total_difficulty: 0, + height: 0, + direction: "Outbound".to_string(), + last_seen: Utc::now(), + sent_bytes_per_sec: 0, + received_bytes_per_sec: 0, + } + } + } +}