gotl 0.1.0

Parse and view Go test logs
Documentation
use ratatui::{
    Frame,
    crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers},
    layout::Rect,
    style::Style,
    widgets::{Clear, List, ListState},
};
use serde_json::Value;

use crate::{DetailsScreen, PostEventAction};

pub struct LogScreen {
    log: Vec<String>,
    list_state: ListState,
    area: Option<Rect>,
}

impl LogScreen {
    pub fn new(log: Vec<String>) -> Self {
        Self {
            log,
            list_state: ListState::default().with_selected(Some(0)),
            area: None,
        }
    }

    pub fn handle_event(&mut self, event: Event) -> Option<PostEventAction> {
        match event {
            Event::Key(KeyEvent {
                code: KeyCode::Esc | KeyCode::Char('q'),
                kind: KeyEventKind::Press,
                ..
            }) => Some(PostEventAction::PopScreen),
            Event::Key(KeyEvent {
                code: KeyCode::Down | KeyCode::Char('j'),
                kind: KeyEventKind::Press | KeyEventKind::Repeat,
                ..
            }) => {
                self.list_state.select_next();
                None
            }
            Event::Key(KeyEvent {
                code: KeyCode::Up | KeyCode::Char('k'),
                kind: KeyEventKind::Press | KeyEventKind::Repeat,
                ..
            }) => {
                self.list_state.select_previous();
                None
            }
            Event::Key(KeyEvent {
                code: KeyCode::Home | KeyCode::Char('g'),
                kind: KeyEventKind::Press,
                ..
            }) => {
                self.list_state.select_first();
                None
            }
            Event::Key(KeyEvent {
                code: KeyCode::End | KeyCode::Char('G'),
                kind: KeyEventKind::Press,
                ..
            }) => {
                self.list_state.select_last();
                None
            }
            Event::Key(
                KeyEvent {
                    code: KeyCode::Char('f'),
                    modifiers: KeyModifiers::CONTROL,
                    kind: KeyEventKind::Press | KeyEventKind::Repeat,
                    ..
                }
                | KeyEvent {
                    code: KeyCode::PageDown,
                    kind: KeyEventKind::Press | KeyEventKind::Repeat,
                    ..
                },
            ) => {
                if let Some(area) = self.area
                    && let Some(selected) = self.list_state.selected_mut() {
                        *selected = selected.saturating_add(area.height as usize);
                    }
                None
            }
            Event::Key(KeyEvent {
                code: KeyCode::Char('d'),
                modifiers: KeyModifiers::CONTROL,
                kind: KeyEventKind::Press | KeyEventKind::Repeat,
                ..
            }) => {
                if let Some(area) = self.area
                    && let Some(selected) = self.list_state.selected_mut() {
                        *selected = selected.saturating_add(area.height as usize / 2);
                    }
                None
            }
            Event::Key(
                KeyEvent {
                    code: KeyCode::Char('b'),
                    modifiers: KeyModifiers::CONTROL,
                    kind: KeyEventKind::Press | KeyEventKind::Repeat,
                    ..
                }
                | KeyEvent {
                    code: KeyCode::PageUp,
                    kind: KeyEventKind::Press | KeyEventKind::Repeat,
                    ..
                },
            ) => {
                if let Some(area) = self.area
                    && let Some(selected) = self.list_state.selected_mut() {
                        *selected = selected.saturating_sub(area.height as usize);
                    }
                None
            }
            Event::Key(KeyEvent {
                code: KeyCode::Char('u'),
                modifiers: KeyModifiers::CONTROL,
                kind: KeyEventKind::Press | KeyEventKind::Repeat,
                ..
            }) => {
                if let Some(area) = self.area
                    && let Some(selected) = self.list_state.selected_mut() {
                        *selected = selected.saturating_sub(area.height as usize / 2);
                    }
                None
            }
            Event::Key(KeyEvent {
                code: KeyCode::Enter,
                kind: KeyEventKind::Press,
                ..
            }) => {
                let selected_line = &self.log[self.list_state.selected().unwrap()];
                let value = match serde_json::from_str(selected_line) {
                    Ok(value) => value,
                    Err(_) => Value::String(selected_line.clone()),
                };
                Some(PostEventAction::PushScreen(
                    DetailsScreen::new(value).into(),
                ))
            }
            _ => None,
        }
    }

    pub fn render(&mut self, frame: &mut Frame) {
        self.area = Some(frame.area());

        let list = List::new(self.log.clone()).highlight_style(Style::default().reversed());
        frame.render_widget(Clear, frame.area());
        frame.render_stateful_widget(list, frame.area(), &mut self.list_state);
    }
}