kismesis 0.5.0

A static site generator with plugins and a custom markup language.
Documentation
use std::{
	fmt::Debug,
	ops::{Bound, RangeBounds},
};

use super::{
	errors::{Err, ParseError, Unpack},
	state::State,
	types::{MultilineRange, Ranged},
	Parser, ParserResult,
};

pub(super) fn preceding<'a, P1, O1, P2, O2>(p1: P1, p2: P2) -> impl Parser<'a, O2>
where
	P1: Parser<'a, O1> + 'a,
	P2: Parser<'a, O2> + 'a,
	O1: 'a,
	O2: 'a,
{
	p1.and_also(p2).map(|(_, y)| y)
}

pub(super) fn ignore<'a, P1, O1>(p1: P1) -> impl Parser<'a, ()>
where
	P1: Parser<'a, O1>,
{
	move |state| match p1.parse(state) {
		Ok((_, next_state)) => Ok(((), next_state)),
		Err(error) => Err(error),
	}
}

pub(super) fn maybe_until<'a, P1, O1, P2, O2>(
	p1: P1,
	p2: P2,
	allow_empty: bool,
) -> impl Parser<'a, Vec<O1>>
where
	P1: Parser<'a, O1>,
	P2: Parser<'a, O2>,
{
	move |mut state: State<'a>| {
		let mut found = Vec::new();
		loop {
			match p2.parse(state.clone()) {
				Ok((_, next_state)) => {
					if found.is_empty() && !allow_empty {
						return Err(ParseError::EmptyString.error_at(&next_state));
					}
					return Ok((found, state));
				}
				Err(_) => match p1.parse(state.clone()) {
					Ok((val, next_state)) => {
						found.push(val);
						state = next_state;
					}
					Err(_) => return Err(ParseError::ConditionUnmet.error_at(&state)),
				},
			}
		}
	}
}

pub(super) fn and_also<'a, P1, O1, P2, O2>(p1: P1, p2: P2) -> impl Parser<'a, (O1, O2)>
where
	P1: Parser<'a, O1>,
	P2: Parser<'a, O2>,
{
	move |state| match p1.parse(state) {
		Ok((first_result, next_state)) => match p2.parse(next_state) {
			Ok((second_result, next_state)) => Ok(((first_result, second_result), next_state)),
			Err(err) => Err(err),
		},
		Err(error) => Err(error),
	}
}
pub(super) fn and_maybe<'a, P1, O1, P2, O2>(p1: P1, p2: P2) -> impl Parser<'a, (O1, Option<O2>)>
where
	P1: Parser<'a, O1>,
	P2: Parser<'a, O2>,
{
	move |state| match p1.parse(state) {
		ParserResult::Ok((first_result, next_state)) => match p2.parse(next_state.clone()) {
			Ok((second_result, next_state)) => {
				Ok(((first_result, Some(second_result)), next_state))
			}
			Err(Err::Error(_)) => Ok(((first_result, None), next_state)),
			Err(x) => Err(x),
		},
		Err(error) => Err(error),
	}
}
pub(super) fn followed_by<'a, P1, O1, P2, O2>(p1: P1, p2: P2) -> impl Parser<'a, O1>
where
	P1: Parser<'a, O1> + 'a,
	P2: Parser<'a, O2> + 'a,
	O1: 'a,
	O2: 'a,
{
	p1.and_also(p2).map(|(x, _)| x)
}

pub(super) fn or<'a, P1, O1, P2>(p1: P1, p2: P2) -> impl Parser<'a, O1>
where
	P1: Parser<'a, O1>,
	P2: Parser<'a, O1>,
{
	move |state: State<'a>| match p1.parse(state.clone()) {
		Ok((result, next_state)) => Ok((result, next_state)),
		Err(Err::Failure(x)) => Err(Err::Failure(x)),
		Err(_) => p2.parse(state),
	}
}

pub(super) fn zero_or_more<'a, P, T>(parser: P) -> impl Parser<'a, Vec<T>>
where
	P: Parser<'a, T>,
{
	repeated(parser, 0..)
}

