muninn_query 0.5.0

Query langugage for muninn logging stack
Documentation
use crate::parser::Error;
use nom::{
  bytes::complete::tag,
  character::complete::{alphanumeric1, digit1},
  combinator::{map_res, not, recognize},
  error::ParseError,
  sequence::tuple,
  AsChar, Compare, InputIter, InputLength, InputTake, InputTakeAtPosition,
};
use std::{cell::RefCell, ops::Range};

/// This used in place of `&str` or `&[u8]` in our `nom` parsers.
pub(crate) type LocatedSpan<'a> = nom_locate::LocatedSpan<&'a str, State<'a>>;

/// Convenient type alias for `nom::IResult<I, O>` reduced to `IResult<O>`.
pub(crate) type IResult<'a, T> = nom::IResult<LocatedSpan<'a>, T>;

pub(crate) trait ToRange {
  fn to_range(&self) -> Range<usize>;
}

impl<'a> ToRange for LocatedSpan<'a> {
  fn to_range(&self) -> Range<usize> {
    let start = self.location_offset();
    let end = start + self.fragment().len();
    start..end
  }
}

#[derive(Clone, Debug)]
pub(crate) struct State<'a>(&'a RefCell<Vec<Error>>);

impl<'a> State<'a> {
  pub fn new(errors: &'a RefCell<Vec<Error>>) -> State<'a> {
    State(errors)
  }

  /// Pushes an error onto the errors stack from within a `nom`
  /// parser combinator while still allowing parsing to continue.
  pub fn report_error(&self, error: Error) {
    let mut errors = self.0.borrow_mut();
    if !errors.contains(&error) {
      errors.push(error);
    }
  }

  /// Pushes an error onto the errors stack unless that span already
  /// is covered.
  pub fn report_error_if_no_overlap(&self, error: Error) {
    let mut errors = self.0.borrow_mut();
    let has_overlap = errors.iter().any(|err| {
      err.0.start <= error.0.start && err.0.end >= error.0.start
        || err.0.start <= error.0.end && err.0.end >= error.0.start
    });
    if !has_overlap {
      errors.push(error);
    }
  }
}

pub(crate) fn keyword<T, Input, Error: ParseError<Input>>(
  keyword: T,
) -> impl FnMut(Input) -> nom::IResult<Input, Input, Error>
where
  Input: InputTake
    + Compare<T>
    + Clone
    + InputLength
    + InputIter
    + InputTakeAtPosition
    + nom::Offset
    + nom::Slice<std::ops::RangeTo<usize>>,
  T: InputLength + Clone,
  <Input as InputTakeAtPosition>::Item: AsChar,
{
  recognize(tuple((tag(keyword), not(alphanumeric1))))
}

pub(crate) fn int16(i: LocatedSpan) -> IResult<i16> {
  map_res(digit1, |res: LocatedSpan| i16::from_str_radix(*res, 10))(i)
}

#[cfg(test)]
pub(crate) fn span(i: &str) -> LocatedSpan {
  let errors = Box::new(RefCell::new(vec![]));
  LocatedSpan::new_extra(i, State(Box::leak(errors)))
}

#[cfg(test)]
pub(crate) fn unwrap_span<T>(res: (LocatedSpan, T)) -> (&str, T) {
  let (span, res) = res;
  (*span, res)
}