use std::ops::ControlFlow;

use nom::{
    error::{ErrorKind, ParseError},
    Err, InputLength, Parser,
};

pub fn fold_till<I, O, E, St, P, F, G>(
    mut p: P,
    mut init: F,
    mut acc: G,
) -> impl FnMut(I) -> nom::IResult<I, St, E>
where
    I: InputLength,
    E: ParseError<I>,
    P: Parser<I, O, E>,
    F: FnMut() -> St,
    G: FnMut(St, O) -> ControlFlow<St, St>,
{
    move |i| {
        let mut res = init();
        let mut input = i;

        loop {
            let len = input.input_len();
            let (i, o) = p.parse(input)?;

            if i.input_len() == len {
                return Err(Err::Error(E::from_error_kind(i, ErrorKind::Many0)));
            }

            match acc(res, o) {
                ControlFlow::Continue(next) => {
                    res = next;
                    input = i;
                }
                ControlFlow::Break(res) => return Ok((i, res)),
            }
        }
    }
}
