1use std::sync::Arc;
2
3use crossbeam_skiplist::SkipMap;
4use tokio::sync::Mutex;
5
6use crate::{grapheme::StyledGraphemes, terminal::Terminal};
7
8pub type SharedRenderer<K> = Arc<Renderer<K>>;
10
11pub struct Renderer<K: Ord + Send + 'static> {
13 terminal: Mutex<Terminal>,
14 graphemes: SkipMap<K, StyledGraphemes>,
15}
16
17impl<K: Ord + Send + 'static> Renderer<K> {
18 pub fn try_new() -> anyhow::Result<Self> {
19 Ok(Self {
20 terminal: Mutex::new(Terminal {
21 position: crossterm::cursor::position()?,
22 }),
23 graphemes: SkipMap::new(),
24 })
25 }
26
27 pub async fn try_new_with_graphemes<I>(init: I, draw: bool) -> anyhow::Result<Self>
28 where
29 I: IntoIterator<Item = (K, StyledGraphemes)>,
30 {
31 let renderer = Self::try_new()?;
32 renderer.update(init);
33 if draw {
34 renderer.render().await?;
35 }
36 Ok(renderer)
37 }
38
39 pub fn update<I>(&self, items: I) -> &Self
40 where
41 I: IntoIterator<Item = (K, StyledGraphemes)>,
42 {
43 items.into_iter().for_each(|(index, graphemes)| {
44 self.graphemes.insert(index, graphemes);
45 });
46 self
47 }
48
49 pub fn remove<I>(&self, items: I) -> &Self
50 where
51 I: IntoIterator<Item = K>,
52 {
53 items.into_iter().for_each(|index| {
54 self.graphemes.remove(&index);
55 });
56 self
57 }
58
59 pub async fn render(&self) -> anyhow::Result<()> {
61 let graphemes: Vec<StyledGraphemes> = self
62 .graphemes
63 .iter()
64 .map(|entry| entry.value().clone())
65 .collect();
66 let mut terminal = self.terminal.lock().await;
67 terminal.draw(&graphemes)
68 }
69}