interactive_parse/
undo.rs

1use std::{
2    cell::Cell,
3    io::{stdout, Write},
4};
5
6use crossterm::{
7    cursor::MoveToPreviousLine,
8    queue,
9    terminal::{Clear, ClearType},
10};
11use log::debug;
12
13use crate::error::{SchemaError, SchemaResult};
14
15pub(crate) trait Undo {
16    type Output;
17    fn undo(self, current_depth: &Cell<u16>) -> SchemaResult<Self::Output>;
18}
19
20impl<T> Undo for Option<T> {
21    type Output = T;
22    fn undo(self, current_depth: &Cell<u16>) -> SchemaResult<Self::Output> {
23        let current_depth_val = current_depth.get();
24        match self {
25            Some(value) => {
26                debug!("Depth {} -> {}", current_depth_val, current_depth_val + 1);
27                current_depth.set(current_depth_val + 1);
28                Ok(value)
29            }
30            None => {
31                debug!("Undo at depth {}", current_depth_val);
32                Err(SchemaError::Undo {
33                    depth: current_depth_val,
34                })
35            }
36        }
37    }
38}
39
40pub(crate) trait CatchUndo {
41    type Output;
42    fn catch_undo(self, current_depth: &Cell<u16>) -> SchemaResult<Self::Output>;
43}
44
45impl<T> CatchUndo for Result<T, SchemaError> {
46    type Output = T;
47    fn catch_undo(self, current_depth: &Cell<u16>) -> SchemaResult<Self::Output> {
48        let current_depth_val = current_depth.get();
49        match self {
50            Ok(value) => {
51                debug!("Depth {} -> {}", current_depth_val, current_depth_val + 1);
52                current_depth.set(current_depth_val + 1);
53                Ok(value)
54            }
55            Err(SchemaError::Undo { depth }) => {
56                debug!("Undo at depth {}", current_depth_val);
57                current_depth.set(depth);
58                Err(SchemaError::Undo { depth })
59            }
60            Err(err) => Err(err),
61        }
62    }
63}
64
65pub(crate) trait RecurseIter<T, U>
66where
67    Self: Iterator<Item = T> + Clone,
68    T: Clone,
69    U: Clone,
70{
71    fn recurse_iter<F: FnMut(T) -> SchemaResult<RecurseLoop<U>> + Clone>(
72        self,
73        current_depth: &Cell<u16>,
74        f: F,
75    ) -> SchemaResult<Vec<U>>;
76}
77
78impl<I, T, U> RecurseIter<T, U> for I
79where
80    I: Iterator<Item = T> + Clone,
81    T: Clone,
82    U: Clone,
83{
84    fn recurse_iter<F: FnMut(T) -> SchemaResult<RecurseLoop<U>> + Clone>(
85        self,
86        current_depth: &Cell<u16>,
87        f: F,
88    ) -> SchemaResult<Vec<U>> {
89        let mut acc = Vec::new();
90        recurse_loop(self, current_depth, &mut acc, f)?;
91        Ok(acc)
92    }
93}
94
95pub(crate) enum RecurseLoop<T> {
96    Continue(T),
97    Return(Option<T>),
98}
99
100pub(crate) fn recurse_loop<
101    I: Iterator<Item = T> + Clone,
102    T: Clone,
103    U: Clone,
104    F: Clone + FnMut(T) -> SchemaResult<RecurseLoop<U>>,
105>(
106    mut iter: I,
107    current_depth: &Cell<u16>,
108    acc: &mut Vec<U>,
109    mut f: F,
110) -> SchemaResult<()> {
111    let iter_checkpoint = iter.clone();
112    let acc_checkpoint = acc.clone();
113    let depth_checkpoint = current_depth.get();
114    let Some(item) = iter.next() else {
115        return Ok(());
116    };
117
118    let val = match f(item) {
119        Ok(RecurseLoop::Continue(item)) => {
120            acc.push(item);
121            recurse_loop(iter, current_depth, acc, f.clone())
122        }
123        Ok(RecurseLoop::Return(item)) => {
124            if let Some(item) = item {
125                acc.push(item);
126            }
127            Ok(())
128        }
129        Err(err) => Err(err),
130    };
131
132    match val {
133        Err(SchemaError::Undo { depth }) => {
134            if depth > depth_checkpoint {
135                current_depth.set(depth_checkpoint);
136                *acc = acc_checkpoint;
137                clear_lines(depth - depth_checkpoint + 1);
138                recurse_loop(iter_checkpoint, current_depth, acc, f)
139            } else {
140                Err(SchemaError::Undo { depth })
141            }
142        }
143        other => other,
144    }
145}
146
147pub(crate) fn clear_lines(n: u16) {
148    let mut stdout = stdout();
149    queue!(
150        stdout,
151        MoveToPreviousLine(n),
152        Clear(ClearType::FromCursorDown)
153    )
154    .unwrap();
155    stdout.flush().unwrap();
156}