Skip to main content

pito_tui/
headless.rs

1use std::time::{Duration, Instant};
2
3use crossterm::event::KeyEvent;
4use ratatui::{Terminal, backend::TestBackend, buffer::Buffer};
5
6use crate::pace::FRAME;
7use crate::shell::{Flow, Tui};
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub struct Bench {
12    pub screen: String,
13    pub frames: usize,
14    pub average: Duration,
15    pub worst: Duration,
16}
17
18fn terminal(width: u16, height: u16) -> Terminal<TestBackend> {
19    match Terminal::new(TestBackend::new(width, height)) {
20        Ok(terminal) => terminal,
21        Err(never) => match never {},
22    }
23}
24
25impl<E: Send + 'static> Tui<E> {
26    pub fn frame(&mut self, width: u16, height: u16) -> Buffer {
27        let mut terminal = terminal(width, height);
28        if let Err(never) = terminal.draw(|frame| self.draw(frame)) {
29            match never {}
30        }
31        terminal.backend().buffer().clone()
32    }
33
34    pub fn shot(&mut self, width: u16, height: u16, keys: &[KeyEvent], settle: bool) -> Buffer {
35        self.start();
36        self.ready(settle);
37        for key in keys {
38            self.frame(width, height);
39            if self.key(*key) == Flow::Quit {
40                break;
41            }
42            self.ready(settle);
43        }
44        self.frame(width, height)
45    }
46
47    fn ready(&mut self, settle: bool) {
48        if settle {
49            self.settle();
50        } else {
51            drop(self.reads());
52        }
53    }
54
55    pub fn bench(&mut self, width: u16, height: u16, frames: usize) -> Vec<Bench> {
56        let names: Vec<String> = self.names().into_iter().map(str::to_string).collect();
57        let mut terminal = terminal(width, height);
58        let start = self.moment;
59        let mut out = Vec::with_capacity(names.len());
60        for (index, screen) in names.into_iter().enumerate() {
61            if !self.go(index) {
62                continue;
63            }
64            self.settle();
65            let mut total = Duration::ZERO;
66            let mut worst = Duration::ZERO;
67            for frame in 0..frames {
68                self.moment = start + FRAME * u32::try_from(frame).unwrap_or(u32::MAX);
69                let began = Instant::now();
70                if let Err(never) = terminal.draw(|frame| self.draw(frame)) {
71                    match never {}
72                }
73                let spent = began.elapsed();
74                total += spent;
75                worst = worst.max(spent);
76            }
77            self.moment = start;
78            out.push(Bench {
79                screen,
80                frames,
81                average: total / u32::try_from(frames.max(1)).unwrap_or(u32::MAX),
82                worst,
83            });
84        }
85        self.open = None;
86        out
87    }
88}