feat(cli): db list in reverse (#2972)

This commit is contained in:
rakita
2023-06-03 14:40:07 +02:00
committed by GitHub
parent 2e406c1791
commit 5e01f9d7b4
3 changed files with 48 additions and 44 deletions

View File

@ -72,9 +72,12 @@ pub enum Subcommands {
pub struct ListArgs {
/// The table name
table: String, // TODO: Convert to enum
/// Where to start iterating
/// Skip first N entries
#[arg(long, short, default_value = "0")]
start: usize,
skip: usize,
/// Reverse the order of the entries. If enabled last table entries are read.
#[arg(long, short, default_value = "false")]
reverse: bool,
/// How many items to take from the walker
#[arg(long, short, default_value = DEFAULT_NUM_ITEMS)]
len: usize,
@ -168,12 +171,12 @@ impl Command {
}
if args.json {
let list_result = tool.list::<tables::$table>(args.start, args.len)?.into_iter().collect::<Vec<_>>();
let list_result = tool.list::<tables::$table>(args.skip, args.len,args.reverse)?.into_iter().collect::<Vec<_>>();
println!("{}", serde_json::to_string_pretty(&list_result)?);
Ok(())
} else {
tui::DbListTUI::<_, tables::$table>::new(|start, count| {
tool.list::<tables::$table>(start, count).unwrap()
tui::DbListTUI::<_, tables::$table>::new(|skip, count| {
tool.list::<tables::$table>(skip, count, args.reverse).unwrap()
}, $start, $len, total_entries).run()
}
})??
@ -186,7 +189,7 @@ impl Command {
}
}
table_tui!(args.table.as_str(), args.start, args.len => [
table_tui!(args.table.as_str(), args.skip, args.len => [
CanonicalHeaders,
HeaderTD,
HeaderNumbers,

View File

@ -5,7 +5,6 @@ use crossterm::{
};
use reth_db::table::Table;
use std::{
collections::BTreeMap,
io,
time::{Duration, Instant},
};
@ -46,15 +45,15 @@ pub(crate) enum ViewMode {
#[derive(Default)]
pub(crate) struct DbListTUI<F, T: Table>
where
F: FnMut(usize, usize) -> BTreeMap<T::Key, T::Value>,
F: FnMut(usize, usize) -> Vec<(T::Key, T::Value)>,
{
/// Fetcher for the next page of items.
///
/// The fetcher is passed the index of the first item to fetch, and the number of items to
/// fetch from that item.
fetch: F,
/// The starting index of the key list in the DB.
start: usize,
/// Skip N indices of the key list in the DB.
skip: usize,
/// The amount of entries to show per page
count: usize,
/// The total number of entries in the database
@ -66,24 +65,24 @@ where
/// The state of the key list.
list_state: ListState,
/// Entries to show in the TUI.
entries: BTreeMap<T::Key, T::Value>,
entries: Vec<(T::Key, T::Value)>,
}
impl<F, T: Table> DbListTUI<F, T>
where
F: FnMut(usize, usize) -> BTreeMap<T::Key, T::Value>,
F: FnMut(usize, usize) -> Vec<(T::Key, T::Value)>,
{
/// Create a new database list TUI
pub(crate) fn new(fetch: F, start: usize, count: usize, total_entries: usize) -> Self {
pub(crate) fn new(fetch: F, skip: usize, count: usize, total_entries: usize) -> Self {
Self {
fetch,
start,
skip,
count,
total_entries,
mode: ViewMode::Normal,
input: String::new(),
list_state: ListState::default(),
entries: BTreeMap::new(),
entries: Vec::new(),
}
}
@ -123,33 +122,33 @@ where
/// Fetch the next page of items
fn next_page(&mut self) {
if self.start + self.count >= self.total_entries {
if self.skip + self.count >= self.total_entries {
return
}
self.start += self.count;
self.skip += self.count;
self.fetch_page();
}
/// Fetch the previous page of items
fn previous_page(&mut self) {
if self.start == 0 {
if self.skip == 0 {
return
}
self.start -= self.count;
self.skip = self.skip.saturating_sub(self.count);
self.fetch_page();
}
/// Go to a specific page.
fn go_to_page(&mut self, page: usize) {
self.start = (self.count * page).min(self.total_entries - self.count);
self.skip = (self.count * page).min(self.total_entries - self.count);
self.fetch_page();
}
/// Fetch the current page
fn fetch_page(&mut self) {
self.entries = (self.fetch)(self.start, self.count);
self.entries = (self.fetch)(self.skip, self.count);
self.reset();
}
@ -189,7 +188,7 @@ fn event_loop<B: Backend, F, T: Table>(
tick_rate: Duration,
) -> io::Result<()>
where
F: FnMut(usize, usize) -> BTreeMap<T::Key, T::Value>,
F: FnMut(usize, usize) -> Vec<(T::Key, T::Value)>,
{
let mut last_tick = Instant::now();
let mut running = true;
@ -217,7 +216,7 @@ where
/// Handle incoming events
fn handle_event<F, T: Table>(app: &mut DbListTUI<F, T>, event: Event) -> io::Result<bool>
where
F: FnMut(usize, usize) -> BTreeMap<T::Key, T::Value>,
F: FnMut(usize, usize) -> Vec<(T::Key, T::Value)>,
{
if app.mode == ViewMode::GoToPage {
if let Event::Key(key) = event {
@ -283,7 +282,7 @@ where
/// Render the UI
fn ui<B: Backend, F, T: Table>(f: &mut Frame<'_, B>, app: &mut DbListTUI<F, T>)
where
F: FnMut(usize, usize) -> BTreeMap<T::Key, T::Value>,
F: FnMut(usize, usize) -> Vec<(T::Key, T::Value)>,
{
let outer_chunks = Layout::default()
.direction(Direction::Vertical)
@ -297,21 +296,23 @@ where
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
.split(outer_chunks[0]);
let key_length = format!("{}", app.start + app.count - 1).len();
let formatted_keys = app
.entries
.keys()
let key_length = format!("{}", app.skip + app.count - 1).len();
let entries: Vec<_> = app.entries.iter().map(|(k, _)| k).collect();
let formatted_keys = entries
.into_iter()
.enumerate()
.map(|(i, k)| {
ListItem::new(format!("[{:0>width$}]: {k:?}", i + app.start, width = key_length))
ListItem::new(format!("[{:0>width$}]: {k:?}", i + app.skip, width = key_length))
})
.collect::<Vec<ListItem<'_>>>();
let key_list = List::new(formatted_keys)
.block(Block::default().borders(Borders::ALL).title(format!(
"Keys (Showing entries {}-{} out of {} entries)",
app.start,
app.start + app.entries.len() - 1,
app.skip,
app.skip + app.entries.len() - 1,
app.total_entries
)))
.style(Style::default().fg(Color::White))
@ -320,7 +321,8 @@ where
.start_corner(Corner::TopLeft);
f.render_stateful_widget(key_list, inner_chunks[0], &mut app.list_state);
let values = app.entries.values().collect::<Vec<_>>();
let values = app.entries.iter().map(|(_, v)| v).collect::<Vec<_>>();
let value_display = Paragraph::new(
app.list_state
.selected()

View File

@ -2,7 +2,7 @@
use eyre::{Result, WrapErr};
use reth_db::{
cursor::{DbCursorRO, Walker},
cursor::DbCursorRO,
database::Database,
table::Table,
transaction::{DbTx, DbTxMut},
@ -12,7 +12,7 @@ use reth_interfaces::p2p::{
priority::Priority,
};
use reth_primitives::{BlockHashOrNumber, HeadersDirection, SealedHeader};
use std::{collections::BTreeMap, path::Path, time::Duration};
use std::{path::Path, time::Duration};
use tracing::info;
/// Get a single header from network
@ -67,22 +67,21 @@ impl<'a, DB: Database> DbTool<'a, DB> {
/// entries into a [`HashMap`][std::collections::HashMap].
pub fn list<T: Table>(
&mut self,
start: usize,
skip: usize,
len: usize,
) -> Result<BTreeMap<T::Key, T::Value>> {
reverse: bool,
) -> Result<Vec<(T::Key, T::Value)>> {
let data = self.db.view(|tx| {
let mut cursor = tx.cursor_read::<T>().expect("Was not able to obtain a cursor.");
// TODO: Upstream this in the DB trait.
let start_walker = cursor.current().transpose();
let walker = Walker::new(&mut cursor, start_walker);
walker.skip(start).take(len).collect::<Vec<_>>()
if reverse {
cursor.walk_back(None)?.skip(skip).take(len).collect::<Result<_, _>>()
} else {
cursor.walk(None)?.skip(skip).take(len).collect::<Result<_, _>>()
}
})?;
data.into_iter()
.collect::<Result<BTreeMap<T::Key, T::Value>, _>>()
.map_err(|e| eyre::eyre!(e))
data.map_err(|e| eyre::eyre!(e))
}
/// Grabs the content of the table for the given key