gotl 0.1.0

Parse and view Go test logs
Documentation
use ratatui::{
    Frame,
    crossterm::event::{Event, KeyCode, KeyEvent, KeyEventKind},
    layout::{Constraint, Flex},
    style::Style,
    widgets::{Clear, List, ListState, Paragraph, Row, Table, TableState, Wrap},
};
use serde_json::Value;

use crate::PostEventAction;

pub struct DetailsScreen {
    value: Value,
    list_state: ListState,
    table_state: TableState,
}

impl DetailsScreen {
    pub fn new(value: Value) -> Self {
        Self {
            value,
            list_state: ListState::default().with_selected(Some(0)),
            table_state: TableState::new().with_selected_cell(Some((0, 1))),
        }
    }

    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,
                ..
            }) => {
                if self.value.is_array() {
                    self.list_state.select_next();
                } else if self.value.is_object() {
                    self.table_state.select_next();
                }
                None
            }
            Event::Key(KeyEvent {
                code: KeyCode::Up | KeyCode::Char('k'),
                kind: KeyEventKind::Press | KeyEventKind::Repeat,
                ..
            }) => {
                if self.value.is_array() {
                    self.list_state.select_previous();
                } else if self.value.is_object() {
                    self.table_state.select_previous();
                }
                None
            }
            Event::Key(KeyEvent {
                code: KeyCode::Enter,
                kind: KeyEventKind::Press,
                ..
            }) => match &self.value {
                Value::Array(values) => Some(PostEventAction::PushScreen(
                    DetailsScreen::new(values[self.list_state.selected().unwrap()].clone()).into(),
                )),
                Value::Object(map) => Some(PostEventAction::PushScreen(
                    DetailsScreen::new(
                        map.values()
                            .nth(self.table_state.selected().unwrap())
                            .cloned()
                            .unwrap(),
                    )
                    .into(),
                )),
                _ => None,
            },
            _ => None,
        }
    }

    pub fn render(&mut self, frame: &mut Frame) {
        frame.render_widget(Clear, frame.area());
        match &self.value {
            Value::Null => frame.render_widget("null", frame.area()),
            Value::Bool(value) => frame.render_widget(value.to_string(), frame.area()),
            Value::Number(number) => frame.render_widget(number.to_string(), frame.area()),
            Value::String(text) => frame.render_widget(
                Paragraph::new(text.clone()).wrap(Wrap { trim: false }),
                frame.area(),
            ),
            Value::Array(values) => {
                let list = List::new(values.iter().map(|value| value.to_string()))
                    .highlight_style(Style::new().reversed());
                frame.render_stateful_widget(list, frame.area(), &mut self.list_state);
            }
            Value::Object(map) => {
                let rows = map
                    .iter()
                    .map(|(key, value)| Row::new([key.clone(), value.to_string()]));
                let widest_key_length = map.keys().fold(0, |acc, value| acc.max(value.len()));
                let table = Table::new(
                    rows,
                    [
                        Constraint::Length(widest_key_length.min(30) as u16),
                        Constraint::Percentage(100),
                    ],
                )
                .flex(Flex::Start)
                .cell_highlight_style(Style::new().reversed());
                frame.render_stateful_widget(table, frame.area(), &mut self.table_state);
            }
        }
    }
}