///|
pub(all) struct Parser[I, T] {
  parse_fn : (I) -> Result[(T, I), ParseError]
}

///|
pub fn[I, T] Parser::new(
  parse_fn : (I) -> Result[(T, I), ParseError],
) -> Parser[I, T] {
  { parse_fn, }
}

///|
pub fn[I, T] Parser::run(
  self : Parser[I, T],
  input : I,
) -> Result[(T, I), ParseError] {
  (self.parse_fn)(input)
}

///|
pub fn[I : Cursor, T] Parser::parse(
  self : Parser[I, T],
  input : I,
) -> Result[T, ParseError] {
  match self.run(input) {
    Ok((value, input)) => {
      guard !input.is_at_eof() else { Ok(value) }
      Err(ParseError::new(input, "end of input"))
    }
    Err(error) => Err(error)
  }
}

///|
pub fn[I, T] Parser::parse_partial(
  self : Parser[I, T],
  input : I,
) -> Result[(T, I), ParseError] {
  self.run(input)
}

///|
pub fn[I, T] pure(value : T) -> Parser[I, T] {
  Parser::new(input => Ok((value, input)))
}

///|
pub fn[I : Cursor, T] fail(expected : String) -> Parser[I, T] {
  Parser::new(input => Err(ParseError::new(input, expected)))
}

///|
pub fn[I, T] delay(thunk : () -> Parser[I, T]) -> Parser[I, T] {
  Parser::new(input => thunk().run(input))
}