Skip to main content

clankerdiff_markdown/
stream.rs

1use std::{
2    ops::Range,
3    sync::atomic::{AtomicU64, Ordering},
4};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct MarkdownStreamUpdate {
8    pub revision: u64,
9    pub changed_source: Range<usize>,
10    pub reset: bool,
11    pub finished: bool,
12}
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub struct MarkdownStreamIdentity(u64);
16
17impl Default for MarkdownStreamIdentity {
18    fn default() -> Self {
19        static NEXT: AtomicU64 = AtomicU64::new(0);
20        Self(NEXT.fetch_add(1, Ordering::Relaxed))
21    }
22}
23
24#[derive(Debug, Default)]
25pub struct MarkdownStream {
26    source: String,
27    revision: u64,
28    identity: MarkdownStreamIdentity,
29    finished: bool,
30}
31
32impl Clone for MarkdownStream {
33    fn clone(&self) -> Self {
34        Self {
35            source: self.source.clone(),
36            revision: self.revision,
37            identity: MarkdownStreamIdentity::default(),
38            finished: self.finished,
39        }
40    }
41}
42
43impl MarkdownStream {
44    #[must_use]
45    pub fn new() -> Self {
46        Self::default()
47    }
48
49    pub fn push(&mut self, chunk: &str) -> MarkdownStreamUpdate {
50        if chunk.is_empty() && !self.finished {
51            return self.unchanged();
52        }
53        let start = self.source.len();
54        let reset = std::mem::take(&mut self.finished);
55        self.source.push_str(chunk);
56        self.update(start..self.source.len(), reset)
57    }
58
59    pub fn replace(&mut self, source: impl Into<String>) -> MarkdownStreamUpdate {
60        self.source = source.into();
61        self.finished = false;
62        self.update(0..self.source.len(), true)
63    }
64
65    pub fn finish(&mut self) -> MarkdownStreamUpdate {
66        if self.finished {
67            return self.unchanged();
68        }
69        self.finished = true;
70        self.update(self.source.len()..self.source.len(), false)
71    }
72
73    #[must_use]
74    pub fn source(&self) -> &str {
75        &self.source
76    }
77
78    #[must_use]
79    pub const fn revision(&self) -> u64 {
80        self.revision
81    }
82
83    #[must_use]
84    pub const fn identity(&self) -> MarkdownStreamIdentity {
85        self.identity
86    }
87
88    #[must_use]
89    pub const fn is_finished(&self) -> bool {
90        self.finished
91    }
92
93    fn update(&mut self, changed_source: Range<usize>, reset: bool) -> MarkdownStreamUpdate {
94        self.revision = self.revision.wrapping_add(1);
95        MarkdownStreamUpdate {
96            revision: self.revision,
97            changed_source,
98            reset,
99            finished: self.finished,
100        }
101    }
102
103    fn unchanged(&self) -> MarkdownStreamUpdate {
104        MarkdownStreamUpdate {
105            revision: self.revision,
106            changed_source: self.source.len()..self.source.len(),
107            reset: false,
108            finished: self.finished,
109        }
110    }
111}