use assembly_data::fdb::{ core, mem::{self, Database, Field, Table}, query::pk_filter, }; use color_eyre::eyre::{eyre, WrapErr}; use mapr::Mmap; use prettytable::{Cell as PCell, Row as PRow, Table as PTable}; use std::{fs::File, path::PathBuf}; use structopt::StructOpt; #[derive(StructOpt)] /// Shows all rows for a single key in a table struct Options { /// The FDB file file: PathBuf, /// The table to use table: String, /// The key to use key: String, } fn field_to_cell(field: mem::Field) -> PCell { match field { Field::Nothing => PCell::new("NULL"), Field::Integer(v) => PCell::new(&format!("{} (i32)", v)), Field::Float(v) => PCell::new(&format!("{} (f32)", v)), Field::Text(v) => PCell::new(&format!("{:?}", v)), Field::Boolean(v) => PCell::new(if v { "true" } else { "false" }), Field::BigInt(v) => PCell::new(&format!("{} (i64)", v)), Field::VarChar(v) => PCell::new(&format!("{:?}", v)), } } fn main() -> color_eyre::Result<()> { color_eyre::install()?; let opts = Options::from_args(); // Load the database file let file = File::open(&opts.file) .wrap_err_with(|| format!("Failed to open input file '{}'", opts.file.display()))?; let mmap = unsafe { Mmap::map(&file)? }; let buffer: &[u8] = &mmap; // Start using the database let db = Database::new(buffer); // Find table let table = db .tables()? .by_name(&opts.table) .ok_or_else(|| eyre!("Failed to find table {:?}", &opts.table))?; let table: Table = table.wrap_err_with(|| format!("Failed to load table {:?}", &opts.table))?; let index_col = table.column_at(0).expect("Table has no columns"); // Setup key filer let value_type = index_col.value_type(); let filter = pk_filter(opts.key, value_type)?; // Find the bucket let bucket = table .bucket_at(filter.hash() as usize % table.bucket_count()) .ok_or_else(|| eyre!("Failed to find bucket"))?; // Collect relevant rows let mut rows: Vec<_> = bucket .row_iter() .filter(|row| { if let Some(index_field) = row.field_at(0) { match index_field { Field::Integer(v) => filter.filter(&core::Field::Integer(v)), Field::Text(v) => filter.filter(&core::Field::Text(v.decode().into_owned())), _ => false, } } else { false } }) .map(|r| r.field_iter()) .collect(); // Prepare output let mut output = PTable::new(); output.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE); let row_count = rows.len(); let column_count = table.column_count(); if column_count > row_count { let mut first = true; for col in table.column_iter() { let mut out_cells = Vec::with_capacity(row_count + 1); out_cells.push(PCell::new(col.name().as_ref())); for iter in rows.iter_mut() { let field = iter.next().unwrap(); out_cells.push(field_to_cell(field)); } let prow = PRow::new(out_cells); if first { output.set_titles(prow); first = false; } else { output.add_row(prow); } } } else { let mut title_cells = Vec::with_capacity(column_count); for col in table.column_iter() { title_cells.push(PCell::new(col.name().as_ref())); } output.set_titles(PRow::new(title_cells)); for row_iter in rows { let mut out_cells = Vec::with_capacity(column_count); for field in row_iter { out_cells.push(field_to_cell(field)); } output.add_row(PRow::new(out_cells)); } } output.printstd(); println!("Printed {} row(s)", row_count); Ok(()) }