• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pythonbrad / clafrica / 6555320572

18 Oct 2023 02:10AM UTC coverage: 98.557% (-0.9%) from 99.443%
6555320572

Pull #99

github

web-flow
Merge 954493489 into 4a7e13941
Pull Request #99: refactor: make the clafrica more modular

739 of 739 new or added lines in 8 files covered. (100.0%)

1229 of 1247 relevant lines covered (98.56%)

80.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.08
/engine/preprocessor/src/lib.rs
1
//! Preprocessor of keyboard events for an input method.
2
//!
3
//! Example
4
//!
5
//! ```rust
6
//! use clafrica_preprocessor::{utils, Command, Preprocessor};
7
//! use keyboard_types::{
8
//!     webdriver::{self, Event},
9
//!     Key::*,
10
//! };
11
//! use std::collections::VecDeque;
12
//!
13
//! // We build initiate our preprocessor
14
//! let data = utils::load_data("cc ç");
15
//! let map = utils::build_map(data);
16
//! let mut preprocessor = Preprocessor::new(map, 8);
17
//!
18
//! // We trigger a sequence
19
//! webdriver::send_keys("cc")
20
//!     .into_iter()
21
//!     .for_each(|e| {
22
//!         match e {
23
//!             Event::Keyboard(e) => preprocessor.process(e),
24
//!             _ => unimplemented!(),
25
//!         };
26
//!     });
27
//!
28
//! // We got the generated command
29
//! let mut expecteds = VecDeque::from(vec![
30
//!     Command::Pause,
31
//!     Command::KeyClick(Backspace),
32
//!     Command::KeyClick(Backspace),
33
//!     Command::CommitText("ç".to_owned()),
34
//!     Command::Resume,
35
//! ]);
36
//!
37
//! while let Some(command) = preprocessor.pop_stack() {
38
//!     assert_eq!(command, expecteds.pop_front().unwrap());
39
//! }
40
//! ```
41

42
#![deny(missing_docs)]
43

44
mod message;
45

46
pub use crate::message::Command;
47
pub use clafrica_memory::utils;
48
use clafrica_memory::{Cursor, Node};
49
pub use keyboard_types::{Key, KeyState, KeyboardEvent};
50
use std::collections::VecDeque;
51

52
/// The main structure of the preprocessor.
53
#[derive(Debug)]
54
pub struct Preprocessor {
55
    cursor: Cursor,
56
    stack: VecDeque<Command>,
57
}
58

