1use std::{
2 cmp::{max, min},
3 fmt::Display,
4 ops::RangeInclusive,
5};
6
7use rand::{prelude::ThreadRng, Rng};
8use serde::Deserialize;
9use termion::style;
10
11const SCENES: [&str; 26] = [
12 include_str!("res/01.json"),
13 include_str!("res/02.json"),
14 include_str!("res/03.json"),
15 include_str!("res/04.json"),
16 include_str!("res/05.json"),
17 include_str!("res/06.json"),
18 include_str!("res/07.json"),
19 include_str!("res/08.json"),
20 include_str!("res/09.json"),
21 include_str!("res/10.json"),
22 include_str!("res/11.json"),
23 include_str!("res/12.json"),
24 include_str!("res/13.json"),
25 include_str!("res/14.json"),
26 include_str!("res/15.json"),
27 include_str!("res/16.json"),
28 include_str!("res/17.json"),
29 include_str!("res/18.json"),
30 include_str!("res/19.json"),
31 include_str!("res/20.json"),
32 include_str!("res/21.json"),
33 include_str!("res/22.json"),
34 include_str!("res/23.json"),
35 include_str!("res/24.json"),
36 include_str!("res/25.json"),
37 include_str!("res/26.json"),
38];
39
40#[derive(Debug)]
41struct Metadata {
42 act: &'static str,
43 scene: usize,
44 lines: usize,
45}
46
47const METADATA: [Metadata; 26] = [
48 Metadata {
49 act: "1",
50 scene: 1,
51 lines: 332,
52 },
53 Metadata {
54 act: "",
55 scene: 2,
56 lines: 191,
57 },
58 Metadata {
59 act: "",
60 scene: 3,
61 lines: 27,
62 },
63 Metadata {
64 act: "",
65 scene: 4,
66 lines: 352,
67 },
68 Metadata {
69 act: "",
70 scene: 5,
71 lines: 48,
72 },
73 Metadata {
74 act: "2",
75 scene: 1,
76 lines: 141,
77 },
78 Metadata {
79 act: "",
80 scene: 2,
81 lines: 177,
82 },
83 Metadata {
84 act: "",
85 scene: 3,
86 lines: 21,
87 },
88 Metadata {
89 act: "",
90 scene: 4,
91 lines: 339,
92 },
93 Metadata {
94 act: "3",
95 scene: 1,
96 lines: 58,
97 },
98 Metadata {
99 act: "",
100 scene: 2,
101 lines: 100,
102 },
103 Metadata {
104 act: "",
105 scene: 3,
106 lines: 25,
107 },
108 Metadata {
109 act: "",
110 scene: 4,
111 lines: 187,
112 },
113 Metadata {
114 act: "",
115 scene: 5,
116 lines: 25,
117 },
118 Metadata {
119 act: "",
120 scene: 6,
121 lines: 117,
122 },
123 Metadata {
124 act: "",
125 scene: 7,
126 lines: 118,
127 },
128 Metadata {
129 act: "4",
130 scene: 1,
131 lines: 90,
132 },
133 Metadata {
134 act: "",
135 scene: 2,
136 lines: 111,
137 },
138 Metadata {
139 act: "",
140 scene: 3,
141 lines: 62,
142 },
143 Metadata {
144 act: "",
145 scene: 4,
146 lines: 32,
147 },
148 Metadata {
149 act: "",
150 scene: 5,
151 lines: 45,
152 },
153 Metadata {
154 act: "",
155 scene: 6,
156 lines: 314,
157 },
158 Metadata {
159 act: "",
160 scene: 7,
161 lines: 110,
162 },
163 Metadata {
164 act: "5",
165 scene: 1,
166 lines: 78,
167 },
168 Metadata {
169 act: "",
170 scene: 2,
171 lines: 13,
172 },
173 Metadata {
174 act: "",
175 scene: 3,
176 lines: 386,
177 },
178];
179
180pub fn display_contents() {
181 println!(
182 "{}{: ^19}{}",
183 style::Italic,
184 "The Tragedie of",
185 style::Reset
186 );
187 println!("{: ^19}\n", "KING LEAR");
188 println!("{: ^19}\n", "by");
189 println!("{}{: ^19}{}", style::Italic, "William", style::Reset);
190 println!("{}{: ^19}{}\n", style::Italic, "Shakespeare", style::Reset);
191 println!(
192 "{}{: ^5}{: ^7}{: ^7}{}",
193 style::Bold,
194 "Act",
195 "Scene",
196 "Lines",
197 style::Reset
198 );
199 for line in &METADATA {
200 if !line.act.is_empty() {
201 println!("{:-^19}", "");
202 }
203 println!("{: ^5}{: ^7}{: ^7}", line.act, line.scene, line.lines,);
204 }
205}
206
207struct TableEntry {
208 start: usize,
209 scenes: RangeInclusive<usize>,
210}
211
212impl TableEntry {
213 fn index(&self, scene: usize) -> Option<usize> {
214 if self.scenes.contains(&scene) {
215 Some(self.start + scene - 1)
216 } else {
217 None
218 }
219 }
220}
221
222const TABLE_OF_CONTENTS: [TableEntry; 5] = [
223 TableEntry {
224 start: 0,
225 scenes: (1..=5),
226 },
227 TableEntry {
228 start: 5,
229 scenes: (1..=4),
230 },
231 TableEntry {
232 start: 9,
233 scenes: (1..=7),
234 },
235 TableEntry {
236 start: 16,
237 scenes: (1..=7),
238 },
239 TableEntry {
240 start: 23,
241 scenes: (1..=3),
242 },
243];
244
245#[derive(Debug, Deserialize, Clone)]
246pub struct Heading {
247 act: String,
248 scene: String,
249 setting: String,
250 staging: String,
251}
252
253#[derive(Debug)]
254pub enum LearError {
255 IoError(std::io::Error),
256 InvalidAct(usize),
257 InvalidScene {
258 act: usize,
259 scene: usize,
260 },
261 InvalidLines {
262 act: usize,
263 scene: usize,
264 lines: RangeInclusive<usize>,
265 },
266}
267
268impl From<serde_json::Error> for LearError {
269 fn from(e: serde_json::Error) -> Self {
270 LearError::IoError(e.into())
271 }
272}
273
274impl Display for LearError {
275 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
276 write!(f, "{:?}", self)
277 }
278}
279
280impl std::error::Error for LearError {}
281
282impl Display for &Heading {
283 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
284 write!(
285 f,
286 "{}{}, {}{}\n{}\n\n\t{}{}{}\n\n",
287 style::Bold,
288 self.act,
289 self.scene,
290 style::Reset,
291 self.setting,
292 style::Italic,
293 self.staging,
294 style::Reset
295 )
296 }
297}
298
299#[derive(Debug, Deserialize, Clone)]
300pub enum Line {
301 #[serde(rename(deserialize = "text"))]
302 Text(String),
303 #[serde(rename(deserialize = "direction"))]
304 Direction(String),
305}
306
307#[derive(Debug, Deserialize, Clone)]
308pub struct Dialogue {
309 character: String,
310 lines: Vec<Line>,
311 act: usize,
312 scene: usize,
313 start: usize,
314 end: usize,
315}
316
317impl Dialogue {
318 fn selection(&self, range: &RangeInclusive<usize>) -> Option<Dialogue> {
319 if self.start > *range.end() || self.end < *range.start() {
321 return None;
322 }
323 let start_line = max(range.start(), &self.start);
325 let end_line = min(range.end(), &self.end);
326 let (start, _) = self
328 .lines
329 .iter()
330 .enumerate()
331 .filter(|(_, line)| matches!(line, Line::Text(_)))
332 .find(|(i, _)| i + self.start >= *start_line)?;
333 let (lines_of_text, _) = self
337 .lines
338 .iter()
339 .filter(|line| matches!(line, Line::Text(_)))
340 .enumerate()
341 .find(|(i, _)| i + self.start >= *end_line)?;
342 let skipped_directions = self
343 .lines
344 .iter()
345 .take(lines_of_text)
346 .filter(|line| matches!(line, Line::Direction(_)))
347 .count();
348 let end = lines_of_text + skipped_directions;
349 let mut lines: Vec<Line> = self.lines[start..=end].to_vec();
351 if lines.is_empty() {
353 return None;
354 }
355 if range.start() > &self.start {
357 lines.insert(0, Line::Text("...".into()));
358 }
359 if range.end() < &self.end {
360 lines.push(Line::Text("...".into()));
361 }
362 Some(Dialogue {
363 lines,
364 start: *start_line,
365 end: *end_line,
366 character: self.character.clone(),
367 ..*self
368 })
369 }
370}
371
372impl Display for &Dialogue {
373 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
374 f.write_fmt(format_args!(
375 "{}{}{}\n",
376 style::Bold,
377 self.character,
378 style::Reset
379 ))?;
380 let iter = self.lines.iter().peekable();
381 let mut prev = None;
382 for line in iter {
384 if let Some(&Line::Direction(_)) = prev {
385 match line {
386 Line::Text(text) => f.write_fmt(format_args!("\n\t{}\n", text))?,
387 Line::Direction(direction) => f.write_fmt(format_args!(
388 "\t{}{}{}\n",
389 style::Italic,
390 direction,
391 style::Reset
392 ))?,
393 };
394 } else {
395 match line {
396 Line::Text(text) => f.write_fmt(format_args!("\t{}\n", text))?,
397 Line::Direction(direction) => f.write_fmt(format_args!(
398 "\n\t{}{}{}\n",
399 style::Italic,
400 direction,
401 style::Reset
402 ))?,
403 };
404 }
405 prev = Some(line);
406 }
407 f.write_fmt(format_args!("\n"))?;
408 Ok(())
409 }
410}
411
412#[derive(Debug, Deserialize, Clone)]
413pub enum Block {
414 Heading(Heading),
415 Dialogue(Dialogue),
416}
417
418impl Block {
419 fn selection(&self, range: &RangeInclusive<usize>) -> Option<Block> {
420 match self {
421 Block::Heading(_) => None,
422 Block::Dialogue(d) => Some(Block::Dialogue(d.selection(range)?)),
423 }
424 }
425}
426
427impl Display for &Block {
428 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
429 match self {
430 Block::Heading(heading) => write!(f, "{}", heading),
431 Block::Dialogue(dialogue) => write!(f, "{}", dialogue),
432 }
433 }
434}
435
436pub fn blocks_to_show(rng: &mut ThreadRng) -> Result<Vec<Block>, std::io::Error> {
437 let scene = rng.gen_range(0..25) as usize;
438 let blocks: Vec<Block> = serde_json::from_str(SCENES[scene])?;
439 let blocks_to_show = rng.gen_range(2..=min(5, blocks.len())) as usize;
440 let range = 0..(blocks.len() - blocks_to_show);
442 let start = if range.is_empty() {
443 0
444 } else {
445 rng.gen_range(range)
446 };
447 let blocks = blocks
448 .into_iter()
449 .skip(start)
450 .take(blocks_to_show)
451 .collect();
452 Ok(blocks)
453}
454
455pub fn text(
456 act: usize,
457 scene: usize,
458 lines: RangeInclusive<usize>,
459) -> Result<Vec<Block>, LearError> {
460 let scene_index = TABLE_OF_CONTENTS
461 .get(act - 1)
462 .ok_or(LearError::InvalidAct(act))?
463 .index(scene)
464 .ok_or(LearError::InvalidScene { act, scene })?;
465 let blocks: Vec<Block> = serde_json::from_str(SCENES[scene_index])?;
466 let blocks: Vec<Block> = blocks
467 .iter()
469 .filter_map(|b| b.selection(&lines))
470 .collect();
471 if blocks.is_empty() {
472 return Err(LearError::InvalidLines { act, scene, lines });
473 }
474 Ok(blocks)
475}
476
477fn attribution(blocks: &[Block]) -> Option<String> {
478 let dialogue: Vec<_> = blocks
479 .iter()
480 .filter_map(|b| match b {
481 Block::Heading(_) => None,
482 Block::Dialogue(dialogue) => Some(dialogue),
483 })
484 .collect();
485 let first = dialogue.first()?;
486 let act = first.act;
487 let scene = first.scene;
488 let start = first.start;
489 let end = dialogue.last()?.end;
490 Some(format!(
491 "({}Lr.{} {}.{}.{}-{})",
492 style::Italic,
493 style::Reset,
494 act,
495 scene,
496 start,
497 end
498 ))
499}
500
501pub fn display(blocks: &[Block]) {
502 let attribution = attribution(blocks);
503 for block in blocks {
504 print!("{}", block);
505 }
506 if let Some(attr) = attribution {
507 println!("{: >80}", attr);
508 }
509}