streamson_lib/handler/
shorten.rs

1//! Handler which shortens matched data
2//! it can be used e.g. shorten long strings
3//! `"some long text"` -> `"some lon..."`
4//!
5//! # Example
6//! ```
7//! use streamson_lib::{handler, matcher, strategy::{self, Strategy}};
8//! use std::sync::{Arc, Mutex};
9//!
10//! let handler = Arc::new(Mutex::new(handler::Shorten::new(3, r#"..""#.to_string())));
11//! let matcher = matcher::Simple::new(r#"{"elements"}[]{"description"}"#).unwrap();
12//!
13//! let mut convert = strategy::Convert::new();
14//!
15//! // Set the matcher for convert strategy
16//! convert.add_matcher(Box::new(matcher), handler);
17//!
18//! for input in vec![
19//!     br#"{"elements": [{"description": "too long string"}, {"#.to_vec(),
20//!     br#""description": "other long string"}]}"#.to_vec(),
21//! ] {
22//!     for converted_data in convert.process(&input).unwrap() {
23//!         println!("{:?}", converted_data);
24//!     }
25//! }
26//! ```
27
28use super::Handler;
29use crate::{error, path::Path, streamer::Token};
30use std::{any::Any, str::FromStr};
31
32/// Handler which shortens the matched data
33///
34#[derive(Debug)]
35pub struct Shorten {
36    /// max length of original data
37    max_length: usize,
38
39    /// how many letters were already used
40    used: usize,
41
42    /// How shortened data are supposed to be terminated
43    /// Note that the new data length = max_length + terminator.length
44    terminator: String,
45}
46
47impl Shorten {
48    /// Creates a new handler which shortens matched data
49    pub fn new(max_length: usize, terminator: String) -> Self {
50        Self {
51            max_length,
52            terminator,
53            used: 0,
54        }
55    }
56}
57
58impl FromStr for Shorten {
59    type Err = error::Handler;
60    fn from_str(input: &str) -> Result<Self, Self::Err> {
61        let splitted: Vec<_> = input.split(',').collect();
62        if splitted.len() == 2 {
63            Ok(Self::new(
64                splitted[0].parse().map_err(error::Handler::new)?,
65                splitted[1].to_string(),
66            ))
67        } else {
68            Err(error::Handler::new("Failed to parse"))
69        }
70    }
71}
72
73impl Handler for Shorten {
74    fn start(
75        &mut self,
76        _path: &Path,
77        _matcher_idx: usize,
78        _token: Token,
79    ) -> Result<Option<Vec<u8>>, error::Handler> {
80        // clear the used so it can start matching again
81        self.used = 0;
82
83        Ok(None)
84    }
85
86    fn feed(
87        &mut self,
88        data: &[u8],
89        _matcher_idx: usize,
90    ) -> Result<Option<Vec<u8>>, error::Handler> {
91        // all letters were used
92        if self.used >= self.max_length {
93            return Ok(None);
94        }
95
96        // entire data can be consumed
97        if data.len() <= (self.max_length - self.used) {
98            self.used += data.len();
99            return Ok(Some(data.to_vec()));
100        }
101
102        // terminates now
103        let result: Vec<u8> = data[..(self.max_length - self.used + 1)]
104            .iter()
105            .cloned()
106            .chain(self.terminator.as_bytes().iter().cloned())
107            .collect();
108
109        self.used = self.max_length;
110
111        Ok(Some(result))
112    }
113
114    fn is_converter(&self) -> bool {
115        true
116    }
117
118    fn as_any(&self) -> &dyn Any {
119        self
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::Shorten;
126    use crate::{
127        matcher::Simple,
128        strategy::{Convert, OutputConverter, Strategy},
129    };
130    use std::sync::{Arc, Mutex};
131
132    #[test]
133    fn shorten_handler() {
134        let mut convert = Convert::new();
135        let shorten_handler = Arc::new(Mutex::new(Shorten::new(10, "..\"".to_string())));
136        let matcher = Simple::new(r#"[]{"description"}"#).unwrap();
137
138        convert.add_matcher(Box::new(matcher), shorten_handler.clone());
139        let output: Vec<u8> = OutputConverter::new()
140            .convert(
141                &convert
142                    .process(
143                        br#"[{"description": "too long description"}, {"description": "short"}]"#,
144                    )
145                    .unwrap(),
146            )
147            .into_iter()
148            .map(|e| e.1)
149            .flatten()
150            .collect();
151
152        assert_eq!(
153            String::from_utf8(output).unwrap(),
154            r#"[{"description": "too long d.."}, {"description": "short"}]"#
155        );
156    }
157
158    #[test]
159    fn shorten_handler_parts() {
160        let mut convert = Convert::new();
161        let shorten_handler = Arc::new(Mutex::new(Shorten::new(10, "..\"".to_string())));
162        let matcher = Simple::new(r#"[]{"description"}"#).unwrap();
163
164        convert.add_matcher(Box::new(matcher), shorten_handler.clone());
165        let mut output = convert.process(br#"[{"description": "t"#).unwrap();
166
167        output.extend(convert.process(br#"oo long d"#).unwrap());
168
169        output.extend(
170            convert
171                .process(br#"escription"}, {"description": "short"}]"#)
172                .unwrap(),
173        );
174
175        let output: Vec<u8> = OutputConverter::new()
176            .convert(&output)
177            .into_iter()
178            .map(|e| e.1)
179            .flatten()
180            .collect();
181
182        assert_eq!(
183            String::from_utf8(output).unwrap(),
184            r#"[{"description": "too long d.."}, {"description": "short"}]"#
185        );
186    }
187}