deja-vu/boot_diags/src/components/progress.rs

157 lines
4.9 KiB
Rust

// 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 <https://www.gnu.org/licenses/>.
//
use color_eyre::Result;
use ratatui::{
Frame,
layout::{Alignment, Rect},
style::{Color, Style, Stylize},
text::{Line, Span},
widgets::{Block, Borders, Clear, Paragraph},
};
use tracing::info;
use core::{
action::{Action, DiagResult},
components::Component,
state::Mode,
};
#[derive(Debug, Clone)]
struct ProgressLine {
name: String,
text: String,
result: DiagResult,
running: bool,
}
impl ProgressLine {
pub fn len_name(&self) -> usize {
self.name.chars().count()
}
}
#[derive(Default, Debug, Clone)]
pub struct Progress {
// command_tx: Option<UnboundedSender<Action>>,
// config: Config,
lines: Vec<ProgressLine>,
mode: Mode,
}
impl Progress {
#[must_use]
pub fn new() -> Self {
Self::default()
}
// fn register_action_handler(&mut self, tx: UnboundedSender<Action>) -> Result<()> {
// self.command_tx = Some(tx);
// Ok(())
// }
// fn register_config_handler(&mut self, config: Config) -> Result<()> {
// self.config = config;
// Ok(())
// }
}
impl Component for Progress {
fn update(&mut self, action: Action) -> Result<Option<Action>> {
match action {
Action::DiagLineStart { text } => {
info!("Caught Action::DiagLineStart {{ \"{}\" }}", &text);
self.lines.push(ProgressLine {
name: text,
text: String::from("OK"),
result: DiagResult::Pass,
running: true,
});
}
Action::DiagLineUpdate { result, text } => {
info!(
"Caught Action::DiagLineUpdate {{ {}, \"{}\" }}",
&result, &text
);
if let Some(line) = self.lines.last_mut() {
line.result = result;
line.text = text;
}
}
Action::DiagLineEnd { text } => {
info!("Caught Action::DiagLineEnd {{ \"{}\" }}", &text);
if let Some(line) = self.lines.last_mut() {
line.running = false;
}
}
Action::SetMode(mode) => {
self.mode = mode;
self.lines.clear();
}
_ => {}
};
Ok(None)
}
fn draw(&mut self, frame: &mut Frame, area: Rect) -> Result<()> {
if self.mode != Mode::BootScan {
return Ok(());
}
let mut body_text = Vec::with_capacity(20); // TODO: Needs fine tuning?
body_text.push(Line::default());
// Add line(s)
self.lines.iter().for_each(|line| {
//.for_each(|(part, color)| spans.push(Span::styled(part, Style::default().fg(color))));
//Line::from(spans)
let color = match line.result {
DiagResult::Pass => Color::Green,
DiagResult::Fail => Color::Red,
DiagResult::Warn => Color::Yellow,
};
let text = if line.running || line.text.is_empty() {
String::from("...")
} else {
line.text.clone()
};
let text = format!(" [ {text} ] ");
let width = area.width as usize - line.len_name() - 1 - text.chars().count() - 7; // ' [ ]'
let spans = vec![
Span::raw(format!("{} {:.<width$}", &line.name, "")),
Span::styled(text, Style::default().fg(color)),
];
body_text.push(Line::from(spans).centered());
body_text.push(Line::default());
body_text.push(Line::default());
});
// Build block
let outer_block = Block::default()
.borders(Borders::ALL)
.style(Style::default().bold());
let inner_block = Block::default()
.borders(Borders::NONE)
.style(Style::default().bold())
.title("Progress")
.title_alignment(Alignment::Center);
let inner = outer_block.inner(area);
let body = Paragraph::new(body_text).block(inner_block);
frame.render_widget(Clear, area);
frame.render_widget(outer_block, area);
frame.render_widget(body, inner);
Ok(())
}
}