use crate::{Error, ErrorKind, Length, Result};
#[derive(Clone, Debug)]
pub(super) struct Position {
input_len: Length,
position: Length,
depth: usize,
}
impl Position {
const MAX_DEPTH: usize = 64;
pub(super) fn new(input_len: Length) -> Self {
Self {
input_len,
position: Length::ZERO,
depth: 0,
}
}
pub(super) fn advance(&mut self, amount: Length) -> Result<Length> {
let new_position = (self.position + amount)?;
if new_position > self.input_len {
return Err(ErrorKind::Incomplete {
expected_len: new_position,
actual_len: self.input_len,
}
.at(self.position));
}
self.position = new_position;
Ok(new_position)
}
pub(super) fn current(&self) -> Length {
self.position
}
pub(super) fn error(&mut self, kind: ErrorKind) -> Error {
kind.at(self.position)
}
pub(super) fn input_len(&self) -> Length {
self.input_len
}
pub(super) fn remaining_len(&self) -> Length {
debug_assert!(self.position <= self.input_len());
self.input_len.saturating_sub(self.position)
}
pub(super) fn split_nested(&mut self, len: Length) -> Result<Resumption> {
match self.depth.checked_add(1) {
Some(depth) if depth < Self::MAX_DEPTH => self.depth = depth,
_ => return Err(self.error(ErrorKind::NestingDepth)),
}
let nested_input_len = (self.position + len)?;
if nested_input_len > self.input_len {
return Err(Error::incomplete(self.input_len));
}
let resumption = Resumption {
input_len: self.input_len,
};
self.input_len = nested_input_len;
Ok(resumption)
}
pub(super) fn resume_nested(&mut self, resumption: Resumption) {
self.input_len = resumption.input_len;
self.depth = self.depth.saturating_sub(1);
}
}
#[derive(Debug)]
pub(super) struct Resumption {
input_len: Length,
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::Position;
use crate::{ErrorKind, Length};
const EXAMPLE_LEN: Length = match Length::new_usize(42) {
Ok(len) => len,
Err(_) => panic!("invalid example len"),
};
#[test]
fn initial_state() {
let pos = Position::new(EXAMPLE_LEN);
assert_eq!(pos.input_len(), EXAMPLE_LEN);
assert_eq!(pos.current(), Length::ZERO);
}
#[test]
fn advance() {
let mut pos = Position::new(EXAMPLE_LEN);
let new_pos = pos.advance(Length::ONE).unwrap();
assert_eq!(new_pos, Length::ONE);
assert_eq!(pos.current(), Length::ONE);
let end_pos = pos.advance((EXAMPLE_LEN - Length::ONE).unwrap()).unwrap();
assert_eq!(end_pos, EXAMPLE_LEN);
assert_eq!(pos.current(), EXAMPLE_LEN);
let err = pos.advance(Length::ONE).unwrap_err();
assert!(matches!(err.kind(), ErrorKind::Incomplete { .. }));
}
#[test]
fn nested() {
let mut pos = Position::new(EXAMPLE_LEN);
let resumption = pos.split_nested(Length::ONE).unwrap();
assert_eq!(pos.current(), Length::ZERO);
assert_eq!(pos.input_len(), Length::ONE);
assert_eq!(pos.advance(Length::ONE).unwrap(), Length::ONE);
let err = pos.advance(Length::ONE).unwrap_err();
assert!(matches!(err.kind(), ErrorKind::Incomplete { .. }));
pos.resume_nested(resumption);
assert_eq!(pos.current(), Length::ONE);
assert_eq!(pos.input_len(), EXAMPLE_LEN);
let err = pos.split_nested(EXAMPLE_LEN).unwrap_err();
assert!(matches!(err.kind(), ErrorKind::Incomplete { .. }));
}
}