y_octo/doc/
batch.rs

1use super::*;
2
3#[derive(Debug, PartialEq)]
4pub struct Batch {
5    doc: Doc,
6    before_state: StateVector,
7    after_state: StateVector,
8    changed: HashMap<YTypeRef, Vec<SmolStr>>,
9}
10
11impl Batch {
12    pub fn new(doc: Doc) -> Self {
13        let current_state = doc.get_state_vector();
14
15        Batch {
16            doc,
17            before_state: current_state.clone(),
18            after_state: current_state,
19            changed: HashMap::new(),
20        }
21    }
22
23    pub fn with_batch<T, F>(&mut self, f: F) -> T
24    where
25        F: FnOnce(Doc) -> T,
26    {
27        let ret = f(self.doc.clone());
28        for (k, v) in self.doc.get_changed() {
29            self.changed.entry(k).or_default().extend(v.iter().cloned());
30        }
31        ret
32    }
33}
34
35pub fn batch_commit<T, F>(mut doc: Doc, f: F) -> Option<T>
36where
37    F: FnOnce(Doc) -> T,
38{
39    // Initialize batch cleanups list
40    let mut batch_cleanups = vec![];
41
42    // Initial call and result initialization
43    let mut initial_call = false;
44
45    {
46        if doc.batch.is_none() {
47            initial_call = true;
48
49            // Start a new batch
50            let batch = Batch::new(doc.clone());
51            doc.batch = Somr::new(batch);
52            batch_cleanups.push(doc.batch.clone());
53        }
54    }
55
56    let batch = doc.batch.get_mut()?;
57    let result = Some(batch.with_batch(f));
58
59    if initial_call
60        && let Some(current_batch) = doc.batch.get()
61        && Some(current_batch) == batch_cleanups[0].get()
62    {
63        // Process observer calls and perform cleanup if this is the initial call
64        cleanup_batches(&mut batch_cleanups);
65        doc.batch.swap_take();
66    }
67
68    result
69}
70
71fn cleanup_batches(batch_cleanups: &mut Vec<Somr<Batch>>) {
72    for batch in batch_cleanups.drain(..) {
73        if let Some(batch) = batch.get() {
74            println!("changed: {:?}", batch.changed);
75        } else {
76            panic!("Batch not initialized");
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84
85    #[test]
86    fn should_get_changed_items() {
87        loom_model!({
88            let doc = DocOptions::new().with_client_id(1).build();
89
90            batch_commit(doc.clone(), |d| {
91                let mut arr = d.get_or_create_array("arr").unwrap();
92                let mut text = d.create_text().unwrap();
93                let mut map = d.create_map().unwrap();
94
95                batch_commit(doc.clone(), |_| {
96                    arr.insert(0, Value::from(text.clone())).unwrap();
97                    arr.insert(1, Value::from(map.clone())).unwrap();
98                });
99
100                batch_commit(doc.clone(), |_| {
101                    text.insert(0, "hello world").unwrap();
102                    text.remove(5, 6).unwrap();
103                });
104
105                batch_commit(doc.clone(), |_| {
106                    map.insert("key".into(), 123).unwrap();
107                });
108
109                batch_commit(doc.clone(), |_| {
110                    map.remove("key");
111                });
112
113                batch_commit(doc.clone(), |_| {
114                    arr.remove(0, 1).unwrap();
115                });
116            });
117        });
118    }
119}