mod binary_widget;
use crossterm::event::KeyEvent;
use nu_pretty_hex::HexStyles;
use nu_protocol::{
Value,
engine::{EngineState, Stack},
};
use ratatui::layout::Rect;
use super::super::{
config::ExploreConfig,
nu_common::NuText,
pager::{
Frame, Transition, ViewInfo,
report::{Report, Severity},
},
};
use super::cursor::Position;
use self::binary_widget::{BinarySettings, BinaryStyle, BinaryWidget};
use super::{Layout, View, ViewConfig, cursor::CursorMoveHandler, cursor::WindowCursor2D};
#[derive(Debug, Clone)]
pub struct BinaryView {
data: Vec<u8>,
cursor: WindowCursor2D,
settings: Settings,
}
#[derive(Debug, Default, Clone)]
struct Settings {
opts: BinarySettings,
style: BinaryStyle,
}
impl BinaryView {
pub fn new(data: Vec<u8>, cfg: &ExploreConfig) -> Self {
let settings = settings_from_config(cfg);
let count_rows = BinaryWidget::new(&data, settings.opts, Default::default()).count_lines();
Self {
data,
cursor: WindowCursor2D::new(count_rows, 1).expect("Failed to create XYCursor"),
settings,
}
}
}
impl View for BinaryView {
fn draw(&mut self, f: &mut Frame, area: Rect, _cfg: ViewConfig<'_>, _layout: &mut Layout) {
let widget = create_binary_widget(self);
f.render_widget(widget, area);
}
fn handle_input(
&mut self,
_: &EngineState,
_: &mut Stack,
_: &Layout,
info: &mut ViewInfo,
key: KeyEvent,
) -> Transition {
if let Ok((Transition::Ok, ..)) = self.handle_input_key(&key) {
let report = create_report(self.cursor);
info.status = Some(report);
}
Transition::None
}
fn collect_data(&self) -> Vec<NuText> {
vec![]
}
fn show_data(&mut self, _pos: usize) -> bool {
false
}
fn exit(&mut self) -> Option<Value> {
None
}
}
impl CursorMoveHandler for BinaryView {
fn get_cursor(&mut self) -> &mut WindowCursor2D {
&mut self.cursor
}
}
fn create_binary_widget(v: &BinaryView) -> BinaryWidget<'_> {
let start_line = v.cursor.window_origin().row;
let count_elements =
BinaryWidget::new(&[], v.settings.opts, Default::default()).count_elements();
let index = start_line * count_elements;
let data = &v.data[index..];
let mut w = BinaryWidget::new(data, v.settings.opts, v.settings.style.clone());
w.set_row_offset(index);
w
}
fn settings_from_config(config: &ExploreConfig) -> Settings {
Settings {
opts: BinarySettings::new(2, 8),
style: BinaryStyle::new(
None,
config.table.column_padding_left as u16,
config.table.column_padding_right as u16,
HexStyles::default(),
),
}
}
fn create_report(cursor: WindowCursor2D) -> Report {
let covered_percent = report_row_position(cursor);
let cursor = report_cursor_position(cursor);
let mode = report_mode_name();
let msg = String::new();
Report::new(msg, Severity::Info, mode, cursor, covered_percent)
}
fn report_mode_name() -> String {
String::from("VIEW")
}
fn report_row_position(cursor: WindowCursor2D) -> String {
if cursor.window_origin().row == 0 {
return String::from("Top");
}
let row = cursor.row().min(cursor.row_limit());
let count_rows = cursor.row_limit();
let percent_rows = get_percentage(row, count_rows);
match percent_rows {
100 => String::from("All"),
value => format!("{value}%"),
}
}
fn report_cursor_position(cursor: WindowCursor2D) -> String {
let Position { row, column } = cursor.window_origin();
format!("{row},{column}")
}
fn get_percentage(value: usize, max: usize) -> usize {
debug_assert!(value <= max, "{value:?} {max:?}");
((value as f32 / max as f32) * 100.0).floor() as usize
}