batchcensor/
transcript.rs

1use crate::{Range, Replace};
2
3/// A parsed stranscript.
4#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
5pub struct Transcript {
6    pub text: String,
7    pub replace: Vec<Replace>,
8    /// Marked words without a timestamp.
9    pub missing: Vec<String>,
10}
11
12impl Transcript {
13    pub fn parse(text: &str) -> Result<Transcript, failure::Error> {
14        let mut it = text.chars();
15
16        let mut replace = Vec::new();
17        let mut missing = Vec::new();
18
19        while let Some(c) = it.next() {
20            match c {
21                '[' => {
22                    let (word, range) = Self::parse_replace(&mut it)?;
23
24                    match range {
25                        Some(range) => {
26                            replace.push(Replace { word, range });
27                        }
28                        None => {
29                            missing.push(word);
30                        }
31                    }
32                }
33                _ => {}
34            }
35        }
36
37        Ok(Transcript {
38            text: text.to_string(),
39            replace,
40            missing,
41        })
42    }
43
44    /// Parse a single replacement: [word]{range}.
45    pub fn parse_replace(
46        it: &mut impl Iterator<Item = char>,
47    ) -> Result<(String, Option<Range>), failure::Error> {
48        let mut word = None;
49        let mut buffer = String::new();
50
51        while let Some(c) = it.next() {
52            match c {
53                ']' => {
54                    word = Some(buffer);
55                    break;
56                }
57                c => {
58                    buffer.push(c);
59                }
60            }
61        }
62
63        let word = match word {
64            Some(word) => word,
65            None => {
66                failure::bail!("missing word");
67            }
68        };
69
70        let open = it.next();
71
72        if open != Some('{') {
73            return Ok((word, None));
74        }
75
76        let mut range = None;
77        let mut buffer = String::new();
78
79        while let Some(c) = it.next() {
80            match c {
81                '}' => {
82                    range = Some(buffer);
83                    break;
84                }
85                c => {
86                    buffer.push(c);
87                }
88            }
89        }
90
91        let range = match range {
92            Some(range) => range,
93            None => {
94                failure::bail!("missing range");
95            }
96        };
97
98        let range = Range::parse(&range).ok_or_else(|| failure::format_err!("bad range"))?;
99
100        Ok((word, Some(range)))
101    }
102}
103
104impl<'de> serde::Deserialize<'de> for Transcript {
105    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
106    where
107        D: serde::Deserializer<'de>,
108    {
109        let s: String = String::deserialize(deserializer)?;
110        Transcript::parse(&s).map_err(|e| <D::Error as serde::de::Error>::custom(e.to_string()))
111    }
112}
113
114impl serde::Serialize for Transcript {
115    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
116    where
117        S: serde::Serializer,
118    {
119        serializer.serialize_str(&self.text)
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::Transcript;
126    use crate::{Range, Replace};
127
128    #[test]
129    pub fn test() -> Result<(), failure::Error> {
130        let transcript = Transcript::parse("foo [bar]{01.123-$} [baz]{^-$}")?;
131
132        let a = Replace {
133            word: String::from("bar"),
134            range: Range::parse("01.123-$").expect("valid range"),
135        };
136
137        assert_eq!(a, transcript.replace[0]);
138
139        let b = Replace {
140            word: String::from("baz"),
141            range: Range::parse("^-$").expect("valid range"),
142        };
143
144        assert_eq!(b, transcript.replace[1]);
145        Ok(())
146    }
147}