pub(super) fn repeated<'a, P, T>(
	parser: P,
	range: impl RangeBounds<usize>,
) -> impl Parser<'a, Vec<T>>
where
	P: Parser<'a, T>,
{
	move |state: State<'a>| {
		let mut state = state;
		let mut found = Vec::<T>::new();
		loop {
			match parser.parse(state.clone()) {
				Ok((token, next_state)) => {
					state = next_state;
					found.push(token);
				}
				Err(Err::Failure(x)) => return Err(Err::Failure(x)),
				_ => break,
			}
		}
		if range.contains(&found.len()) {
			Ok((found, state))
		} else {
			let start = match range.start_bound() {
				Bound::Included(x) => Bound::Included(*x),
				Bound::Excluded(x) => Bound::Excluded(*x),
				Bound::Unbounded => Bound::<usize>::Unbounded,
			};
			let end = match range.end_bound() {
				Bound::Included(x) => Bound::Included(*x),
				Bound::Excluded(x) => Bound::Excluded(*x),
				Bound::Unbounded => Bound::<usize>::Unbounded,
			};

			Err(ParseError::NotInRange(start, end).error_at(&state))
		}
	}
}
pub(super) fn peek<'a, P, T>(parser: P) -> impl Parser<'a, T>
where
	P: Parser<'a, T>,
{
	move |state: State<'a>| {
		let (val, _) = parser.parse(state.clone())?;
		Ok((val, state))
	}
}

#[allow(dead_code)]
pub(super) fn dbg<'a, P, T: Debug>(parser: P) -> impl Parser<'a, T>
where
	P: Parser<'a, T>,
{
	move |state: State<'a>| {
		let r = parser.parse(state);
		println!("{r:#?}");
		r
	}
}

pub(super) fn cut<'a, P, T>(parser: P) -> impl Parser<'a, T>
where
	P: Parser<'a, T>,
{
	move |state: State<'a>| match parser.parse(state) {
		Err(Err::Error(x)) => Err(Err::Failure(x)),
		pat => pat,
	}
}

pub(super) fn not<'a, P, T>(parser: P) -> impl Parser<'a, ()>
where
	P: Parser<'a, T>,
{
	move |state: State<'a>| match parser.parse(state.clone()) {
		Err(Err::Error(_)) => Ok(((), state)),
		Err(Err::Failure(x)) => Err(Err::Failure(x)),
		Ok((_, state)) => Err(ParseError::ConditionUnmet.error_at(&state)),
	}
}

pub(super) fn maybe<'a, P, T>(parser: P) -> impl Parser<'a, Option<T>>
where
	P: Parser<'a, T>,
{
	move |state: State<'a>| match parser.parse(state.clone()) {
		Err(Err::Error(_)) => Ok((None, state)),
		Err(Err::Failure(x)) => Err(Err::Failure(x)),
		Ok((x, state)) => Ok((Some(x), state)),
	}
}

pub(super) fn map<'a, P, F, T1, T2>(parser: P, fun: F) -> impl Parser<'a, T2>
where
	P: Parser<'a, T1>,
	F: Fn(T1) -> T2,
{
	move |state: State<'a>| parser.parse(state).map(|(val, state)| (fun(val), state))
}

pub(super) fn change_err<'a, P, F, T1>(parser: P, fun: F) -> impl Parser<'a, T1>
where
	P: Parser<'a, T1>,
	F: Fn() -> ParseError,
{
	move |state: State<'a>| match parser.parse(state) {
		x @ Ok(_) => x,
		Err(x) => {
			let failure = matches!(x, Err::Failure(_));
			let mut x = x.unpack();
			if failure {
				Err(Err::Failure(x))
			} else {
				*x.error.unpack_mut() = fun();
				Err(Err::Error(x))
			}
		}
	}
}

pub(super) fn is<'a, P, F, T1>(parser: P, fun: F) -> impl Parser<'a, T1>
where
	P: Parser<'a, T1>,
	F: Fn(&T1) -> bool,
	T1: 'a,
{
	move |state: State<'a>| {
		let (val, state) = match parser.parse(state) {
			Ok(x) => x,
			Err(x) => return Err(x),
		};
		if fun(&val) {
			Ok((val, state))
		} else {
			Err(ParseError::ConditionUnmet.error_at(&state))
		}
	}
}

pub(super) fn get_range<'a, P, T1>(parser: P) -> impl Parser<'a, Ranged<T1>>
where
	P: Parser<'a, T1>,
{
	move |state: State<'a>| {
		let start = state.get_start_position();
		let (val, next_state) = parser.parse(state)?;
		let end = next_state.get_end_position();
		let range = {
			if end.get_idx() == start.get_idx() + 1 {
				MultilineRange::Single(start)
			} else {
				MultilineRange::Range(start, end)
			}
		};
		Ok((Ranged { value: val, range }, next_state))
	}
}