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}