59
impl Preprocessor {
60
    /// Initiate a new preprocessor.
61
    pub fn new(map: Node, buffer_size: usize) -> Self {
7✔
62
        let cursor = Cursor::new(map, buffer_size);
7✔
63
        let stack = VecDeque::with_capacity(15);
7✔
64

7✔
65
        Self { cursor, stack }
7✔
66
    }
7✔
67

68
    /// Cancel the previous operation.
69
    fn rollback(&mut self) -> bool {
101✔
70
        self.stack.push_back(Command::KeyRelease(Key::Backspace));
101✔
71

72
        if let Some(out) = self.cursor.undo() {
101✔
73
            (1..out.chars().count())
53✔
74
                .for_each(|_| self.stack.push_back(Command::KeyClick(Key::Backspace)));
53✔
75

76
            // Clear the remaining code
77
            while let (None, 1.., ..) = self.cursor.state() {
103✔
78
                self.cursor.undo();
50✔
79
            }
50✔
80

81
            if let (Some(_in), ..) = self.cursor.state() {
53✔
82
                self.stack.push_back(Command::CommitText(_in));
21✔
83
            }
32✔
84

85
            true
53✔
86
        } else {
87
            false
48✔
88
        }
89
    }
101✔
90

91
    /// Process the key event.
92
    pub fn process(&mut self, event: KeyboardEvent) -> (bool, bool) {
684✔
93
        let (mut changed, mut committed) = (false, false);
684✔
94

684✔
95
        match (event.state, event.key) {
684✔
96
            (KeyState::Down, Key::Backspace) => {
58✔
97
                self.pause();
58✔
98
                committed = self.rollback();
58✔
99
                self.resume();
58✔
100
                changed = true;
58✔
101
            }
58✔
102
            (KeyState::Down, Key::Character(character))
195✔
103
                if character
195✔
104
                    .chars()
195✔
105
                    .next()
195✔
106
                    .map(|e| e.is_alphanumeric() || e.is_ascii_punctuation())
195✔
107
                    .unwrap_or(false) =>
195✔
108
            {
195✔
109
                let character = character.chars().next().unwrap();
195✔
110

111
                if let Some(_in) = self.cursor.hit(character) {
195✔
112
                    self.pause();
73✔
113

73✔
114
                    let mut prev_cursor = self.cursor.clone();
73✔
115
                    prev_cursor.undo();
73✔
116
                    self.stack.push_back(Command::KeyClick(Key::Backspace));
73✔
117

118
                    // Remove the remaining code
119
                    while let (None, 1.., ..) = prev_cursor.state() {
138✔
120
                        prev_cursor.undo();
65✔
121
                        self.stack.push_back(Command::KeyClick(Key::Backspace));
65✔
122
                    }
65✔
123

124
                    if let (Some(out), ..) = prev_cursor.state() {
73✔
125
                        (0..out.chars().count())
28✔
126
                            .for_each(|_| self.stack.push_back(Command::KeyClick(Key::Backspace)))
32✔
127
                    }
45✔
128

129
                    self.stack.push_back(Command::CommitText(_in));
73✔
130
                    self.resume();
73✔
131
                    committed = true;
73✔
132
                };
122✔
133

134
                changed = true;
195✔
135
            }
136
            (KeyState::Down, Key::Shift | Key::CapsLock) => (),
13✔
137
            (KeyState::Down, _) => {
25✔
138
                self.cursor.clear();
25✔
139
                changed = true;
25✔
140
            }
25✔
141
            _ => (),
393✔
142
        };
143

144
        (changed, committed)
684✔
145
    }
684✔
146

147
    /// Commit a text.
148
    pub fn commit(&mut self, text: &str) {
13✔
149
        self.pause();
13✔
150

151
        while !self.cursor.is_empty() {
56✔
152
            self.stack.push_back(Command::KeyPress(Key::Backspace));
43✔
153
            self.rollback();
43✔
154
        }
43✔
155
        self.stack.push_back(Command::CommitText(text.to_owned()));
13✔
156
        self.resume();
13✔
157
        // We clear the buffer
13✔
158
        self.cursor.clear();
13✔
159
    }
13✔
160

161
    /// Pause the keyboard event listerner.
162
    fn pause(&mut self) {
144✔
163
        self.stack.push_back(Command::Pause);
144✔
164
    }
144✔
165

166
    /// Resume the keyboard event listener.
167
    fn resume(&mut self) {
144✔
168
        self.stack.push_back(Command::Resume);
144✔
169
    }
144✔
170

171
    /// Return the sequence present in the memory.
172
    pub fn get_input(&self) -> String {
228✔
173
        self.cursor
228✔
174
            .to_sequence()
228✔
175
            .into_iter()
228✔
176
            .filter(|c| *c != '\0')
2,278✔
177
            .collect::<String>()
228✔
178
    }
228✔
179

180
    /// Return the next command to be executed.
181
    pub fn pop_stack(&mut self) -> Option<Command> {
1,357✔
182
        self.stack.pop_front()
1,357✔
183
    }
1,357✔
184

185
    /// Clear the stack.
186
    pub fn clear_stack(&mut self) {
1✔
187
        self.stack.clear();
1✔
188
    }
1✔
189
}
190

