use crate::State;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, thiserror::Error)]
#[error("recursion limit exceeded: depth {}, maximum {}", .0.depth(), .0.limitation())]
pub struct RecursionLimitExceeded(RecursionLimiter);
impl RecursionLimitExceeded {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn depth(&self) -> usize {
self.0.depth()
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn limitation(&self) -> usize {
self.0.limitation()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct RecursionLimiter {
max: usize,
current: usize,
}
impl Default for RecursionLimiter {
#[cfg_attr(not(tarpaulin), inline(always))]
fn default() -> Self {
Self::new()
}
}
impl RecursionLimiter {
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn new() -> Self {
Self {
max: 500,
current: 0,
}
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn with_limitation(max: usize) -> Self {
Self { max, current: 0 }
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn depth(&self) -> usize {
self.current
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn limitation(&self) -> usize {
self.max
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn increase(&mut self) {
self.current += 1;
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn decrease(&mut self) {
self.current = self.current.saturating_sub(1);
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn increase_recursion(&mut self) {
self.increase();
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn decrease_recursion(&mut self) {
self.decrease();
}
#[cfg_attr(not(tarpaulin), inline(always))]
pub const fn check(&self) -> Result<(), RecursionLimitExceeded> {
if self.depth() > self.limitation() {
Err(RecursionLimitExceeded(*self))
} else {
Ok(())
}
}
}
impl State for RecursionLimiter {
type Error = RecursionLimitExceeded;
#[cfg_attr(not(tarpaulin), inline(always))]
fn check(&self) -> Result<(), Self::Error> {
<Self as RecursionTracker>::check(self)
}
}
pub trait RecursionTracker {
type Error;
fn increase(&mut self);
fn decrease(&mut self);
fn check(&self) -> Result<(), Self::Error>;
#[cfg_attr(not(tarpaulin), inline(always))]
fn increase_and_check(&mut self) -> Result<(), Self::Error> {
self.increase();
self.check()
}
}
impl RecursionTracker for RecursionLimiter {
type Error = RecursionLimitExceeded;
#[cfg_attr(not(tarpaulin), inline(always))]
fn increase(&mut self) {
self.increase();
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn decrease(&mut self) {
self.decrease();
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn check(&self) -> Result<(), Self::Error> {
self.check()
}
}
#[cfg(feature = "logos")]
const _: () = {
use crate::{Token, lexer::LogosLexer};
use logos::{Lexer, Logos};
impl<'a, T> RecursionTracker for Lexer<'a, T>
where
T: Logos<'a>,
T::Extras: RecursionTracker,
{
type Error = <T::Extras as RecursionTracker>::Error;
#[cfg_attr(not(tarpaulin), inline(always))]
fn increase(&mut self) {
self.extras.increase();
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn decrease(&mut self) {
self.extras.decrease();
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn check(&self) -> Result<(), Self::Error> {
self.extras.check()
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn increase_and_check(&mut self) -> Result<(), Self::Error> {
self.extras.increase_and_check()
}
}
impl<'a, T, L> RecursionTracker for LogosLexer<'a, T, L>
where
T: From<L> + Token<'a>,
L: Logos<'a>,
L::Extras: RecursionTracker,
{
type Error = <L::Extras as RecursionTracker>::Error;
#[cfg_attr(not(tarpaulin), inline(always))]
fn increase(&mut self) {
self.inner_mut().increase();
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn decrease(&mut self) {
self.inner_mut().decrease();
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn check(&self) -> Result<(), Self::Error> {
self.inner().check()
}
#[cfg_attr(not(tarpaulin), inline(always))]
fn increase_and_check(&mut self) -> Result<(), Self::Error> {
self.inner_mut().increase_and_check()
}
}
};