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
32pub 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}