1use std::time::{Duration, Instant};
17
18use color_eyre::Result;
19use rand::{
20 distributions::{Distribution, Uniform},
21 rngs::ThreadRng,
22};
23use ratatui::{
24 crossterm::event::{self, Event, KeyCode},
25 layout::{Constraint, Layout},
26 style::{Color, Style},
27 widgets::{Block, Borders, Sparkline},
28 DefaultTerminal, Frame,
29};
30
31fn main() -> Result<()> {
32 color_eyre::install()?;
33 let terminal = ratatui::init();
34 let app_result = App::new().run(terminal);
35 ratatui::restore();
36 app_result
37}
38
39struct App {
40 signal: RandomSignal,
41 data1: Vec<u64>,
42 data2: Vec<u64>,
43 data3: Vec<u64>,
44}
45
46#[derive(Clone)]
47struct RandomSignal {
48 distribution: Uniform<u64>,
49 rng: ThreadRng,
50}
51
52impl RandomSignal {
53 fn new(lower: u64, upper: u64) -> Self {
54 Self {
55 distribution: Uniform::new(lower, upper),
56 rng: rand::thread_rng(),
57 }
58 }
59}
60
61impl Iterator for RandomSignal {
62 type Item = u64;
63 fn next(&mut self) -> Option<u64> {
64 Some(self.distribution.sample(&mut self.rng))
65 }
66}
67
68impl App {
69 fn new() -> Self {
70 let mut signal = RandomSignal::new(0, 100);
71 let data1 = signal.by_ref().take(200).collect::<Vec<u64>>();
72 let data2 = signal.by_ref().take(200).collect::<Vec<u64>>();
73 let data3 = signal.by_ref().take(200).collect::<Vec<u64>>();
74 Self {
75 signal,
76 data1,
77 data2,
78 data3,
79 }
80 }
81
82 fn on_tick(&mut self) {
83 let value = self.signal.next().unwrap();
84 self.data1.pop();
85 self.data1.insert(0, value);
86 let value = self.signal.next().unwrap();
87 self.data2.pop();
88 self.data2.insert(0, value);
89 let value = self.signal.next().unwrap();
90 self.data3.pop();
91 self.data3.insert(0, value);
92 }
93
94 fn run(mut self, mut terminal: DefaultTerminal) -> Result<()> {
95 let tick_rate = Duration::from_millis(250);
96
97 let mut last_tick = Instant::now();
98 loop {
99 terminal.draw(|frame| self.draw(frame))?;
100
101 let timeout = tick_rate.saturating_sub(last_tick.elapsed());
102 if event::poll(timeout)? {
103 if let Event::Key(key) = event::read()? {
104 if key.code == KeyCode::Char('q') {
105 return Ok(());
106 }
107 }
108 }
109 if last_tick.elapsed() >= tick_rate {
110 self.on_tick();
111 last_tick = Instant::now();
112 }
113 }
114 }
115
116 fn draw(&self, frame: &mut Frame) {
117 let chunks = Layout::vertical([
118 Constraint::Length(3),
119 Constraint::Length(3),
120 Constraint::Min(0),
121 ])
122 .split(frame.area());
123 let sparkline = Sparkline::default()
124 .block(
125 Block::new()
126 .borders(Borders::LEFT | Borders::RIGHT)
127 .title("Data1"),
128 )
129 .data(&self.data1)
130 .style(Style::default().fg(Color::Yellow));
131 frame.render_widget(sparkline, chunks[0]);
132 let sparkline = Sparkline::default()
133 .block(
134 Block::new()
135 .borders(Borders::LEFT | Borders::RIGHT)
136 .title("Data2"),
137 )
138 .data(&self.data2)
139 .style(Style::default().bg(Color::Green));
140 frame.render_widget(sparkline, chunks[1]);
141 let sparkline = Sparkline::default()
143 .block(
144 Block::new()
145 .borders(Borders::LEFT | Borders::RIGHT)
146 .title("Data3"),
147 )
148 .data(&self.data3)
149 .style(Style::default().fg(Color::Red));
150 frame.render_widget(sparkline, chunks[2]);
151 }
152}