use std::ops::RangeBounds; use nom::{bytes::complete::tag, Compare, InputTake, Slice}; fn main() { let input = "this is my test input".to_owned(); let wrapped_input = WrappedInput::new(input.as_str()); let output = tag::<_, _, (_, nom::error::ErrorKind)>("this")(wrapped_input).unwrap(); println!("{:#?}", output); } #[derive(Debug)] struct WrappedInput<'s> { full_source: &'s str, start: usize, end: usize, //exclusive preceding_line_break: Option, } impl<'s> WrappedInput<'s> { /// Returns a wrapped string that keeps track of values we need for parsing org-mode. /// /// Only call this on the full original string. Calling this on a substring can result in invalid values. pub fn new(input: &'s str) -> Self { WrappedInput { full_source: input, start: 0, end: input.len(), preceding_line_break: None, } } } impl<'s> InputTake for WrappedInput<'s> { fn take(&self, count: usize) -> Self { self.slice(..count) } fn take_split(&self, count: usize) -> (Self, Self) { (self.slice(count..), self.slice(..count)) } } impl<'s, O: Into<&'s str>> Compare for WrappedInput<'s> { fn compare(&self, t: O) -> nom::CompareResult { (&self.full_source[self.start..self.end]).compare(t.into()) } fn compare_no_case(&self, t: O) -> nom::CompareResult { (&self.full_source[self.start..self.end]).compare_no_case(t.into()) } } impl<'s> From<&'s str> for WrappedInput<'s> { fn from(value: &'s str) -> Self { WrappedInput::new(value) } } impl<'s> From<&WrappedInput<'s>> for &'s str { fn from(value: &WrappedInput<'s>) -> Self { &value.full_source[value.start..value.end] } } impl<'s, R> Slice for WrappedInput<'s> where R: RangeBounds, { fn slice(&self, range: R) -> Self { let new_start = match range.start_bound() { std::ops::Bound::Included(idx) => self.start + idx, std::ops::Bound::Excluded(idx) => self.start + idx - 1, std::ops::Bound::Unbounded => self.start, }; let new_end = match range.end_bound() { std::ops::Bound::Included(idx) => self.start + idx, std::ops::Bound::Excluded(idx) => self.start + idx - 1, std::ops::Bound::Unbounded => self.start, }; // TODO: calculate updated values for WrappedInput WrappedInput { full_source: self.full_source, start: new_start, end: new_end, preceding_line_break: None, } } } impl<'s> std::fmt::Display for WrappedInput<'s> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { Into::<&str>::into(self).fmt(f) } }