pub mod input;
pub mod search;
pub mod selection;
pub mod types;
pub use types::{
DecoderInfo, DecoderParam, DecoderSource, InputMode, ParamType, SelectionMode,
};
use crate::decode::{DecodedValue, Endian};
use crate::file_buffer::FileBuffer;
pub const DEFAULT_BYTES_PER_ROW: usize = 16;
pub struct App {
pub buffer: FileBuffer,
pub filename: String,
pub cursor: usize,
pub selection_anchor: Option<usize>,
pub selection_end: Option<usize>,
pub scroll_offset: usize,
pub visible_rows: usize,
pub endian: Endian,
pub mode: SelectionMode,
pub input_mode: InputMode,
pub goto_input: String,
pub quit: bool,
pub bit_cursor: usize,
pub bit_selection_anchor: Option<usize>,
pub bit_selection_end: Option<usize>,
pub bytes_per_row: usize,
pub decode_panel_pct: u16,
pub decode_entries: Vec<DecodedValue>,
pub decode_focus: Option<usize>,
pub decode_scroll_offset: usize,
pub hex_area: Option<ratatui::layout::Rect>,
pub decode_area: Option<ratatui::layout::Rect>,
pub stats_area: Option<ratatui::layout::Rect>,
pub decoders: Vec<DecoderInfo>,
pub decoder_settings_cursor: usize,
pub param_edit_input: String,
pub show_stats_panel: bool,
pub stats_scroll_offset: usize,
pub count_prefix: Option<usize>,
pub search_input: String,
pub search_pattern: Option<Vec<u8>>,
pub search_was_hex: bool,
}
impl App {
pub fn new(buffer: FileBuffer, filename: String) -> Self {
Self {
buffer,
filename,
cursor: 0,
selection_anchor: None,
selection_end: None,
scroll_offset: 0,
visible_rows: 20,
endian: Endian::Little,
mode: SelectionMode::Byte,
input_mode: InputMode::Normal,
goto_input: String::new(),
quit: false,
bit_cursor: 0,
bit_selection_anchor: None,
bit_selection_end: None,
bytes_per_row: DEFAULT_BYTES_PER_ROW,
decode_panel_pct: 50,
decode_entries: Vec::new(),
decode_focus: None,
decode_scroll_offset: 0,
hex_area: None,
decode_area: None,
stats_area: None,
decoders: Vec::new(),
decoder_settings_cursor: 0,
param_edit_input: String::new(),
show_stats_panel: false,
stats_scroll_offset: 0,
count_prefix: None,
search_input: String::new(),
search_pattern: None,
search_was_hex: false,
}
}
pub fn register_decoder(
&mut self,
name: String,
source: DecoderSource,
params: Vec<DecoderParam>,
) {
self.decoders.push(DecoderInfo {
name,
source,
enabled: true,
params,
});
}
pub fn decoder_params(&self, name: &str, source: &DecoderSource) -> Vec<(String, String)> {
self.decoders
.iter()
.find(|d| d.name == name && d.source == *source)
.map(|d| {
d.params
.iter()
.map(|p| (p.name.clone(), p.value.clone()))
.collect()
})
.unwrap_or_default()
}
pub fn settings_row_count(&self) -> usize {
self.decoders.iter().map(|d| 1 + d.params.len()).sum()
}
pub fn settings_cursor_target(&self) -> Option<(usize, Option<usize>)> {
let mut pos = 0;
for (di, decoder) in self.decoders.iter().enumerate() {
if self.decoder_settings_cursor == pos {
return Some((di, None));
}
pos += 1;
for pi in 0..decoder.params.len() {
if self.decoder_settings_cursor == pos {
return Some((di, Some(pi)));
}
pos += 1;
}
}
None
}
pub fn is_decoder_enabled(&self, name: &str, source: &DecoderSource) -> bool {
self.decoders
.iter()
.find(|d| d.name == name && d.source == *source)
.map(|d| d.enabled)
.unwrap_or(true)
}
pub fn is_builtin_enabled(&self) -> bool {
self.decoders
.iter()
.find(|d| d.source == DecoderSource::Builtin)
.map(|d| d.enabled)
.unwrap_or(true)
}
pub fn file_len(&self) -> usize {
self.buffer.len()
}
pub fn total_rows(&self) -> usize {
(self.file_len() + self.bytes_per_row - 1) / self.bytes_per_row
}
}