pub const MAX_NESTING_DEPTH: usize = 512;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct Nesting(usize);
impl Nesting {
pub const ROOT: Self = Self(0);
#[must_use]
pub const fn level(self) -> usize {
self.0
}
pub const fn descend(self) -> Result<Self, NestingExceeded> {
if self.0 >= MAX_NESTING_DEPTH {
return Err(NestingExceeded {
limit: MAX_NESTING_DEPTH,
});
}
Ok(Self(self.0 + 1))
}
#[must_use]
pub const fn ascend(self) -> Self {
Self(self.0.saturating_sub(1))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("nesting exceeds the limit of {limit} levels")]
pub struct NestingExceeded {
pub limit: usize,
}
pub fn check_bracket_nesting<T>(
tokens: &[T],
opens: impl Fn(&T) -> bool,
closes: impl Fn(&T) -> bool,
) -> Result<(), (usize, NestingExceeded)> {
let mut level = Nesting::ROOT;
for (index, token) in tokens.iter().enumerate() {
if opens(token) {
level = level.descend().map_err(|e| (index, e))?;
} else if closes(token) {
level = level.ascend();
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn descend_stops_exactly_at_the_bound() {
let mut level = Nesting::ROOT;
for _ in 0..MAX_NESTING_DEPTH {
level = level.descend().expect("within the bound");
}
assert_eq!(level.level(), MAX_NESTING_DEPTH);
assert_eq!(
level.descend(),
Err(NestingExceeded {
limit: MAX_NESTING_DEPTH
})
);
assert_eq!(level.ascend().level(), MAX_NESTING_DEPTH - 1);
assert_eq!(Nesting::ROOT.ascend(), Nesting::ROOT);
}
#[test]
fn bracket_nesting_reports_the_first_crossing_token() {
let opens = |c: &char| *c == '(';
let closes = |c: &char| *c == ')';
let balanced: Vec<char> = "(()())".chars().collect();
assert_eq!(check_bracket_nesting(&balanced, opens, closes), Ok(()));
let mut deep: Vec<char> = std::iter::repeat_n('(', MAX_NESTING_DEPTH).collect();
deep.extend(std::iter::repeat_n(')', MAX_NESTING_DEPTH));
assert_eq!(check_bracket_nesting(&deep, opens, closes), Ok(()));
deep.insert(MAX_NESTING_DEPTH, '(');
assert_eq!(
check_bracket_nesting(&deep, opens, closes),
Err((
MAX_NESTING_DEPTH,
NestingExceeded {
limit: MAX_NESTING_DEPTH
}
))
);
}
}