Use custom Arc backed input
Signed-off-by: Marcel Müller <neikos@neikos.email>
This commit is contained in:
parent
1ea15f0e49
commit
1ee7611981
6 changed files with 283 additions and 77 deletions
194
src/input.rs
Normal file
194
src/input.rs
Normal file
|
|
@ -0,0 +1,194 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue