1use std::io;
2use std::io::Write;
3
4use crossterm::{
5 cursor::{Hide, MoveTo},
6 queue,
7 terminal::{Clear, ClearType},
8};
9use rand::Rng;
10
11use crate::layout_panel::LayoutPanel;
12use crate::render_context::RenderContext;
13
14pub struct Renderer<W: Write> {
20 pub writer: W,
21 pub rng: Box<dyn Rng>,
22 ctx: RenderContext,
23 panel: LayoutPanel,
24}
25
26impl<W: Write> Renderer<W> {
27 pub fn new(writer: W, rng: Box<dyn Rng>, ctx: RenderContext) -> Self {
28 Self {
29 writer,
30 rng,
31 ctx,
32 panel: LayoutPanel {
33 row: 0,
34 column: 0,
35 width: 0,
36 },
37 }
38 }
39
40 pub fn ctx(&self) -> &RenderContext {
41 &self.ctx
42 }
43
44 pub fn panel(&self) -> LayoutPanel {
45 self.panel
46 }
47
48 #[cfg(not(tarpaulin_include))]
54 pub fn frame<F>(&mut self, ctx: RenderContext, f: F) -> io::Result<()>
55 where
56 F: FnOnce(&mut Self) -> io::Result<()>,
57 {
58 self.ctx = ctx;
59 queue!(self.writer, Hide, Clear(ClearType::All))?;
60 f(self)?;
61 self.writer.flush()
62 }
63
64 #[cfg(not(tarpaulin_include))]
66 pub fn draw(&mut self, component: &impl Component, panel: LayoutPanel) -> io::Result<u16> {
67 self.panel = panel;
68 component.render(self)
69 }
70
71 #[cfg(not(tarpaulin_include))]
76 pub fn with_panel<F>(&mut self, f: F) -> io::Result<u16>
77 where
78 F: FnOnce(&mut W, LayoutPanel, &mut Box<dyn Rng>) -> io::Result<u16>,
79 {
80 let panel = self.panel;
81 queue!(self.writer, MoveTo(panel.column, panel.row))?;
82 f(&mut self.writer, panel, &mut self.rng)
83 }
84}
85
86pub trait Component {
92 fn render<W: Write>(&self, renderer: &mut Renderer<W>) -> io::Result<u16>;
93}
94
95#[cfg(test)]
96mod tests {
97 use crate::test_helpers::test_renderer;
98
99 #[test]
100 fn ctx_returns_terminal_size_and_elapsed() {
101 let renderer = test_renderer();
102 let ctx = renderer.ctx();
103 assert_eq!(ctx.terminal_size.width, 80);
104 assert_eq!(ctx.terminal_size.height, 24);
105 }
106
107 #[test]
108 fn panel_defaults_to_zero() {
109 let renderer = test_renderer();
110 let panel = renderer.panel();
111 assert_eq!(panel.row, 0);
112 assert_eq!(panel.column, 0);
113 assert_eq!(panel.width, 0);
114 }
115}