tegratop 0.2.1

TUI for monitoring Nvidia jetson boards
Documentation
use log::error;
use std::{fs, path::Path};

use anyhow::{Context, Result};

use ratatui::{
    layout::{Constraint, Rect},
    style::{Style, Stylize},
    widgets::{Block, BorderType, Borders, Cell, Padding, Row, Table},
    Frame,
};
use regex::Regex;

#[derive(Debug, Default)]
pub struct Board {
    name: Option<String>,
    l4t: Option<String>,
}

impl Board {
    fn name() -> Result<String> {
        let path = Path::new("/sys/firmware/devicetree/base/model");
        let name =
            fs::read_to_string(path).context(format!("Failed to read from {}", path.display()))?;

        Ok(name)
    }

    fn l4t() -> Result<Option<String>> {
        let path = Path::new("/etc/nv_tegra_release");
        let l4t_buffer =
            fs::read_to_string(path).context(format!("Failed to read from {}", path.display()))?;

        let re = Regex::new(r"R(\d+) \(release\), REVISION: (\d+\.\d+)")?;

        if let Some(captures) = re.captures(l4t_buffer.as_str()) {
            let release = captures.get(1).map_or("", |m| m.as_str());
            let revision = captures.get(2).map_or("", |m| m.as_str());
            return Ok(Some(format!("{}.{}", release, revision)));
        };

        Ok(None)
    }

    pub fn new() -> Self {
        let name = Board::name().map_or_else(
            |e| {
                error!("{}", e);
                None
            },
            Some,
        );

        let l4t = match Board::l4t() {
            Ok(v) => v,
            Err(e) => {
                error!("{}", e);
                None
            }
        };

        Self { name, l4t }
    }

    pub fn render(&self, frame: &mut Frame, block: Rect) {
        let rows: [Row; 2] = [
            Row::new(vec![
                Cell::new("Name").style(Style::default().bold()),
                Cell::new(match &self.name {
                    Some(name) => name,
                    None => " - ",
                }),
            ]),
            Row::new(vec![
                Cell::new("L4t").style(Style::default().bold()),
                Cell::new(match &self.l4t {
                    Some(l4t) => l4t,
                    None => " - ",
                }),
            ]),
        ];

        let widths = [Constraint::Length(6), Constraint::Fill(1)];

        let board = Table::new(rows, widths).block(
            Block::default()
                .borders(Borders::ALL)
                .border_type(BorderType::Rounded)
                .title("Board")
                .padding(Padding::horizontal(1))
                .title_style(Style::new().bold()),
        );
        frame.render_widget(board, block);
    }
}