afrim_memory/lib.rs
1#![deny(missing_docs)]
2//! Data structure to make handling of sequential code more convenient.
3//!
4//! It takes sequential codes and generates a text buffer that will be used to easily get a
5//! corresponding character through an input.
6//!
7//! # Notes
8//! - sequence: A sequential code corresponding to a character.
9//! Eg. af1 = "ɑ̀"
10//! - input: The user input (or a set of sequences).
11//! Eg. ngaf7 nkwe2e2 ka7meru7n
12//! - text buffer: The memory where our text data will be stored.
13//! - node: A node in the text buffer.
14//!
15//! # Example
16//!
17//! ```
18//! use afrim_memory::{Node, utils};
19//!
20//! // Builds a TextBuffer.
21//! let text_buffer = Node::default();
22//! text_buffer.insert(vec!['a', 'f'], "ɑ".to_owned());
23//! text_buffer.insert(vec!['a', 'f', '1'], "ɑ̀".to_owned());
24//!
25//! // Bulk insertion of data in the TextBuffer.
26//! let data = vec![vec!["af11", "ɑ̀ɑ̀"], vec!["?.", "ʔ"]];
27//! let text_buffer = utils::build_map(data);
28//!
29//! // Traverses the tree.
30//! let node = text_buffer.goto('a').and_then(|node| node.goto('f')).and_then(|node| node.goto('1')).and_then(|node| node.goto('1'));
31//! assert_eq!(node.unwrap().take(), Some("ɑ̀ɑ̀".to_owned()));
32//! ```
33//!
34//! # Example: in reading data through a file
35//!
36//! ```no_run
37//! use afrim_memory::utils;
38//!
39//! // Import data from a string.
40//! let data = "a1 à\ne2 é";
41//! let data = utils::load_data(data);
42//! let text_buffer = utils::build_map(data);
43//! ```
44//!
45//! # Example: with the usage of a cursor
46//!
47//! ```
48//! use afrim_memory::{Cursor, Node};
49//! use std::rc::Rc;
50//!
51//! // Build a TextBuffer.
52//! let text_buffer = Node::default();
53//! text_buffer.insert(vec!['i', '-'], "ɨ".to_owned());
54//! text_buffer.insert(vec!['i', '-', '3'], "ɨ̄".to_owned());
55//!
56//! // Builds the cursor.
57//! let memory = Rc::new(text_buffer);
58//! let mut cursor = Cursor::new(memory, 16);
59//!
60//! // Moves the cursor through the input.
61//! let input = "i-3";
62//! input.chars().for_each(|c| { cursor.hit(c); });
63//! // Verify the current state.
64//! assert_eq!(cursor.state(), (Some("ɨ̄".to_owned()), 3, '3'));
65//!
66//! // Undo the last insertion.
67//! assert_eq!(cursor.undo(), Some("ɨ̄".to_owned()));
68//! // Verify the current state.
69//! assert_eq!(cursor.state(), (Some("ɨ".to_owned()), 2, '-'));
70//! ```
71//!
72//! [`TextBuffer`]: https://en.wikipedia.org/wiki/Text_buffer
73
74use std::collections::{HashMap, VecDeque};
75use std::{cell::RefCell, fmt, rc::Rc};
76pub mod utils;
77
78/// A node in the text buffer.
79///
80/// ```text
81/// 0 ----------------> The root node
82/// / \
83/// 'g' 's' -------------> Node: Rc<Node>
84/// / \
85/// "ɣ" = '+' 'h' -----------> Node: Rc<Node>
86/// \
87/// '+' = "ʃ" ---> Node that holds a value
88/// ```
89#[derive(Clone, Debug)]
90pub struct Node {
91 children: RefCell<HashMap<char, Rc<Node>>>,
92 /// Depth of the node.
93 pub depth: usize,
94 /// Character holded by the node.
95 pub key: char,
96 value: RefCell<Option<String>>,
97}
98
99impl Default for Node {
100 /// Create a root node.
101 ///
102 /// A root node always holds a null character as key and is recommanded to use
103 /// to initialize the text buffer. You should always use it to create a text buffer because the
104 /// internal code can change.
105 ///
106 /// # Example
107 ///
108 /// ```
109 /// use afrim_memory::Node;
110 ///
111 /// // It's recommanded to use it, to initialize your text buffer.
112 /// let text_buffer = Node::default();
113 /// // Not recommanded.
114 /// let another_text_buffer = Node::new('\0', 0);
115 ///
116 /// assert!(text_buffer.is_root());
117 /// assert!(another_text_buffer.is_root());
118 /// ```
119 fn default() -> Self {
120 Self::new('\0', 0)
121 }
122}
123
124impl Node {
125 /// Initializes a new node in the text buffer.
126 ///
127 /// Can also be used to initialize the text buffer (not recommanded).
128 /// Uses [`Node::default`](crate::Node::default) instead.
129 ///
130 /// # Example
131 ///
132 /// ```
133 /// use afrim_memory::Node;
134 ///
135 /// let text_buffer = Node::new('\0', 0);
136 ///
137 /// // You cannot assign directly a value to a node.
138 /// // But, an alternative is as below.
139 /// let node = Node::new('u', 0);
140 /// node.insert(vec![], "ʉ̠̀".to_owned());
141 /// assert_eq!(node.take(), Some("ʉ̠̀".to_owned()));
142 /// ```
143 ///
144 /// **Note**: Early, [`Node::new`](crate::Node::new) was the only way to initialize a text
145 /// buffer but it has been replaced by [`Node::default`](crate::Node::default)
146 /// which is now more adapted for this use case.
147 pub fn new(key: char, depth: usize) -> Self {
148 Self {
149 children: HashMap::new().into(),
150 depth,
151 key,
152 value: None.into(),
153 }
154 }
155
156 /// Inserts a sequence in the text buffer.
157 ///
158 /// # Example
159 ///
160 /// ```
161 /// use afrim_memory::Node;
162 ///
163 /// let text_buffer = Node::default();
164 /// text_buffer.insert(vec!['.', 't'], "ṫ".to_owned());
165 ///
166 /// let node = text_buffer.goto('.').and_then(|node| node.goto('t'));
167 /// assert_eq!(node.unwrap().take(), Some("ṫ".to_owned()));
168 /// ```
169 pub fn insert(&self, sequence: Vec<char>, value: String) {
170 if let Some(character) = sequence.first() {
171 self.children
172 .borrow_mut()
173 .entry(*character)
174 .or_insert_with(|| Rc::new(Self::new(*character, self.depth + 1)))
175 .insert(sequence.into_iter().skip(1).collect(), value);
176 } else {
177 *self.value.borrow_mut() = Some(value);
178 };
179 }
180
181 /// Moves from the current node to his child.
182 ///
183 /// Useful to go through a sequence.
184 ///
185 /// # Example
186 ///
187 /// ```
188 /// use afrim_memory::Node;
189 ///
190 /// let text_buffer = Node::default();
191 /// text_buffer.insert(vec!['o', '/'], "ø".to_owned());
192 /// text_buffer.insert(vec!['o', '*'], "ɔ".to_owned());
193 /// text_buffer.insert(vec!['o', '1'], "ò".to_owned());
194 /// text_buffer.insert(vec!['o', '*', '~'], "ɔ̃".to_owned());
195 ///
196 /// // let sequence = ['o', '*', '~'];
197 /// let node = text_buffer.goto('o').unwrap();
198 /// assert_eq!(node.take(), None);
199 /// let node = node.goto('*').unwrap();
200 /// assert_eq!(node.take(), Some("ɔ".to_owned()));
201 /// let node = node.goto('~').unwrap();
202 /// assert_eq!(node.take(), Some("ɔ̃".to_owned()));
203 /// ```
204 pub fn goto(&self, character: char) -> Option<Rc<Self>> {
205 self.children.borrow().get(&character).map(Rc::clone)
206 }
207
208 /// Extracts the value of the node.
209 ///
210 /// A node in the text buffer don't always holds a value.
211 /// Hence, his value is optional.
212 ///
213 /// # Example
214 ///
215 /// ```
216 /// use afrim_memory::Node;
217 ///
218 /// let text_buffer = Node::default();
219 /// text_buffer.insert(vec!['1', 'c'], "c̀".to_string());
220 ///
221 /// let node = text_buffer.goto('1').unwrap();
222 /// assert_eq!(node.take(), None);
223 /// let node = node.goto('c').unwrap();
224 /// assert_eq!(node.take(), Some("c̀".to_owned()));
225 /// ```
226 pub fn take(&self) -> Option<String> {
227 self.value.borrow().as_ref().map(ToOwned::to_owned)
228 }
229
230 /// Returns true is the node is at the initial depth.
231 ///
232 /// Useful when dealing with the [`Cursor`].
233 /// Will permit to know the beginning and the end of a sequence.
234 ///
235 /// # Example
236 ///
237 /// ```
238 /// use afrim_memory::{Cursor, Node};
239 ///
240 /// let text_buffer = Node::default();
241 /// text_buffer.insert(vec!['e', '2' ], "é".to_owned());
242 /// text_buffer.insert(vec!['i', '7' ], "ǐ".to_owned());
243 ///
244 /// assert!(text_buffer.is_root());
245 /// let node = text_buffer.goto('e').unwrap();
246 /// assert!(!node.is_root());
247 ///
248 /// ```
249 pub fn is_root(&self) -> bool {
250 self.depth == 0
251 }
252}
253
254/// The Cursor permits to keep a track of the different positions while moving in
255/// the text buffer.
256///
257/// ```text
258/// '\0' - 'k' - '\0' - 'w' - '\0' '\0' '\0' - '\'' - 'n' - 'i' - '7' |--> 0
259/// | /| / |
260/// | / | / |
261/// 'e' / 'e' / |--> 1
262/// | / | / |
263/// '2' '2' |--> 2
264/// |
265/// | depth
266/// v
267/// ```
268///
269/// # Example
270///
271/// ```
272/// use afrim_memory::{Cursor, Node};
273/// use std::rc::Rc;
274///
275/// let text_buffer = Node::default();
276/// text_buffer.insert(vec!['e', '2'], "é".to_owned());
277/// text_buffer.insert(vec!['i', '7'], "ǐ".to_owned());
278///
279/// // We build our cursor.
280/// let memory = Rc::new(text_buffer);
281/// let mut cursor = Cursor::new(memory, 16);
282/// let input = "nkwe2e2'ni7";
283/// input.chars().for_each(|c| { cursor.hit(c); });
284///
285/// assert_eq!(
286/// cursor.to_sequence(),
287/// vec![
288/// 'k', '\0', 'w', '\0', 'e', '2', '\0', 'e', '2', '\0',
289/// '\'', '\0', 'n', '\0', 'i', '7'
290/// ]
291/// );
292/// ```
293///
294/// Note the partitioning of this input. The cursor can browse through the memory based
295/// on an input and save a track of his positions. It's useful when we want handle
296/// backspace operations in an input method engine.
297#[derive(Clone)]
298pub struct Cursor {
299 buffer: VecDeque<Rc<Node>>,
300 root: Rc<Node>,
301}
302
303impl fmt::Debug for Cursor {
304 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
305 self.to_sequence().fmt(f)
306 }
307}
308
309impl Cursor {
310 /// Initializes the cursor of a text buffer.
311 ///
312 /// `capacity` is the number of hit that the cursor can track. The cursor follows the FIFO
313 /// rule. If the capacity is exceeded, the oldest hit will be discarded.
314 ///
315 /// **Note**: Be careful when you set this capacity. We recommend to select a capacity equal or
316 /// greater than the maximun sequence length that you want handle.
317 ///
318 /// # Example
319 ///
320 /// ```
321 /// use afrim_memory::{Cursor, Node};
322 /// use std::rc::Rc;
323 ///
324 /// let text_buffer = Node::default();
325 /// let memory = Rc::new(text_buffer);
326 ///
327 /// // A cursor of our text buffer.
328 /// let cursor = Cursor::new(memory, 16);
329 /// ```
330 ///
331 /// **Note**: It's recommended to initialize the text buffer with
332 /// [`Node::default`](crate::Node::default) to evict unexpected behaviors.
333 pub fn new(root: Rc<Node>, capacity: usize) -> Self {
334 Self {
335 buffer: VecDeque::with_capacity(capacity),
336 root,
337 }
338 }
339
340 /// Enters a character in the sequence and returns his corresponding out.
341 ///
342 /// Permits to simulate the user typing in the input method engine.
343 /// For each character entered, the cursor will move through the text buffer in looking of the
344 /// corresponding sequence. If the sequence is got (end on a value), his value will be returned.
345 ///
346 /// # Example
347 ///
348 /// ```
349 /// use afrim_memory::{Cursor, Node};
350 /// use std::rc::Rc;
351 ///
352 /// let text_buffer = Node::default();
353 /// text_buffer.insert(vec!['o', 'e'], "œ".to_owned());
354 /// let memory = Rc::new(text_buffer);
355 ///
356 /// let mut cursor = Cursor::new(memory, 16);
357 /// // let input= "coeur";
358 /// assert_eq!(cursor.hit('c'), None);
359 /// assert_eq!(cursor.hit('o'), None);
360 /// assert_eq!(cursor.hit('e'), Some("œ".to_owned()));
361 /// assert_eq!(cursor.hit('u'), None);
362 /// assert_eq!(cursor.hit('r'), None);
363 ///
364 /// assert_eq!(cursor.to_sequence(), vec!['\0', 'c', '\0', 'o', 'e', '\0', 'u', '\0', 'r']);
365 /// ```
366 ///
367 /// **Note**:
368 /// - The `\0` at the index 0, marks the beginning of a new sequence and the end of a
369 /// old. It also represents the root node.
370 /// - A character don't necessary need to be in the text buffer. The cursor will create a
371 /// tempory node to represent it in his internal memory. All characters not present in the text
372 /// buffer will be at the same depth that the root node.
373 pub fn hit(&mut self, character: char) -> Option<String> {
374 let node = self
375 .buffer
376 .iter()
377 .last()
378 .and_then(|node| node.goto(character))
379 .or_else(|| {
380 // We end the current sequence
381 self.insert(Rc::new(Node::default()));
382 // and start a new one
383 self.root.goto(character)
384 })
385 .unwrap_or_else(|| Rc::new(Node::new(character, 0)));
386
387 let out = node.take();
388 self.insert(node);
389
390 out
391 }
392
393 fn insert(&mut self, node: Rc<Node>) {
394 if self.buffer.len() == self.buffer.capacity() {
395 self.buffer.pop_front();
396 }
397 self.buffer.push_back(node);
398 }
399
400 /// Removes the last node and returns his corresponding out.
401 /// Or simplily, undo the previous hit.
402 ///
403 /// Useful to simulate a backspace operation.
404 ///
405 /// # Example
406 ///
407 /// ```
408 /// use afrim_memory::{Cursor, Node};
409 /// use std::rc::Rc;
410 ///
411 /// let text_buffer = Node::default();
412 /// text_buffer.insert(vec!['o', 'e'], "œ".to_owned());
413 /// let memory = Rc::new(text_buffer);
414 ///
415 /// let mut cursor = Cursor::new(memory, 16);
416 /// // let input = "coeur";
417 /// assert_eq!(cursor.hit('c'), None);
418 /// assert_eq!(cursor.hit('o'), None);
419 /// assert_eq!(cursor.hit('e'), Some("œ".to_owned()));
420 /// assert_eq!(cursor.hit('u'), None);
421 /// assert_eq!(cursor.hit('r'), None);
422 ///
423 /// // Undo
424 /// assert_eq!(cursor.undo(), None);
425 /// assert_eq!(cursor.undo(), None);
426 /// assert_eq!(cursor.undo(), Some("œ".to_owned()));
427 /// assert_eq!(cursor.undo(), None);
428 /// assert_eq!(cursor.undo(), None);
429 ///
430 /// assert_eq!(cursor.to_sequence(), vec!['\0']);
431 /// ```
432 ///
433 /// **Note**: Look at the `\0` at the end. It represents the root node, and the start of a
434 /// new sequence. Even if you remove it until obtain an empty buffer, the cursor will add it
435 /// before each new sequence. You can considere it as a delimiter between two sequences. But if
436 /// you want clear or verify if the buffer is empty, you can use [Cursor::clear](crate::Cursor::clear) or [Cursor::is_empty](crate::Cursor::is_empty).
437 pub fn undo(&mut self) -> Option<String> {
438 let node = self.buffer.pop_back();
439
440 node.and_then(|node| {
441 if node.key == '\0' {
442 self.undo()
443 } else {
444 node.take()
445 }
446 })
447 }
448
449 /// Resumes at the current sequence.
450 ///
451 /// By removing the end marker of the current sequence, this method allows the cursor
452 /// to continue using it as an ongoing sequence.
453 ///
454 /// # Example
455 ///
456 /// ```
457 /// use afrim_memory::{Cursor, Node};
458 /// use std::rc::Rc;
459 ///
460 /// let text_buffer = Node::default();
461 /// text_buffer.insert(vec!['c', '_'], "ç".to_owned());
462 /// let memory = Rc::new(text_buffer);
463 ///
464 /// let mut cursor = Cursor::new(memory, 8);
465 /// cursor.hit('c');
466 /// cursor.hit('c');
467 /// cursor.undo();
468 /// assert_eq!(cursor.to_sequence(), vec!['\0', 'c', '\0']);
469 ///
470 /// // We want the next hit to reuse this sequence.
471 /// cursor.resume();
472 /// assert_eq!(cursor.to_sequence(), vec!['\0', 'c']);
473 /// assert_eq!(cursor.hit('_'), Some("ç".to_owned()).to_owned());
474 /// ```
475 pub fn resume(&mut self) {
476 if self
477 .buffer
478 .iter()
479 .last()
480 .map_or(false, |node| node.is_root())
481 {
482 self.buffer.pop_back();
483 }
484 }
485
486 /// Returns the current state of the cursor.
487 ///
488 /// Permits to know the current position in the memory and also the last hit.
489 ///
490 /// # Example
491 ///
492 /// ```
493 /// use afrim_memory::{Cursor, Node};
494 /// use std::rc::Rc;
495 ///
496 /// let text_buffer = Node::default();
497 /// text_buffer.insert(vec!['o', '/'], "ø".to_owned());
498 /// let memory = Rc::new(text_buffer);
499 ///
500 /// let mut cursor = Cursor::new(memory, 8);
501 /// // The cursor starts always at the root node.
502 /// assert_eq!(cursor.state(), (None, 0, '\0'));
503 /// cursor.hit('o');
504 /// assert_eq!(cursor.state(), (None, 1, 'o'));
505 /// ```
506 pub fn state(&self) -> (Option<String>, usize, char) {
507 self.buffer
508 .iter()
509 .last()
510 .map(|n| (n.take(), n.depth, n.key))
511 .unwrap_or_default()
512 }
513
514 /// Returns the current sequence in the cursor.
515 ///
516 /// It's always useful to know what is inside the memory of the cursor for debugging / logging.
517 /// The
518 ///
519 /// # Example
520 ///
521 /// ```
522 /// use afrim_memory::{Cursor, Node};
523 /// use std::rc::Rc;
524 ///
525 /// let text_buffer = Node::default();
526 /// text_buffer.insert(vec!['.', '.', 'z'], "z̈".to_owned());
527 /// let memory = Rc::new(text_buffer);
528 ///
529 /// let mut cursor = Cursor::new(memory, 8);
530 /// "z..z".chars().for_each(|c| { cursor.hit(c); });
531 ///
532 /// assert_eq!(cursor.to_sequence(), vec!['\0', 'z', '\0', '.', '.', 'z']);
533 /// ```
534 pub fn to_sequence(&self) -> Vec<char> {
535 self.buffer.iter().map(|node| node.key).collect()
536 }
537
538 /// Clear the memory of the cursor.
539 ///
540 /// In clearing the internal buffer, all the tracking information will be lost.
541 ///
542 /// # Example
543 ///
544 /// ```
545 /// use afrim_memory::{Cursor, Node};
546 /// use std::rc::Rc;
547 ///
548 /// let text_buffer = Node::default();
549 /// let memory = Rc::new(text_buffer);
550 /// let mut cursor = Cursor::new(memory, 8);
551 ///
552 /// "hello".chars().for_each(|c| { cursor.hit(c); });
553 /// assert!(!cursor.is_empty());
554 ///
555 /// cursor.clear();
556 /// assert!(cursor.is_empty());
557 /// ```
558 pub fn clear(&mut self) {
559 self.buffer.clear();
560 }
561
562 /// Verify if the cursor is empty.
563 ///
564 /// # Example
565 ///
566 /// ```
567 /// use afrim_memory::{Cursor, Node};
568 /// use std::rc::Rc;
569 ///
570 /// let text_buffer = Node::default();
571 /// let memory = Rc::new(text_buffer);
572 ///
573 /// let mut cursor = Cursor::new(memory, 8);
574 /// assert!(cursor.is_empty());
575 ///
576 /// cursor.hit('a');
577 /// assert!(!cursor.is_empty());
578 /// ```
579 pub fn is_empty(&self) -> bool {
580 return self.buffer.iter().filter(|c| c.key != '\0').count() == 0;
581 }
582}
583
584#[cfg(test)]
585mod tests {
586 #[test]
587 fn test_node() {
588 use crate::Node;
589
590 let root = Node::default();
591
592 assert!(root.is_root());
593
594 root.insert(vec!['a', 'f'], "ɑ".to_owned());
595 root.insert(vec!['a', 'f', '1'], "ɑ̀".to_owned());
596
597 assert!(root.goto('a').is_some());
598 assert!(!root.goto('a').unwrap().is_root());
599 assert!(root.goto('b').is_none());
600
601 let node = root.goto('a').and_then(|e| e.goto('f'));
602 assert_eq!(node.as_ref().unwrap().take(), Some("ɑ".to_owned()));
603
604 let node = node.and_then(|e| e.goto('1'));
605 assert_eq!(node.as_ref().unwrap().take(), Some("ɑ̀".to_owned()));
606 }
607
608 #[test]
609 fn test_cursor() {
610 use crate::{utils, Cursor};
611 use std::rc::Rc;
612
613 macro_rules! hit {
614 ( $cursor:ident $( $c:expr ),* ) => (
615 $( $cursor.hit($c); )*
616 );
617 }
618
619 macro_rules! undo {
620 ( $cursor:ident $occ:expr ) => {
621 (0..$occ).into_iter().for_each(|_| {
622 $cursor.undo();
623 });
624 };
625 }
626
627 let data = include_str!("../data/sample.txt");
628 let root = utils::build_map(utils::load_data(data));
629
630 let mut cursor = Cursor::new(Rc::new(root), 8);
631
632 assert_eq!(cursor.state(), (None, 0, '\0'));
633
634 hit!(cursor '2', 'i', 'a', 'f');
635 assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'i', 'a', 'f']);
636
637 assert_eq!(cursor.state(), (Some("íɑ́".to_owned()), 4, 'f'));
638
639 undo!(cursor 1);
640 assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'i', 'a']);
641
642 // Cursor::resume
643 hit!(cursor 'x');
644 assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'i', 'a', '\0', 'x']);
645 undo!(cursor 1);
646 cursor.resume();
647 hit!(cursor 'f');
648 assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'i', 'a', 'f']);
649
650 undo!(cursor 2);
651 cursor.hit('e');
652 assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'i', 'e']);
653
654 undo!(cursor 2);
655 hit!(cursor 'o', 'o');
656 assert_eq!(cursor.to_sequence(), vec!['\0', '2', 'o', 'o']);
657
658 undo!(cursor 3);
659 assert_eq!(cursor.to_sequence(), vec!['\0']);
660
661 hit!(cursor '2', '2', 'u', 'a');
662 assert_eq!(
663 cursor.to_sequence(),
664 vec!['\0', '\0', '2', '\0', '2', 'u', 'a']
665 );
666 undo!(cursor 4);
667 assert_eq!(cursor.to_sequence(), vec!['\0', '\0']);
668 assert!(cursor.is_empty());
669 undo!(cursor 1);
670 assert_eq!(cursor.to_sequence(), vec![]);
671
672 hit!(
673 cursor
674 'a', 'a', '2', 'a', 'e', 'a', '2', 'f', 'a',
675 '2', '2', 'x', 'x', '2', 'i', 'a', '2', '2', '_', 'f',
676 '2', 'a', '2', 'a', '_'
677 );
678 assert_eq!(
679 cursor.to_sequence(),
680 vec!['f', '\0', '2', 'a', '\0', '2', 'a', '_']
681 );
682
683 assert_eq!(
684 format!("{:?}", cursor),
685 format!("{:?}", cursor.to_sequence())
686 );
687
688 cursor.clear();
689 assert_eq!(cursor.to_sequence(), vec![]);
690 }
691}