Documentation
use nom::{
	AsChar,
	Compare,
	InputIter,
	InputLength,
	InputTake,
	InputTakeAtPosition,
	Offset,
	Slice,
};
use std::ops::{
	Range,
	RangeFrom,
	RangeTo,
};
use nom::branch::alt;
use nom::character::complete::{
	char,
	multispace1,
	not_line_ending,
};
use nom::combinator::{
	fail,
	recognize,
};
use nom::multi::many0;
use nom::sequence::{
	delimited,
	tuple,
};
use crate::ParserOptions;
use crate::utils::{
	IResult,
	value,
};

pub(crate) fn ws_or_comment<'a, I, C>(opts: &'a ParserOptions) -> impl FnMut(I) -> IResult<I, ()> + 'a
where
	I: Clone
		+ Compare<&'static str>
		+ InputIter<Item = C>
		+ InputLength
		+ InputTake
		+ InputTakeAtPosition<Item = C>
		+ Offset
		+ Slice<Range<usize>>
		+ Slice<RangeFrom<usize>>
		+ Slice<RangeTo<usize>>
		+ 'a
		,
	C: AsChar + Clone,
{
	value(
		many0(alt((
			move |input| if opts.comments {
				recognize(
					tuple((
						char('#'),
						not_line_ending,
					))
				)(input)
			} else {
				fail(input)
			},
			recognize(multispace1),
		))),
		(),
	)
}

pub(crate) fn surrounded_ws_or_comment<'a, I, C, O, P>(opts: &'a ParserOptions, parser: P) -> impl FnMut(I) -> IResult<I, O> + 'a
where
	P: FnMut(I) -> IResult<I, O> + 'a,
	I: Clone
		+ Compare<&'static str>
		+ InputIter<Item = C>
		+ InputLength
		+ InputTake
		+ InputTakeAtPosition<Item = C>
		+ Offset
		+ Slice<Range<usize>>
		+ Slice<RangeFrom<usize>>
		+ Slice<RangeTo<usize>>
		+ 'a
		,
	O: 'a,
	C: AsChar + Clone,
{
	delimited(
		ws_or_comment(opts),
		parser,
		ws_or_comment(opts),
	)
}

#[allow(unused_imports)]
#[cfg(test)]
mod tests {
	use super::*;
	use crate::vec;

	use nom::error::{
		VerboseError,
		VerboseErrorKind,
	};

	#[test]
	fn comments() {
		let src = " \n \t # whatever\n";

		let opts = ParserOptions::new()
			.comments(true)
			.build();

		assert_eq!(
			ws_or_comment(&opts)(src),
			Ok(("", ())),
		);

		let opts = ParserOptions::new()
			.comments(false)
			.build();

		assert_eq!(
			ws_or_comment(&opts)(src),
			Ok(("# whatever\n", ())),
		);
	}
}