191
#[cfg(test)]
192
mod tests {
193
    use crate::message::Command;
194
    use crate::utils;
195
    use crate::Preprocessor;
196
    use keyboard_types::{
197
        webdriver::{self, Event},
198
        Key::*,
199
    };
200
    use std::collections::VecDeque;
201

202
    #[test]
1✔
203
    fn test_process() {
1✔
204
        let data = utils::load_data("ccced ç\ncc ç");
1✔
205
        let map = utils::build_map(data);
1✔
206
        let mut preprocessor = Preprocessor::new(map, 8);
1✔
207
        webdriver::send_keys("\u{E00C}ccced")
1✔
208
            .into_iter()
1✔
209
            .for_each(|e| {
12✔
210
                match e {
12✔
211
                    Event::Keyboard(e) => preprocessor.process(e),
12✔
212
                    _ => unimplemented!(),
×
213
                };
214
            });
12✔
215
        let mut expecteds = VecDeque::from(vec![
1✔
216
            Command::Pause,
1✔
217
            Command::KeyClick(Backspace),
1✔
218
            Command::KeyClick(Backspace),
1✔
219
            Command::CommitText("ç".to_owned()),
1✔
220
            Command::Resume,
1✔
221
            Command::Pause,
1✔
222
            Command::KeyClick(Backspace),
1✔
223
            Command::KeyClick(Backspace),
1✔
224
            Command::KeyClick(Backspace),
1✔
225
            Command::KeyClick(Backspace),
1✔
226
            Command::CommitText("ç".to_owned()),
1✔
227
            Command::Resume,
1✔
228
        ]);
1✔
229

230
        while let Some(command) = preprocessor.pop_stack() {
13✔
231
            assert_eq!(command, expecteds.pop_front().unwrap());
12✔
232
        }
233
    }
1✔
234

235
    #[test]
1✔
236
    fn test_commit() {
1✔
237
        use clafrica_memory::Node;
1✔
238
        use keyboard_types::KeyboardEvent;
1✔
239

1✔
240
        let mut preprocessor = Preprocessor::new(Node::default(), 8);
1✔
241
        preprocessor.process(KeyboardEvent {
1✔
242
            key: Character("a".to_owned()),
1✔
243
            ..Default::default()
1✔
244
        });
1✔
245
        preprocessor.commit("word");
1✔
246

1✔
247
        let mut expecteds = VecDeque::from(vec![
1✔
248
            Command::Pause,
1✔
249
            Command::KeyPress(Backspace),
1✔
250
            Command::KeyRelease(Backspace),
1✔
251
            Command::CommitText("word".to_owned()),
1✔
252
            Command::Resume,
1✔
253
        ]);
1✔
254

255
        while let Some(command) = preprocessor.pop_stack() {
6✔
256
            assert_eq!(command, expecteds.pop_front().unwrap());
5✔
257
        }
258
    }
1✔
259

260
    #[test]
1✔
261
    fn test_rollback() {
1✔
262
        use keyboard_types::KeyboardEvent;
1✔
263

1✔
264
        let data = utils::load_data("ccced ç\ncc ç");
1✔
265
        let map = utils::build_map(data);
1✔
266
        let mut preprocessor = Preprocessor::new(map, 8);
1✔
267
        let backspace_event = KeyboardEvent {
1✔
268
            key: Backspace,
1✔
269
            ..Default::default()
1✔
270
        };
1✔
271

1✔
272
        webdriver::send_keys("ccced").into_iter().for_each(|e| {
10✔
273
            match e {
10✔
274
                Event::Keyboard(e) => preprocessor.process(e),
10✔
275
                _ => unimplemented!(),
×
276
            };
277
        });
10✔
278

1✔
279
        preprocessor.clear_stack();
1✔
280
        assert_eq!(preprocessor.get_input(), "ccced".to_owned());
1✔
281
        preprocessor.process(backspace_event.clone());
1✔
282
        assert_eq!(preprocessor.get_input(), "cc".to_owned());
1✔
283
        preprocessor.process(backspace_event);
1✔
284
        assert_eq!(preprocessor.get_input(), "".to_owned());
1✔
285

286
        let mut expecteds = VecDeque::from(vec![
1✔
287
            Command::Pause,
1✔
288
            Command::KeyRelease(Backspace),
1✔
289
            Command::CommitText("ç".to_owned()),
1✔
290
            Command::Resume,
1✔
291
            Command::Pause,
1✔
292
            Command::KeyRelease(Backspace),
1✔
293
            Command::Resume,
1✔
294
        ]);
1✔
295

296
        while let Some(command) = preprocessor.pop_stack() {
8✔
297
            assert_eq!(command, expecteds.pop_front().unwrap());
7✔
298
        }
299
    }
1✔
300

301
    #[test]
1✔
302
    fn test_advanced() {
1✔
303
        use std::fs;
1✔
304

1✔
305
        let data = fs::read_to_string("./data/sample.txt").unwrap();
1✔
306
        let data = utils::load_data(&data);
1✔
307
        let map = utils::build_map(data);
1✔
308
        let mut preprocessor = Preprocessor::new(map, 64);
1✔
309

1✔
310
        webdriver::send_keys(
1✔
311
            "u\u{E003}uu\u{E003}uc_ceduuaf3afafaff3uu3\
1✔
312
            \u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}\u{E003}"
1✔
313
        ).into_iter().for_each(|e| {
80✔
314
            match e {
80✔
315
                Event::Keyboard(e) => preprocessor.process(e),
80✔
316
                _ => unimplemented!(),
×
317
            };
318
        });
80✔
319

1✔
320
        let mut expecteds = VecDeque::from(vec![
1✔
321
            // Process
1✔
322
            Command::Pause,
1✔
323
            Command::KeyRelease(Backspace),
1✔
324
            Command::Resume,
1✔
325
            Command::Pause,
1✔
326
            Command::KeyClick(Backspace),
1✔
327
            Command::KeyClick(Backspace),
1✔
328
            Command::CommitText("ʉ".to_owned()),
1✔
329
            Command::Resume,
1✔
330
            Command::Pause,
1✔
331
            Command::KeyRelease(Backspace),
1✔
332
            Command::Resume,
1✔
333
            Command::Pause,
1✔
334
            Command::KeyClick(Backspace),
1✔
335
            Command::KeyClick(Backspace),
1✔
336
            Command::CommitText("ç".to_owned()),
1✔
337
            Command::Resume,
1✔
338
            Command::Pause,
1✔
339
            Command::KeyClick(Backspace),
1✔
340
            Command::KeyClick(Backspace),
1✔
341
            Command::KeyClick(Backspace),
1✔
342
            Command::KeyClick(Backspace),
1✔
343
            Command::CommitText("ç".to_owned()),
1✔
344
            Command::Resume,
1✔
345
            Command::Pause,
1✔
346
            Command::KeyClick(Backspace),
1✔
347
            Command::KeyClick(Backspace),
1✔
348
            Command::CommitText("ʉ".to_owned()),
1✔
349
            Command::Resume,
1✔
350
            Command::Pause,
1✔
351
            Command::KeyClick(Backspace),
1✔
352
            Command::KeyClick(Backspace),
1✔
353
            Command::KeyClick(Backspace),
1✔
354
            Command::KeyClick(Backspace),
1✔
355
            Command::CommitText("ʉ\u{304}ɑ\u{304}".to_owned()),
1✔
356
            Command::Resume,
1✔
357
            Command::Pause,
1✔
358
            Command::KeyClick(Backspace),
1✔
359
            Command::KeyClick(Backspace),
1✔
360
            Command::CommitText("ɑ".to_owned()),
1✔
361
            Command::Resume,
1✔
362
            Command::Pause,
1✔
363
            Command::KeyClick(Backspace),
1✔
364
            Command::KeyClick(Backspace),
1✔
365
            Command::CommitText("ɑ".to_owned()),
1✔
366
            Command::Resume,
1✔
367
            Command::Pause,
1✔
368
            Command::KeyClick(Backspace),
1✔
369
            Command::KeyClick(Backspace),
1✔
370
            Command::CommitText("ɑ".to_owned()),
1✔
371
            Command::Resume,
1✔
372
            Command::Pause,
1✔
373
            Command::KeyClick(Backspace),
1✔
374
            Command::KeyClick(Backspace),
1✔
375
            Command::CommitText("ɑɑ".to_owned()),
1✔
376
            Command::Resume,
1✔
377
            Command::Pause,
1✔
378
            Command::KeyClick(Backspace),
1✔
379
            Command::KeyClick(Backspace),
1✔
380
            Command::KeyClick(Backspace),
1✔
381
            Command::CommitText("ɑ\u{304}ɑ\u{304}".to_owned()),
1✔
382
            Command::Resume,
1✔
383
            Command::Pause,
1✔
384
            Command::KeyClick(Backspace),
1✔
385
            Command::KeyClick(Backspace),
1✔
386
            Command::CommitText("ʉ".to_owned()),
1✔
387
            Command::Resume,
1✔
388
            Command::Pause,
1✔
389
            Command::KeyClick(Backspace),
1✔
390
            Command::KeyClick(Backspace),
1✔
391
            Command::CommitText("ʉ\u{304}".to_owned()),
1✔
392
            Command::Resume,
1✔
393
            // Rollback
1✔
394
            Command::Pause,
1✔
395
            Command::KeyRelease(Backspace),
1✔
396
            Command::KeyClick(Backspace),
1✔
397
            Command::CommitText("ʉ".to_owned()),
1✔
398
            Command::Resume,
1✔
399
            Command::Pause,
1✔
400
            Command::KeyRelease(Backspace),
1✔
401
            Command::Resume,
1✔
402
            Command::Pause,
1✔
403
            Command::KeyRelease(Backspace),
1✔
404
            Command::KeyClick(Backspace),
1✔
405
            Command::KeyClick(Backspace),
1✔
406
            Command::KeyClick(Backspace),
1✔
407
            Command::CommitText("ɑɑ".to_owned()),
1✔
408
            Command::Resume,
1✔
409
            Command::Pause,
1✔
410
            Command::KeyRelease(Backspace),
1✔
411
            Command::KeyClick(Backspace),
1✔
412
            Command::CommitText("ɑ".to_owned()),
1✔
413
            Command::Resume,
1✔
414
            Command::Pause,
1✔
415
            Command::KeyRelease(Backspace),
1✔
416
            Command::Resume,
1✔
417
            Command::Pause,
1✔
418
            Command::KeyRelease(Backspace),
1✔
419
            Command::Resume,
1✔
420
            Command::Pause,
1✔
421
            Command::KeyRelease(Backspace),
1✔
422
            Command::Resume,
1✔
423
            Command::Pause,
1✔
424
            Command::KeyRelease(Backspace),
1✔
425
            Command::KeyClick(Backspace),
1✔
426
            Command::KeyClick(Backspace),
1✔
427
            Command::KeyClick(Backspace),
1✔
428
            Command::CommitText("ʉ".to_owned()),
1✔
429
            Command::Resume,
1✔
430
            Command::Pause,
1✔
431
            Command::KeyRelease(Backspace),
1✔
432
            Command::Resume,
1✔
433
            Command::Pause,
1✔
434
            Command::KeyRelease(Backspace),
1✔
435
            Command::CommitText("ç".to_owned()),
1✔
436
            Command::Resume,
1✔
437
            Command::Pause,
1✔
438
            Command::KeyRelease(Backspace),
1✔
439
            Command::Resume,
1✔
440
            Command::Pause,
1✔
441
            Command::KeyRelease(Backspace),
1✔
442
            Command::Resume,
1✔
443
        ]);
1✔
444

445
        while let Some(command) = preprocessor.pop_stack() {
121✔
446
            assert_eq!(command, expecteds.pop_front().unwrap());
120✔
447
        }
448
    }
1✔
449
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc