use std::ops::RangeBounds; use nom::Compare; use nom::InputIter; use nom::InputLength; use nom::InputTake; use nom::InputTakeAtPosition; use nom::Offset; use nom::Slice; use crate::error::CustomError; use crate::error::MyError; pub(crate) type BracketDepth = i16; #[derive(Copy, Clone)] pub(crate) struct OrgSource<'s> { full_source: &'s str, start: usize, end: usize, // exclusive start_of_line: usize, bracket_depth: BracketDepth, // [] brace_depth: BracketDepth, // {} parenthesis_depth: BracketDepth, // () preceding_character: Option, } impl<'s> std::fmt::Debug for OrgSource<'s> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_tuple("Org") .field(&Into::<&str>::into(self)) .finish() } } impl<'s> OrgSource<'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(crate) fn new(input: &'s str) -> Self { OrgSource { full_source: input, start: 0, end: input.len(), start_of_line: 0, preceding_character: None, bracket_depth: 0, brace_depth: 0, parenthesis_depth: 0, } } /// Get the text since the line break preceding the start of this WrappedInput. pub(crate) fn text_since_line_break(&self) -> &'s str { &self.full_source[self.start_of_line..self.start] } pub(crate) fn len(&self) -> usize { self.end - self.start } pub(crate) fn get_byte_offset(&self) -> usize { self.start } pub(crate) fn get_preceding_character(&self) -> Option { self.preceding_character } pub(crate) fn is_at_start_of_line(&self) -> bool { self.start == self.start_of_line } pub(crate) fn get_until(&self, other: OrgSource<'s>) -> OrgSource<'s> { assert!(other.start >= self.start); assert!(other.end <= self.end); self.slice(..(other.start - self.start)) } pub(crate) fn get_bracket_depth(&self) -> BracketDepth { self.bracket_depth } pub(crate) fn get_brace_depth(&self) -> BracketDepth { self.brace_depth } pub(crate) fn get_parenthesis_depth(&self) -> BracketDepth { self.parenthesis_depth } } impl<'s> InputTake for OrgSource<'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, O: Into<&'o str>> Compare for OrgSource<'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 OrgSource<'s> { fn from(value: &'s str) -> Self { OrgSource::new(value) } } impl<'s> From<&OrgSource<'s>> for &'s str { fn from(value: &OrgSource<'s>) -> Self { &value.full_source[value.start..value.end] } } impl<'s> From> for &'s str { fn from(value: OrgSource<'s>) -> Self { &value.full_source[value.start..value.end] } } impl<'s, R> Slice for OrgSource<'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 + 1, std::ops::Bound::Excluded(idx) => self.start + idx, std::ops::Bound::Unbounded => self.end, }; if new_start < self.start { panic!("Attempted to extend before the start of the WrappedInput.") } if new_end > self.end { panic!("Attempted to extend past the end of the WrappedInput.") } if new_start == self.start && new_end == self.end { return self.clone(); } let skipped_text = &self.full_source[self.start..new_start]; let mut start_of_line = self.start_of_line; let mut bracket_depth = self.bracket_depth; let mut brace_depth = self.brace_depth; let mut parenthesis_depth = self.parenthesis_depth; for (offset, byte) in skipped_text.bytes().enumerate() { match byte { b'\n' => { start_of_line = self.start + offset + 1; } b'[' => { bracket_depth += 1; } b']' => { bracket_depth -= 1; } b'{' => { brace_depth += 1; } b'}' => { brace_depth -= 1; } b'(' => { parenthesis_depth += 1; } b')' => { parenthesis_depth -= 1; } _ => {} }; } OrgSource { full_source: self.full_source, start: new_start, end: new_end, start_of_line, preceding_character: skipped_text.chars().last().or(self.preceding_character), bracket_depth, brace_depth, parenthesis_depth, } } } impl<'s> std::fmt::Display for OrgSource<'s> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { Into::<&str>::into(self).fmt(f) } } impl<'s> InputLength for OrgSource<'s> { fn input_len(&self) -> usize { self.end - self.start } } impl<'s> InputIter for OrgSource<'s> { type Item = <&'s str as InputIter>::Item; type Iter = <&'s str as InputIter>::Iter; type IterElem = <&'s str as InputIter>::IterElem; fn iter_indices(&self) -> Self::Iter { Into::<&str>::into(self).char_indices() } fn iter_elements(&self) -> Self::IterElem { Into::<&str>::into(self).iter_elements() } fn position

(&self, predicate: P) -> Option where P: Fn(Self::Item) -> bool, { Into::<&str>::into(self).position(predicate) } fn slice_index(&self, count: usize) -> Result { Into::<&str>::into(self).slice_index(count) } } impl<'s> Offset for OrgSource<'s> { fn offset(&self, second: &Self) -> usize { second.start - self.start } } impl<'s> InputTakeAtPosition for OrgSource<'s> { type Item = <&'s str as InputTakeAtPosition>::Item; fn split_at_position>( &self, predicate: P, ) -> nom::IResult where P: Fn(Self::Item) -> bool, { match Into::<&str>::into(self).position(predicate) { Some(idx) => Ok(self.take_split(idx)), None => Err(nom::Err::Incomplete(nom::Needed::new(1))), } } fn split_at_position1>( &self, predicate: P, e: nom::error::ErrorKind, ) -> nom::IResult where P: Fn(Self::Item) -> bool, { match Into::<&str>::into(self).position(predicate) { Some(0) => Err(nom::Err::Error(E::from_error_kind(self.clone(), e))), Some(idx) => Ok(self.take_split(idx)), None => Err(nom::Err::Incomplete(nom::Needed::new(1))), } } fn split_at_position_complete>( &self, predicate: P, ) -> nom::IResult where P: Fn(Self::Item) -> bool, { match self.split_at_position(predicate) { Err(nom::Err::Incomplete(_)) => Ok(self.take_split(self.input_len())), res => res, } } fn split_at_position1_complete>( &self, predicate: P, e: nom::error::ErrorKind, ) -> nom::IResult where P: Fn(Self::Item) -> bool, { let window = Into::<&str>::into(self); match window.position(predicate) { Some(0) => Err(nom::Err::Error(E::from_error_kind(self.clone(), e))), Some(n) => Ok(self.take_split(n)), None => { if window.input_len() == 0 { Err(nom::Err::Error(E::from_error_kind(self.clone(), e))) } else { Ok(self.take_split(self.input_len())) } } } } } pub(crate) fn convert_error<'a, I: Into>>( err: nom::Err, ) -> nom::Err> { match err { nom::Err::Incomplete(needed) => nom::Err::Incomplete(needed), nom::Err::Error(err) => nom::Err::Error(err.into()), nom::Err::Failure(err) => nom::Err::Failure(err.into()), } } impl<'s> From>> for CustomError<&'s str> { fn from(value: CustomError>) -> Self { match value { CustomError::MyError(err) => CustomError::MyError(err.into()), CustomError::Nom(input, error_kind) => CustomError::Nom(input.into(), error_kind), CustomError::IO(err) => CustomError::IO(err), } } } impl<'s> From>> for MyError<&'s str> { fn from(value: MyError>) -> Self { MyError(value.0.into()) } } #[cfg(test)] mod tests { use super::*; #[test] fn range() { let input = OrgSource::new("foo bar baz"); let output = input.slice(4..7); assert_eq!(output.to_string(), "bar"); } #[test] fn range_to() { let input = OrgSource::new("foo bar baz"); let output = input.slice(..7); assert_eq!(output.to_string(), "foo bar"); } #[test] fn range_from() { let input = OrgSource::new("foo bar baz"); let output = input.slice(4..); assert_eq!(output.to_string(), "bar baz"); } #[test] fn full_range() { let input = OrgSource::new("foo bar baz"); let output = input.slice(..); assert_eq!(output.to_string(), "foo bar baz"); } #[test] fn nested_range() { let input = OrgSource::new("lorem foo bar baz ipsum"); let first_cut = input.slice(6..17); let output = first_cut.slice(4..7); assert_eq!(first_cut.to_string(), "foo bar baz"); assert_eq!(output.to_string(), "bar"); } #[test] #[should_panic] fn out_of_bounds() { let input = OrgSource::new("lorem foo bar baz ipsum"); input.slice(6..30); } #[test] #[should_panic] fn out_of_nested_bounds() { let input = OrgSource::new("lorem foo bar baz ipsum"); let first_cut = input.slice(6..17); first_cut.slice(4..14); } #[test] fn line_break() { let input = OrgSource::new("lorem\nfoo\nbar\nbaz\nipsum"); assert_eq!(input.slice(5..).start_of_line, 0); assert_eq!(input.slice(6..).start_of_line, 6); assert_eq!(input.slice(6..).slice(10..).start_of_line, 14); } #[test] fn text_since_line_break() { let input = OrgSource::new("lorem\nfoo\nbar\nbaz\nipsum"); assert_eq!(input.text_since_line_break(), ""); assert_eq!(input.slice(5..).text_since_line_break(), "lorem"); assert_eq!(input.slice(6..).text_since_line_break(), ""); assert_eq!(input.slice(6..).slice(10..).text_since_line_break(), "ba"); } #[test] fn preceding_character() { let input = OrgSource::new("lorem\nfoo\nbar\nbaz\nipsum"); assert_eq!(input.get_preceding_character(), None); assert_eq!(input.slice(5..).get_preceding_character(), Some('m')); assert_eq!(input.slice(6..).get_preceding_character(), Some('\n')); assert_eq!( input.slice(6..).slice(10..).get_preceding_character(), Some('a') ); } #[test] fn is_at_start_of_line() { let input = OrgSource::new("lorem\nfoo\nbar\nbaz\nipsum"); assert_eq!(input.is_at_start_of_line(), true); assert_eq!(input.slice(5..).is_at_start_of_line(), false); assert_eq!(input.slice(6..).is_at_start_of_line(), true); assert_eq!(input.slice(6..).slice(10..).is_at_start_of_line(), false); } #[test] fn preceding_character_unicode() { let input = OrgSource::new("๐Ÿงก๐Ÿ’›๐Ÿ’š๐Ÿ’™๐Ÿ’œ"); assert_eq!(input.get_preceding_character(), None); assert_eq!(input.slice(8..).get_preceding_character(), Some('๐Ÿ’›')); } #[test] fn depth() { let input = OrgSource::new("[][()][({)]}}}}"); assert_eq!(input.get_bracket_depth(), 0); assert_eq!(input.get_brace_depth(), 0); assert_eq!(input.get_parenthesis_depth(), 0); assert_eq!(input.slice(4..).get_bracket_depth(), 1); assert_eq!(input.slice(4..).get_brace_depth(), 0); assert_eq!(input.slice(4..).get_parenthesis_depth(), 1); assert_eq!(input.slice(4..).slice(6..).get_bracket_depth(), 1); assert_eq!(input.slice(4..).slice(6..).get_brace_depth(), 1); assert_eq!(input.slice(4..).slice(6..).get_parenthesis_depth(), 0); assert_eq!(input.slice(14..).get_brace_depth(), -2); } }