mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-09 19:36:47 +00:00
213 lines
4.9 KiB
Rust
213 lines
4.9 KiB
Rust
use std::error;
|
||
|
||
use rand::{distributions::Uniform, prelude::Distribution, rngs::ThreadRng};
|
||
use ratatui::widgets::ListState;
|
||
|
||
/// Application result type.
|
||
pub type AppResult<T> = std::result::Result<T, Box<dyn error::Error>>;
|
||
|
||
/// Application.
|
||
#[derive(Debug)]
|
||
pub struct App<'a> {
|
||
/// APP的标题
|
||
pub title: &'a str,
|
||
/// Is the application running?
|
||
pub running: bool,
|
||
|
||
pub enhanced_graphics: bool,
|
||
|
||
/// counter
|
||
pub counter: u8,
|
||
|
||
pub tabs: TabsState<'a>,
|
||
|
||
pub memory_log_sparkline: Signal<RandomSignal>,
|
||
|
||
logs: Vec<String>,
|
||
pub stateful_logs: StatefulList<(&'a str, &'a str)>,
|
||
|
||
backend_log_receiver: Option<std::sync::mpsc::Receiver<String>>,
|
||
}
|
||
|
||
impl<'a> App<'a> {
|
||
/// Constructs a new instance of [`App`].
|
||
pub fn new(title: &'a str) -> Self {
|
||
let mut rand_signal = RandomSignal::new(0, 100);
|
||
let sparkline_points = rand_signal.by_ref().take(300).collect();
|
||
let sparkline = Signal {
|
||
source: rand_signal,
|
||
points: sparkline_points,
|
||
tick_rate: 1,
|
||
};
|
||
|
||
Self {
|
||
title,
|
||
running: true,
|
||
enhanced_graphics: true,
|
||
counter: 0,
|
||
tabs: TabsState::new(vec!["Tab0", "Tab1", "Tab2"]),
|
||
memory_log_sparkline: sparkline,
|
||
logs: Vec::new(),
|
||
stateful_logs: StatefulList::with_items(vec![]),
|
||
backend_log_receiver: None,
|
||
}
|
||
}
|
||
|
||
pub fn set_backend_log_receiver(&mut self, receiver: std::sync::mpsc::Receiver<String>) {
|
||
self.backend_log_receiver = Some(receiver);
|
||
}
|
||
|
||
/// Handles the tick event of the terminal.
|
||
pub fn tick(&mut self) {
|
||
self.memory_log_sparkline.on_tick();
|
||
self.handle_logs_on_tick();
|
||
}
|
||
|
||
/// 当到达tick时,处理日志
|
||
fn handle_logs_on_tick(&mut self) {
|
||
let logs_to_push = self
|
||
.backend_log_receiver
|
||
.as_ref()
|
||
.map(|rv| rv.try_iter().collect::<Vec<String>>());
|
||
|
||
if let Some(logs) = logs_to_push {
|
||
for log in logs {
|
||
self.push_log(log);
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Set running to false to quit the application.
|
||
pub fn quit(&mut self) {
|
||
self.running = false;
|
||
}
|
||
|
||
pub fn increment_counter(&mut self) {
|
||
if let Some(res) = self.counter.checked_add(1) {
|
||
self.counter = res;
|
||
}
|
||
}
|
||
|
||
pub fn decrement_counter(&mut self) {
|
||
if let Some(res) = self.counter.checked_sub(1) {
|
||
self.counter = res;
|
||
}
|
||
}
|
||
|
||
pub fn push_log(&mut self, log: String) {
|
||
self.logs.push(log);
|
||
}
|
||
|
||
pub fn logs(&self) -> &Vec<String> {
|
||
&self.logs
|
||
}
|
||
}
|
||
|
||
#[derive(Debug)]
|
||
pub struct TabsState<'a> {
|
||
pub titles: Vec<&'a str>,
|
||
pub index: usize,
|
||
}
|
||
|
||
impl<'a> TabsState<'a> {
|
||
pub fn new(titles: Vec<&'a str>) -> TabsState {
|
||
TabsState { titles, index: 0 }
|
||
}
|
||
pub fn next(&mut self) {
|
||
self.index = (self.index + 1) % self.titles.len();
|
||
}
|
||
|
||
pub fn previous(&mut self) {
|
||
if self.index > 0 {
|
||
self.index -= 1;
|
||
} else {
|
||
self.index = self.titles.len() - 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug)]
|
||
pub struct Signal<S: Iterator> {
|
||
source: S,
|
||
pub points: Vec<S::Item>,
|
||
tick_rate: usize,
|
||
}
|
||
|
||
impl<S> Signal<S>
|
||
where
|
||
S: Iterator,
|
||
{
|
||
fn on_tick(&mut self) {
|
||
for _ in 0..self.tick_rate {
|
||
self.points.remove(0);
|
||
}
|
||
self.points
|
||
.extend(self.source.by_ref().take(self.tick_rate));
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug)]
|
||
pub struct RandomSignal {
|
||
distribution: Uniform<u64>,
|
||
rng: ThreadRng,
|
||
}
|
||
|
||
impl RandomSignal {
|
||
pub fn new(lower: u64, upper: u64) -> RandomSignal {
|
||
RandomSignal {
|
||
distribution: Uniform::new(lower, upper),
|
||
rng: rand::thread_rng(),
|
||
}
|
||
}
|
||
}
|
||
|
||
impl Iterator for RandomSignal {
|
||
type Item = u64;
|
||
fn next(&mut self) -> Option<u64> {
|
||
Some(self.distribution.sample(&mut self.rng))
|
||
}
|
||
}
|
||
|
||
#[derive(Debug)]
|
||
pub struct StatefulList<T> {
|
||
pub state: ListState,
|
||
pub items: Vec<T>,
|
||
}
|
||
|
||
impl<T> StatefulList<T> {
|
||
pub fn with_items(items: Vec<T>) -> StatefulList<T> {
|
||
StatefulList {
|
||
state: ListState::default(),
|
||
items,
|
||
}
|
||
}
|
||
|
||
pub fn next(&mut self) {
|
||
let i = match self.state.selected() {
|
||
Some(i) => {
|
||
if i >= self.items.len() - 1 {
|
||
0
|
||
} else {
|
||
i + 1
|
||
}
|
||
}
|
||
None => 0,
|
||
};
|
||
self.state.select(Some(i));
|
||
}
|
||
|
||
pub fn previous(&mut self) {
|
||
let i = match self.state.selected() {
|
||
Some(i) => {
|
||
if i == 0 {
|
||
self.items.len() - 1
|
||
} else {
|
||
i - 1
|
||
}
|
||
}
|
||
None => 0,
|
||
};
|
||
self.state.select(Some(i));
|
||
}
|
||
}
|