Skip to main content

s3wf2/emitter/
console.rs

1use crate::{
2    document::{
3        Block, BlockNode, CharacterSet, CharacterType, Document, Element, ElementNode, LineType,
4    },
5    emitter::Emit,
6};
7use ansi_term::{Colour, Style};
8use lazy_static::lazy_static;
9use std::io::{prelude::*, Result};
10
11#[derive(PartialEq)]
12enum StackStyle {
13    Bold,
14    Italic,
15    Underlined,
16    Striked,
17    Blink,
18}
19
20#[derive(PartialEq)]
21enum AbstractStyle {
22    Style(StackStyle),
23    Color(Colour),
24}
25
26enum NewlineState {
27    InUse,
28    Ready,
29    NewParagraph,
30}
31
32/// Emitter for virtual terminal.
33pub struct ConsoleEmitter {
34    style_stack: Vec<AbstractStyle>,
35    newline_state: NewlineState,
36}
37
38lazy_static! {
39    static ref MALE_COLORS: Vec<Colour> = vec![
40        Colour::Fixed(26),
41        Colour::Fixed(80),
42        Colour::Fixed(74),
43        Colour::Fixed(62)
44    ];
45    static ref FEMALE_COLORS: Vec<Colour> = vec![
46        Colour::Fixed(170),
47        Colour::Fixed(179),
48        Colour::Fixed(209),
49        Colour::Fixed(229)
50    ];
51    static ref MOB_COLORS: Vec<Colour> = vec![
52        Colour::Fixed(195),
53        Colour::Fixed(230),
54        Colour::Fixed(153),
55        Colour::Fixed(158)
56    ];
57}
58
59impl ConsoleEmitter {
60    pub fn new() -> ConsoleEmitter {
61        ConsoleEmitter {
62            style_stack: vec![],
63            newline_state: NewlineState::NewParagraph,
64        }
65    }
66
67    fn current_style(&self) -> ansi_term::Style {
68        let mut color = Colour::White;
69        let mut styles = vec![];
70        for abst in &self.style_stack {
71            match abst {
72                AbstractStyle::Style(s) => {
73                    if !styles.contains(&s) {
74                        styles.push(s);
75                    }
76                }
77                AbstractStyle::Color(c) => {
78                    color = *c;
79                }
80            }
81        }
82
83        styles.iter().fold(Style::from(color), |r, s| match s {
84            StackStyle::Bold => r.bold(),
85            StackStyle::Italic => r.italic(),
86            StackStyle::Underlined => r.underline(),
87            StackStyle::Striked => r.strikethrough(),
88            StackStyle::Blink => r.blink(),
89        })
90    }
91
92    fn get_color<'c>(&self, character: Option<&'c CharacterType>) -> (Colour, &'c str) {
93        match character {
94            Some(CharacterType::Male(i, n)) => (MALE_COLORS[i % MALE_COLORS.len()], n),
95            Some(CharacterType::Female(i, n)) => (FEMALE_COLORS[i % FEMALE_COLORS.len()], n),
96            Some(CharacterType::Mob(i, n)) => (MOB_COLORS[i % MOB_COLORS.len()], n),
97            Some(CharacterType::Custom(cc, n)) => {
98                let (r, g, b) = if cc.len() == 3 {
99                    let r = u8::from_str_radix(&cc[0..1], 16).unwrap_or(7) * 17;
100                    let g = u8::from_str_radix(&cc[1..2], 16).unwrap_or(7) * 17;
101                    let b = u8::from_str_radix(&cc[2..3], 16).unwrap_or(7) * 17;
102                    (r, g, b)
103                } else {
104                    let r = u8::from_str_radix(&cc[0..2], 16).unwrap_or(127);
105                    let g = u8::from_str_radix(&cc[2..4], 16).unwrap_or(127);
106                    let b = u8::from_str_radix(&cc[4..6], 16).unwrap_or(127);
107                    (r, g, b)
108                };
109                (Colour::RGB(r, g, b), n)
110            }
111            None => (Colour::Fixed(249), "[Undefined]"),
112        }
113    }
114
115    fn confirm_paragraph(&mut self, writer: &mut impl Write) -> Result<()> {
116        match self.newline_state {
117            NewlineState::InUse => writeln!(writer, "\n")?,
118            NewlineState::Ready => writeln!(writer)?,
119            NewlineState::NewParagraph => (),
120        }
121        self.newline_state = NewlineState::NewParagraph;
122        Ok(())
123    }
124
125    fn confirm_newline(&mut self, writer: &mut impl Write) -> Result<()> {
126        match self.newline_state {
127            NewlineState::InUse => writeln!(writer)?,
128            NewlineState::Ready => (),
129            NewlineState::NewParagraph => (),
130        }
131        self.newline_state = NewlineState::Ready;
132        Ok(())
133    }
134
135    fn emit_block(
136        &mut self,
137        writer: &mut impl Write,
138        characters: &CharacterSet,
139        block: &BlockNode,
140    ) -> Result<()> {
141        match block.kind {
142            Block::Section => {
143                self.confirm_paragraph(writer)?;
144                self.style_stack.push(AbstractStyle::Color(Colour::Yellow));
145                self.emit_element(writer, characters, &ElementNode::Text("###### "))?;
146                self.emit_elements(writer, characters, &block.children)?;
147                self.emit_element(writer, characters, &ElementNode::Text(" ######"))?;
148                self.style_stack.pop();
149
150                self.confirm_paragraph(writer)
151            }
152            Block::Subsection => {
153                self.confirm_paragraph(writer)?;
154                self.style_stack.push(AbstractStyle::Color(Colour::Yellow));
155                self.emit_element(writer, characters, &ElementNode::Text("====== "))?;
156                self.emit_elements(writer, characters, &block.children)?;
157                self.emit_element(writer, characters, &ElementNode::Text(" ======"))?;
158                self.style_stack.pop();
159
160                self.confirm_paragraph(writer)
161            }
162            Block::Horizontal => {
163                self.confirm_paragraph(writer)?;
164                writeln!(writer, "--------------------------------------------------------------------------------")?;
165                self.newline_state = NewlineState::Ready;
166                self.confirm_paragraph(writer)
167            }
168            Block::Paragraph => {
169                self.confirm_paragraph(writer)?;
170                self.emit_elements(writer, characters, &block.children)?;
171                self.confirm_paragraph(writer)
172            }
173            Block::Quotation => {
174                self.confirm_paragraph(writer)?;
175                self.confirm_newline(writer)?;
176                self.style_stack
177                    .push(AbstractStyle::Color(Colour::RGB(127, 127, 127)));
178                self.emit_elements(writer, characters, &block.children)?;
179                self.style_stack.pop();
180                self.confirm_paragraph(writer)
181            }
182            Block::UnorderedList => {
183                self.confirm_paragraph(writer)?;
184                self.emit_elements(writer, characters, &block.children)?;
185                self.confirm_paragraph(writer)
186            }
187        }
188    }
189
190    fn emit_elements(
191        &mut self,
192        writer: &mut impl Write,
193        characters: &CharacterSet,
194        elements: &[ElementNode],
195    ) -> Result<()> {
196        for element in elements {
197            self.emit_element(writer, characters, element)?;
198        }
199        Ok(())
200    }
201
202    fn emit_element(
203        &mut self,
204        writer: &mut impl Write,
205        characters: &CharacterSet,
206        element: &ElementNode,
207    ) -> Result<()> {
208        match element {
209            ElementNode::Text(text) => {
210                self.newline_state = NewlineState::InUse;
211                write!(writer, "{}", self.current_style().paint(*text))
212            }
213            ElementNode::Surrounded {
214                kind,
215                children,
216                parameters,
217            } => match kind {
218                Element::Bold => {
219                    self.style_stack
220                        .push(AbstractStyle::Style(StackStyle::Bold));
221                    self.emit_elements(writer, characters, children)?;
222                    self.style_stack.pop();
223                    Ok(())
224                }
225                Element::Italic => {
226                    self.style_stack
227                        .push(AbstractStyle::Style(StackStyle::Italic));
228                    self.emit_elements(writer, characters, children)?;
229                    self.style_stack.pop();
230                    Ok(())
231                }
232                Element::Underlined => {
233                    self.style_stack
234                        .push(AbstractStyle::Style(StackStyle::Underlined));
235                    self.emit_elements(writer, characters, children)?;
236                    self.style_stack.pop();
237                    Ok(())
238                }
239                Element::Deleted => {
240                    self.style_stack
241                        .push(AbstractStyle::Style(StackStyle::Striked));
242                    self.emit_elements(writer, characters, children)?;
243                    self.style_stack.pop();
244                    Ok(())
245                }
246                Element::Dotted => {
247                    self.style_stack
248                        .push(AbstractStyle::Style(StackStyle::Blink));
249                    self.emit_elements(writer, characters, children)?;
250                    self.style_stack.pop();
251                    Ok(())
252                }
253                Element::Ruby | Element::Link => {
254                    self.emit_elements(writer, characters, children)?;
255                    self.emit_element(writer, characters, &ElementNode::Text("("))?;
256                    self.emit_elements(writer, characters, parameters)?;
257                    self.emit_element(writer, characters, &ElementNode::Text(")"))
258                }
259                Element::Item => {
260                    self.confirm_newline(writer)?;
261                    self.emit_element(writer, characters, &ElementNode::Text("・"))?;
262                    self.emit_elements(writer, characters, children)?;
263                    self.confirm_newline(writer)
264                }
265                Element::Newline => writeln!(writer),
266                Element::Line(id, LineType::Inline) => {
267                    let ctype = characters.get(id);
268                    let (color, _) = self.get_color(ctype);
269                    self.style_stack.push(AbstractStyle::Color(color));
270                    self.emit_elements(writer, characters, children)?;
271                    self.style_stack.pop();
272                    Ok(())
273                }
274                Element::Line(id, line_type) => {
275                    let (color, name) = self.get_color(characters.get(id));
276                    self.confirm_newline(writer)?;
277                    self.style_stack.push(AbstractStyle::Color(color));
278                    if *line_type == LineType::NameShownBlock {
279                        self.emit_element(writer, characters, &ElementNode::Text(name))?;
280                    }
281                    self.emit_elements(writer, characters, children)?;
282                    self.style_stack.pop();
283                    self.confirm_newline(writer)
284                }
285                Element::Monospaced | Element::Parameter => {
286                    self.emit_elements(writer, characters, children)
287                }
288            },
289        }
290    }
291}
292
293impl<'a> Emit<'a> for ConsoleEmitter {
294    fn emit(&mut self, writer: &mut impl Write, document: &Document<'a>) -> Result<()> {
295        for block in &document.blocks {
296            self.emit_block(writer, &document.characters, block)?;
297        }
298        Ok(())
299    }
300}