source_span/buffer/lazy.rs
1use std::cell::RefCell;
2
3use crate::{Metrics, Position, Span};
4
5/// Lazy string buffer that fills up on demand, can be iterated and indexed by
6/// character position.
7///
8/// The `SourceBuffer` wraps aroung a `char` iterator. It can be itself used as
9/// a `char` iterator, or as a `SourceBuffer` to access an arbitrary fragment of
10/// the input source stream.
11pub struct SourceBuffer<E, I: Iterator<Item = Result<char, E>>, M: Metrics> {
12 p: RefCell<Inner<E, I>>,
13
14 /// Metrics used.
15 metrics: M,
16}
17
18struct Inner<E, I: Iterator<Item = Result<char, E>>> {
19 /// Input source `char` stream.
20 input: I,
21
22 /// SourceBuffer error state.
23 error: Option<E>,
24
25 /// Buffer data.
26 data: Vec<char>,
27
28 /// Lines index.
29 ///
30 /// Contains the index of the first character of each line.
31 lines: Vec<usize>,
32
33 /// Span of the buffer.
34 span: Span,
35}
36
37impl<E, I: Iterator<Item = Result<char, E>>> Inner<E, I> {
38 /// Read the next line from the input stream and add it to the buffer.
39 /// Returns `true` if a new line has been added. Returns `false` if the
40 /// source stream is done.
41 fn read_line<M: Metrics>(&mut self, metrics: &M) -> bool {
42 if self.error.is_none() {
43 let line = self.span.end().line;
44 while line == self.span.end().line {
45 match self.input.next() {
46 Some(Ok(c)) => {
47 self.data.push(c);
48 self.span.push(c, metrics);
49 }
50 Some(Err(e)) => {
51 self.error = Some(e);
52 return false;
53 }
54 None => return false,
55 }
56 }
57
58 // register the next line index.
59 self.lines.push(self.data.len());
60
61 true
62 } else {
63 false
64 }
65 }
66
67 /// Get the index of the char at the given cursor position if it is in the
68 /// buffer. If it is not in the buffer but after the buffered content,
69 /// the input stream will be read until the buffer span includes the
70 /// given position.
71 ///
72 /// Returns `None` if the given position if previous to the buffer start
73 /// positions, if the source stream ends before the given position, or
74 /// if the line at the given position is shorter than the given position
75 /// column.
76 fn index_at<M: Metrics>(&mut self, pos: Position, metrics: &M) -> Result<Option<usize>, E> {
77 if pos < self.span.start() {
78 Ok(None)
79 } else {
80 while pos >= self.span.end() && self.read_line(metrics) {}
81
82 if pos >= self.span.end() {
83 let mut error = None;
84 std::mem::swap(&mut error, &mut self.error);
85 match error {
86 Some(e) => Err(e),
87 None => Ok(None),
88 }
89 } else {
90 // line index relative to the first line of the buffer.
91 let relative_line = pos.line - self.span.start().line;
92 // get the index of the char of the begining of the line in the buffer.
93 let mut i = self.lines[relative_line];
94 // place a virtual cursor at the begining of the target line.
95 let mut cursor = Position::new(pos.line, 0);
96
97 while cursor < pos {
98 cursor = cursor.next(self.data[i], metrics);
99 i += 1;
100 }
101
102 if cursor == pos {
103 // found it!
104 Ok(Some(i))
105 } else {
106 // the position does not exist in the buffer.
107 Ok(None)
108 }
109 }
110 }
111 }
112
113 /// Get the character at the given index.
114 ///
115 /// If it is not in the buffer but after the buffered content, the input
116 /// stream will be read until the buffer span includes the given
117 /// position. Returns `None` if the source stream ends before the given
118 /// position.
119 fn get<M: Metrics>(&mut self, i: usize, metrics: &M) -> Result<Option<char>, E> {
120 while i >= self.data.len() && self.read_line(metrics) {}
121
122 if i >= self.data.len() {
123 let mut error = None;
124 std::mem::swap(&mut error, &mut self.error);
125 match error {
126 Some(e) => Err(e),
127 None => Ok(None),
128 }
129 } else {
130 Ok(Some(self.data[i]))
131 }
132 }
133}
134
135impl<E, I: Iterator<Item = Result<char, E>>, M: Metrics> SourceBuffer<E, I, M> {
136 /// Create a new empty buffer starting at the given position.
137 pub fn new(input: I, position: Position, metrics: M) -> Self {
138 Self {
139 p: RefCell::new(Inner {
140 input,
141 error: None,
142 data: Vec::new(),
143 lines: vec![0],
144 span: position.into(),
145 }),
146 metrics,
147 }
148 }
149
150 /// Get the metrics used by the source buffer to map every character.
151 pub fn metrics(&self) -> &M { &self.metrics }
152
153 /// Get the span of the entire buffered data.
154 pub fn span(&self) -> Span { self.p.borrow().span }
155
156 /// Get the index of the char at the given cursor position if it is in the
157 /// buffer. If it is not in the buffer but after the buffered content,
158 /// the input stream will be read until the buffer span includes the
159 /// given position.
160 ///
161 /// Returns `None` if the given position if previous to the buffer start
162 /// positions, if the source stream ends before the given position, or
163 /// if the line at the given position is shorter than the given position
164 /// column.
165 pub fn index_at(&self, pos: Position) -> Result<Option<usize>, E> {
166 self.p.borrow_mut().index_at(pos, &self.metrics)
167 }
168
169 /// Get the char at the given position if it is in the buffer.
170 /// If it is not in the buffer yet, the input stream will be pulled until
171 /// the buffer span includes the given position.
172 ///
173 /// Returns `None` if the given position is out of range, if the source
174 /// stream ends before the given position, or if the line at the given
175 /// position is shorter than the given position column.
176 pub fn at(&self, pos: Position) -> Result<Option<char>, E> {
177 match self.index_at(pos) {
178 Ok(Some(i)) => self.p.borrow_mut().get(i, &self.metrics),
179 Ok(None) => Ok(None),
180 Err(e) => Err(e)
181 }
182 }
183
184 /// Get the character at the given index.
185 ///
186 /// If it is not in the buffer but after the buffered content, the input
187 /// stream will be read until the buffer span includes the given
188 /// position. Returns `None` if the source stream ends before the given
189 /// position.
190 pub fn get(&self, i: usize) -> Result<Option<char>, E> { self.p.borrow_mut().get(i, &self.metrics) }
191
192 /// Returns an iterator through the characters of the buffer from the
193 /// begining of it.
194 ///
195 /// When it reaches the end of the buffer, the buffer will start reading
196 /// from the source stream.
197 pub fn iter(&self) -> Iter<E, I, M> {
198 Iter {
199 buffer: self,
200 i: Some(Ok(0)),
201 pos: self.p.borrow().span.start(),
202 end: Position::end(),
203 }
204 }
205
206 /// Returns an iterator through the characters of the buffer from the given
207 /// position.
208 ///
209 /// If the input position precedes the buffer start position, then it will
210 /// start from the buffer start position.
211 /// When it reaches the end of the buffer, the buffer will start reading
212 /// from the source stream.
213 pub fn iter_from(&self, pos: Position) -> Iter<E, I, M> {
214 let start = self.p.borrow().span.start();
215 let pos = std::cmp::max(start, pos);
216
217 Iter {
218 buffer: self,
219 i: self.index_at(pos).transpose(),
220 pos,
221 end: Position::end(),
222 }
223 }
224
225 /// Returns an iterator through the characters of the buffer in the given
226 /// span.
227 ///
228 /// If the input start position precedes the buffer start position, then it
229 /// will start from the buffer start position.
230 /// When it reaches the end of the buffer, the buffer will start reading
231 /// from the source stream.
232 pub fn iter_span(&self, span: Span) -> Iter<E, I, M> {
233 let start = self.p.borrow().span.start();
234 let pos = std::cmp::max(start, span.start());
235
236 Iter {
237 buffer: self,
238 i: self.index_at(pos).transpose(),
239 pos,
240 end: span.end(),
241 }
242 }
243}
244
245/// Iterator over the characters of a [`SourceBuffer`].
246///
247/// This iterator is created using the [`SourceBuffer::iter`] method or the
248/// [`SourceBuffer::iter_from`] method. When it reaches the end of the buffer,
249/// the buffer will start reading from the source stream until the stream itself
250/// return `None`.
251pub struct Iter<'b, E, I: 'b + Iterator<Item = Result<char, E>>, M: Metrics> {
252 buffer: &'b SourceBuffer<E, I, M>,
253 i: Option<Result<usize, E>>,
254 pos: Position,
255 end: Position,
256}
257
258impl<'b, E, I: 'b + Iterator<Item = Result<char, E>>, M: Metrics> Iter<'b, E, I, M> {
259 pub fn into_string(self) -> Result<String, E> {
260 let mut string = String::new();
261
262 for c in self {
263 string.push(c?);
264 }
265
266 Ok(string)
267 }
268}
269
270impl<'b, E, I: 'b + Iterator<Item = Result<char, E>>, M: Metrics> Iterator for Iter<'b, E, I, M> {
271 type Item = Result<char, E>;
272
273 fn next(&mut self) -> Option<Result<char, E>> {
274 if self.pos >= self.end {
275 None
276 } else {
277 match &mut self.i {
278 Some(Ok(ref mut i)) => match self.buffer.get(*i) {
279 Ok(Some(c)) => {
280 self.pos = self.pos.next(c, self.buffer.metrics());
281 *i += 1;
282 Some(Ok(c))
283 }
284 Ok(None) => None,
285 Err(e) => Some(Err(e)),
286 },
287 None => None,
288 ref mut i => {
289 let mut new_i = None;
290 std::mem::swap(&mut new_i, i);
291 if let Some(Err(e)) = new_i {
292 Some(Err(e))
293 } else {
294 unreachable!()
295 }
296 }
297 }
298 }
299 }
300}