nomo/src/input.rs
Marcel Müller 1ee7611981 Use custom Arc backed input
Signed-off-by: Marcel Müller <neikos@neikos.email>
2026-03-06 12:42:11 +01:00

194 lines
4.2 KiB
Rust

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 TempleInput {
backing: Arc<str>,
range: Range<usize>,
}
impl std::fmt::Debug for TempleInput {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "\"{}\"", self.as_str())
}
}
impl From<&str> for TempleInput {
fn from(value: &str) -> Self {
let backing = Arc::from(value.to_string());
let range = 0..value.len();
TempleInput { backing, range }
}
}
impl FindSlice<&str> for TempleInput {
fn find_slice(&self, substr: &str) -> Option<core::ops::Range<usize>> {
self.as_str().find_slice(substr)
}
}
impl Compare<&str> for TempleInput {
fn compare(&self, t: &str) -> winnow::stream::CompareResult {
self.as_str().compare(t)
}
}
impl Deref for TempleInput {
type Target = str;
fn deref(&self) -> &Self::Target {
&self.backing[self.range.clone()]
}
}
impl TempleInput {
pub fn as_str(&self) -> &str {
self.deref()
}
}
impl Offset for TempleInput {
fn offset_from(&self, start: &Self) -> usize {
self.as_str().offset_from(&start.as_str())
}
}
pub struct TempleInputIter {
idx: usize,
input: TempleInput,
}
impl Iterator for TempleInputIter {
type Item = (usize, char);
fn next(&mut self) -> Option<Self::Item> {
let val = self.input.as_str().char_indices().nth(self.idx);
self.idx += 1;
val
}
}
impl StreamIsPartial for TempleInput {
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 TempleInput {
type Token = char;
type Slice = TempleInput;
type IterOffsets = TempleInputIter;
type Checkpoint = TempleInput;
fn iter_offsets(&self) -> Self::IterOffsets {
TempleInputIter {
idx: 0,
input: self.clone(),
}
}
fn eof_offset(&self) -> usize {
self.len()
}
fn next_token(&mut self) -> Option<Self::Token> {
let c = self.chars().next()?;
self.range.start += c.len_utf8();
Some(c)
}
fn peek_token(&self) -> Option<Self::Token> {
self.chars().next()
}
fn offset_for<P>(&self, predicate: P) -> Option<usize>
where
P: Fn(Self::Token) -> bool,
{
self.as_str().offset_for(predicate)
}
fn offset_at(&self, tokens: usize) -> Result<usize, winnow::error::Needed> {
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 {
self.clone()
}
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::TempleInput;
#[test]
fn check_stream_impl() {
let mut stream = TempleInput::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);
}
}