use std::ops::Deref; use std::ops::Range; use std::sync::Arc; use winnow::stream::Compare; use winnow::stream::FindSlice; use winnow::stream::Offset; use winnow::stream::Stream; use winnow::stream::StreamIsPartial; #[derive(Clone, PartialEq, Eq)] pub struct NomoInput { backing: Arc, range: Range, } impl NomoInput { pub fn from_parts(backing: Arc, range: Range) -> NomoInput { NomoInput { backing, range } } pub fn into_parts(self) -> (Arc, Range) { (self.backing, self.range) } pub fn get_range(&self) -> Range { self.range.clone() } } #[derive(Debug, Clone)] pub struct NomoInputCheckpoint { range: Range, } impl std::fmt::Debug for NomoInput { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?} ({:?})", self.as_str(), self.range) } } impl From for NomoInput { fn from(value: String) -> Self { let range = 0..value.len(); let backing = Arc::from(value); NomoInput { backing, range } } } impl From<&str> for NomoInput { fn from(value: &str) -> Self { let backing = Arc::from(value.to_string()); let range = 0..value.len(); NomoInput { backing, range } } } impl FindSlice<&str> for NomoInput { fn find_slice(&self, substr: &str) -> Option> { self.as_str().find_slice(substr) } } impl Compare<&str> for NomoInput { fn compare(&self, t: &str) -> winnow::stream::CompareResult { self.as_str().compare(t) } } impl Deref for NomoInput { type Target = str; fn deref(&self) -> &Self::Target { &self.backing[self.range.clone()] } } impl NomoInput { pub fn as_str(&self) -> &str { self.deref() } } impl Offset for NomoInputCheckpoint { fn offset_from(&self, start: &Self) -> usize { self.range.start - start.range.start } } impl Offset for NomoInput { fn offset_from(&self, start: &NomoInputCheckpoint) -> usize { self.range.start - start.range.start } } impl Offset for NomoInput { fn offset_from(&self, start: &Self) -> usize { self.as_str().offset_from(&start.as_str()) } } pub struct NomoInputIter { idx: usize, input: NomoInput, } impl Iterator for NomoInputIter { type Item = (usize, char); fn next(&mut self) -> Option { let val = self.input.as_str().char_indices().nth(self.idx); self.idx += 1; val } } impl StreamIsPartial for NomoInput { type PartialState = (); fn complete(&mut self) -> Self::PartialState { // Always complete } fn restore_partial(&mut self, _state: Self::PartialState) {} fn is_partial_supported() -> bool { false } } impl Stream for NomoInput { type Token = char; type Slice = NomoInput; type IterOffsets = NomoInputIter; type Checkpoint = NomoInputCheckpoint; fn iter_offsets(&self) -> Self::IterOffsets { NomoInputIter { idx: 0, input: self.clone(), } } fn eof_offset(&self) -> usize { self.len() } fn next_token(&mut self) -> Option { let c = self.chars().next()?; self.range.start += c.len_utf8(); Some(c) } fn peek_token(&self) -> Option { self.chars().next() } fn offset_for

(&self, predicate: P) -> Option where P: Fn(Self::Token) -> bool, { self.as_str().offset_for(predicate) } fn offset_at(&self, tokens: usize) -> Result { self.as_str().offset_at(tokens) } fn next_slice(&mut self, offset: usize) -> Self::Slice { let mut next = self.clone(); self.range.start += offset; next.range.end = self.range.start; next } fn peek_slice(&self, offset: usize) -> Self::Slice { let mut next = self.clone(); next.range.end = self.range.start + offset; next } fn checkpoint(&self) -> Self::Checkpoint { NomoInputCheckpoint { range: self.get_range(), } } fn reset(&mut self, checkpoint: &Self::Checkpoint) { self.range = checkpoint.range.clone(); } fn raw(&self) -> &dyn core::fmt::Debug { self } fn trace(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { f.write_str(self.as_str()) } } #[cfg(test)] mod tests { use winnow::stream::Stream; use crate::input::NomoInput; #[test] fn check_stream_impl() { let mut stream = NomoInput::from("checking"); let checkpoint = stream.checkpoint(); assert_eq!(stream.peek_token(), Some('c')); assert_eq!(stream.next_token(), Some('c')); let next_slice = stream.next_slice(4); assert_eq!(next_slice.as_str(), "heck"); assert_eq!(stream.peek_token(), Some('i')); stream.reset(&checkpoint); assert_eq!(stream.peek_token(), Some('c')); let peek = stream.peek_slice(4); assert_eq!(peek.as_str(), "chec"); let eof_offset = stream.eof_offset(); assert_eq!(eof_offset, 8); } }