mdbook_playscript/
counter.rs

1use std::collections::HashSet;
2use std::path::{Path, PathBuf};
3use mdplayscript::renderer::HtmlRenderer;
4use pulldown_cmark::Event;
5use rand::Rng;
6
7pub struct IgnorePatterns(Vec<glob::Pattern>);
8
9impl IgnorePatterns {
10    pub fn new() -> Self {
11        Self(vec![])
12    }
13
14    pub fn from_toml_values(v: &[toml::Value]) -> IgnorePatterns {
15        let mut pat = Vec::new();
16
17        for s in v.iter() {
18            let s = match s.as_str() {
19                Some(s) => s,
20                None => {
21                    log::warn!("given pattern is not a string: {}", s);
22                    continue;
23                },
24            };
25            match glob::Pattern::new(s) {
26                Ok(p) => {
27                    pat.push(p);
28                },
29                Err(e) => {
30                    log::warn!("ignore pattern error: {}", e);
31                },
32            }
33        }
34
35        IgnorePatterns(pat)
36    }
37
38    pub fn matches_path(&self, path: &Path) -> bool {
39        self.0
40            .iter()
41            .any(|pat| pat.matches_path(path))
42    }
43}
44
45pub struct CounterFactory<'a> {
46    prefix: &'a str,
47    issued_class: HashSet<String>,
48}
49
50impl<'a> CounterFactory<'a> {
51    pub fn new(prefix: &'a str) -> Self {
52        Self {
53            prefix: prefix,
54            issued_class: HashSet::new(),
55        }
56    }
57
58    pub fn issue(&mut self) -> Counter {
59        loop {
60            let class = generate_random_class_name(self.prefix);
61            if self.issued_class.insert(class.clone()) {
62                return Counter { class: class };
63            }
64        }
65    }
66}
67
68fn generate_random_class_name(prefix: &str) -> String {
69    let mut rng = rand::thread_rng();
70
71    let tag: String = std::iter::repeat(())
72        .map(|_| rng.sample(rand::distributions::Alphanumeric))
73        .map(char::from)
74        .take(10)
75        .collect();
76
77    format!("{}{}", prefix, tag)
78}
79
80pub struct Counter {
81    class: String,
82}
83
84impl Counter {
85    pub fn set_class_to_renderer(&self, renderer: &mut HtmlRenderer) {
86        renderer.speech_classes.add(&self.class);
87    }
88
89    pub fn generate_placeholder(&self, ignored: &IgnorePatterns, src: Option<&PathBuf>) -> Vec<Event<'static>> {
90        let div = match src {
91            Some(src) if ignored.matches_path(src) => {
92                log::info!("Ignore {}",src.display());
93                crate::IgnoredPlaceholder.to_cow_str()
94            },
95            _ => {
96                log::info!("Process {:?}", src);
97                format!(r#"<div class="mdplayscript-count {}"></div>"#, self.class)
98                    .into()
99            },
100        };
101
102        vec![Event::Html(div.into())]
103    }
104}