// This file is part of Deja-Vu. // // Deja-Vu is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Deja-Vu is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. // See the GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with Deja-Vu. If not, see . // use color_eyre::Result; use ratatui::{ Frame, layout::Rect, widgets::{Block, Clear, Padding, Paragraph, Wrap}, }; use tokio::sync::mpsc::UnboundedSender; use core::{ action::Action, components::{Component, state::StatefulList}, config::Config, state::Mode, }; use std::{ cmp::min, sync::{Arc, Mutex}, }; use crate::diags::DiagGroup; #[derive(Default, Debug, Clone)] pub struct LogView { command_tx: Option>, config: Config, line_index: u16, list: StatefulList, mode: Mode, diag_groups: Arc>>, } impl LogView { #[must_use] pub fn new(diag_groups: Arc>>) -> Self { LogView { diag_groups, ..Default::default() } } } impl Component for LogView { fn register_action_handler(&mut self, tx: UnboundedSender) -> Result<()> { self.command_tx = Some(tx); Ok(()) } fn register_config_handler(&mut self, config: Config) -> Result<()> { self.config = config; Ok(()) } fn update(&mut self, action: Action) -> Result> { match action { Action::KeyUp => { if self.mode == Mode::LogView { if self.line_index > 0 { self.line_index = self.line_index - 1; } } else { self.list.previous(); } } Action::KeyDown => { if self.mode == Mode::LogView { let new_index = self.line_index + 1; if let Some(log_text) = self.list.get_selected() { let lines: Vec<&str> = log_text.split('\n').collect(); if new_index as usize > lines.len() { self.line_index = lines.len() as u16; } else { self.line_index = new_index; } } } else { self.list.next(); } } Action::KeyPageUp => { if self.mode == Mode::LogView { if self.line_index > 10 { self.line_index -= 10; } else { self.line_index = 0; } } } Action::KeyPageDown => { if self.mode == Mode::LogView { let new_index = self.line_index + 10; if let Some(log_text) = self.list.get_selected() { let lines: Vec<&str> = log_text.split('\n').collect(); let new_index: u16 = min((lines.len() - 3) as u16, new_index); self.line_index = new_index; } } } Action::KeyHome => { if self.mode == Mode::LogView { self.line_index = 0; } } Action::KeyEnd => { if self.mode == Mode::LogView { if let Some(log_text) = self.list.get_selected() { let lines: Vec<&str> = log_text.split('\n').collect(); self.line_index = (lines.len() - 3) as u16; } } } Action::Process => { if self.mode == Mode::LogView { if let Some(command_tx) = self.command_tx.clone() { command_tx.send(Action::SetMode(Mode::BootDiags))?; } } } Action::SetMode(new_mode) => { self.line_index = 0; self.mode = new_mode; if self.mode == Mode::BootDiags { self.list.clear_items(); if let Ok(diag_groups) = self.diag_groups.lock() { let raw_logs = diag_groups .iter() .map(|group| group.get_logs_raw()) .collect(); self.list.set_items(raw_logs); } } } _ => {} }; Ok(None) } fn draw(&mut self, frame: &mut Frame, rect: Rect) -> Result<()> { if self.mode != Mode::LogView { return Ok(()); } if let Some(log_text) = self.list.get_selected() { let paragraph = Paragraph::new(log_text) .wrap(Wrap { trim: true }) .scroll((self.line_index, 0)) .block(Block::bordered().padding(Padding::horizontal(1))); frame.render_widget(Clear, rect); frame.render_widget(paragraph, rect); } Ok(()) } }