extern crate nom;

use nom::combinator::map_res;
use nom::{
    branch::alt,
    bytes::complete::tag,
    character::complete::{digit1, multispace0},
    combinator::map,
    error::{context, VerboseError, VerboseErrorKind},
    sequence::{preceded, tuple},
    IResult,
};

#[derive(Debug, PartialEq)]
pub enum Op {
    Add,
}

#[derive(Debug, PartialEq)]
pub enum Expr {
    Num(i32),
    BinOp(Box<Expr>, Op, Box<Expr>),
}

pub fn parse_i32(i: &str) -> IResult<&str, Expr, VerboseError<&str>> {
    map_res::<_, _, _, _, VerboseError<&str>, _, _>(digit1, |digit_str: &str| {
        match digit_str.parse::<i32>() {
            // Err(e) => panic!("e {:?}", e),
            Err(_) => Err(VerboseError {
                errors: vec![(digit_str, VerboseErrorKind::Context("not a 32-bit integer"))],
            }),
            Ok(x) => Ok(Expr::Num(x)),
        }
    })(i)
}

fn parse_expr(input: &str) -> IResult<&str, Expr, VerboseError<&str>> {
    context(
        "parse_expr",
        preceded(
            multispace0,
            alt((
                map(
                    tuple((parse_i32, preceded(multispace0, tag("+")), parse_expr)),
                    |(l, _, r)| Expr::BinOp(Box::new(l), Op::Add, Box::new(r)),
                ),
                parse_i32,
            )),
        ),
    )(input)
}

// cargo test
#[test]
fn test_parse_i32_1() {
    let res = parse_expr("2");
    assert!(res == Ok(("", Expr::Num(1))))
}

#[test]
fn test_parse_i32_2() {
    let _ = parse_expr("1a").is_ok();
}

fn main() {
    println!("{:?}", parse_expr("1"));
    println!("{:?}", parse_expr("1+2 + 3"));
    println!("{:?}", parse_expr("   1+ 1a"));
    println!("{:?}", parse_expr("11111111111111111111111111"));
}