//! This module contains a CDDL parser.
//!
//! The only public items here are the function [`parse_cddl`] and the error
//! [`ParseError`] and its child enum [`ErrorKind`].
//!
//! # Examples
//! ```
//! use cddl_cat::parse_cddl;
//!
//! let input = "map = { name: tstr }";
//! assert!(parse_cddl(input).is_ok());
//! ```

// This occurs all over the place starting in rust 1.89.0.
// It's a quirk of the way we handle nom IResult types, and I don't
// have the patience to fix it now.
//
// warning: hiding a lifetime that's elided elsewhere is confusing
//   --> src/parser/mod.rs:78:19
//    |
// 78 | fn comment(input: &str) -> JResult<&str, &str> {
//    |                   ^^^^     ^^^^^^^^^^^^^^^^^^^
//    |                   |        |       |     |
//    |                   |        |       |     the same lifetime is elided here
//    |                   |        |       the same lifetime is elided here
//    |                   |        the same lifetime is hidden here
//    |                   the lifetime is elided here
#![allow(mismatched_lifetime_syntaxes)]

use escape8259::unescape;
use nom::{
    branch::alt,
    bytes::complete::{tag, take_while1},
    character::complete::{
        anychar, char as charx, digit0, digit1, hex_digit1, multispace1, not_line_ending, one_of,
    },
    combinator::{all_consuming, map, map_res, opt, recognize, value as valuex},
    multi::{many0, many1, separated_list1},
    sequence::{delimited, pair, preceded, separated_pair, terminated, tuple},
};
use std::convert::TryFrom;

use crate::ast::*;
use parse_err::{parse_error, CowParseError};

mod parse_err;
pub use parse_err::{ErrorKind, ParseError};

//
// A note on the design of the parser:
//
// Parsers built from the "nom" crate look a bit different from most other
// Rust code; for extra readability there is a lot of extra indents that
// rustfmt wouldn't like (and rustfmt::skip is applied extensively.)
//

// This error type is used everywhere in this parser.  It allows
// me to mix locally-generated custom errors with the errors that
// are automatically generated by the parser.
type JResult<'a, I, O> = nom::IResult<I, O, CowParseError<'a>>;

// A workaround for the fact that nom::combinator::map_res discards the returned error type.
// See also https://github.com/Geal/nom/issues/1171
fn map_res_fail<I: Clone, O1, O2, E: nom::error::ParseError<I>, F, G>(
    mut first: F,
    second: G,
) -> impl FnMut(I) -> nom::IResult<I, O2, E>
where
    F: FnMut(I) -> nom::IResult<I, O1, E>,
    G: Fn(O1) -> Result<O2, E>,
{
    move |input: I| {
        let (input, o1) = first(input)?;
        match second(o1) {
            Ok(o2) => Ok((input, o2)),
            Err(e) => Err(nom::Err::Failure(e)),
        }
    }
}

// CDDL whitespace definition:
// Note no support for tabs, or naked linefeed characters.
//
// S = *WS
// WS = SP / NL
// SP = %x20
// NL = COMMENT / CRLF
// COMMENT = ";" *PCHAR CRLF
// PCHAR = %x20-7E / %x80-10FFFD
// CRLF = %x0A / %x0D.0A

// This varies a bit from the RFC, again, with respect to whitespace.
#[rustfmt::skip]
fn comment(input: &str) -> JResult<&str, &str> {
    // semicolon, anything, terminated by CR or CR+LF.
    preceded(
        charx(';'),
        not_line_ending
    )(input)
}

// Any amount of whitespace (including none) (including comments)
// Note: this counts tab characters as whitespace, which differs from RFC8610.
#[rustfmt::skip]
fn ws(input: &str) -> JResult<&str, &str> {
    recognize(
        many0(
            alt((
                // multispace1 includes tabs
                multispace1,
                comment
            ))
        )
    )
    (input)
}

// An optional comma and any whitespace surrounding it.
#[rustfmt::skip]
fn optcom(input: &str) -> JResult<&str, ()> {
    valuex((), pair(
        ws,
        opt(pair(
            tag(","),
            ws
        ))
    ))
    (input)
}

// id = EALPHA *(*("-" / ".") (EALPHA / DIGIT))
// So the first character needs to be EALPHA (alpha plus @_$)
// and then any number of EALPHA or DIGIT or "-" or ".",
// ending with EALPHA or DIGIT.
// we'll take this in steps:
// 1. first EALPHA char
// 2. do the following zero or more times:
//    a. optionally consume one of -.
//    b. consume EALPHA or DIGIT.

fn ealpha_char(c: char) -> bool {
    c.is_ascii_alphabetic() || c == '@' || c == '_' || c == '$'
}

#[rustfmt::skip]
fn ealpha(input: &str) -> JResult<&str, &str> {
    take_while1(ealpha_char)
    (input)
}

// The tail characters of an ident: *("-" / ".") (EALPHA / DIGIT)
#[rustfmt::skip]
fn ident_tail(input: &str) -> JResult<&str, &str> {
    recognize(
        preceded(
            opt(many1(one_of("-."))),
            alt((
                ealpha,
                digit1
            ))
        )
    )
    (input)
}

#[rustfmt::skip]
fn ident(input: &str) -> JResult<&str, &str> {
    recognize(
        preceded(
            ealpha,
            many0(ident_tail)
        )
    )
    (input)
}

// uint = DIGIT1 *DIGIT
//      / "0x" 1*HEXDIG
//      / "0b" 1*BINDIG
//      / "0"
#[rustfmt::skip]
fn uint_hex(input: &str) -> JResult<&str, &str> {
    preceded(
        tag("0x"),
        hex_digit1
    )
    (input)
}

#[rustfmt::skip]
fn uint_binary(input: &str) -> JResult<&str, &str> {
    preceded(
        tag("0b"),
        recognize(many1(one_of("01")))
    )
    (input)
}

#[rustfmt::skip]
fn uint_decimal(input: &str) -> JResult<&str, &str> {
    alt((
        tag("0"),
        recognize(
            pair(
                one_of("123456789"),
                digit0
            )
        )
    ))
    (input)
}

// Parsing of integers, floats, etc. can get pretty complicated.  Because
// different downstream parsers may want the integer in different forms, we
// store its information in a temporary struct that preserves the original
// string slice along with some context that will help us remember what that
// string represents.
struct RawUint<'a> {
    slice: &'a str,
    base: u32,
}

// Parse the string for uint; return the slice and the intended base (radix).
#[rustfmt::skip]
fn uint(input: &str) -> JResult<&str, RawUint> {
    alt((
        map(uint_hex, |slice| RawUint{slice, base: 16}),
        map(uint_binary, |slice| RawUint{slice, base: 2}),
        map(uint_decimal, |slice| {
            RawUint{slice, base: 10}
        }),
    ))
    (input)
}

// Extract an unsigned integer using the correct base (radix).
#[rustfmt::skip]
fn uint_u64(input: &str) -> JResult<&str, u64> {
    map_res_fail(uint, |raw| {
        u64::from_str_radix(raw.slice, raw.base)
        .map_err(|_| {
            parse_error(ErrorKind::MalformedInteger, raw.slice)
        })
    })
    (input)
}

// Like RawUint, this is a temporary representation so we can
// preserve both the string slice and some metadata about it.
struct RawInt<'a> {
    slice: &'a str,
    base: u32,
    neg: bool,
}

// A signed integer
#[rustfmt::skip]
fn int(input: &str) -> JResult<&str, RawInt> {
    let f = pair(
        opt(charx('-')),
        uint
    );
    map(f, |(optneg, rawuint)| {
        RawInt {
            slice: rawuint.slice,
            base: rawuint.base,
            neg: optneg.is_some(),
        }
    })
    (input)
}

// "." fraction
// fraction = 1*DIGIT
#[rustfmt::skip]
fn dot_fraction(input: &str) -> JResult<&str, &str> {
    recognize(
        pair(
            charx('.'),
            digit1
        )
    )
    (input)
}

// "e" exponent
// exponent = ["+"/"-"] 1*DIGIT
#[rustfmt::skip]
fn e_exponent(input: &str) -> JResult<&str, &str> {
    recognize(
        tuple((
            charx('e'),
            opt(one_of("+-")),
            digit1
        ))
    )
    (input)
}

// A helper function for converting string -> Value::Float,
// and mapping to the right error type
#[rustfmt::skip]
fn parse_float(s: &str) -> Result<Value, CowParseError> {
    match s.parse::<f64>() {
        Ok(fl) => Ok(Value::Float(fl)),
        Err(_) => Err(parse_error(ErrorKind::MalformedFloat, s)),
    }
}

// A helper function for converting RawInt -> Value::Xint,
// and mapping to the right error type
fn parse_int(raw: RawInt) -> Result<Value, CowParseError> {
    // Note: the string slice doesn't contain the '-' character, so we
    // need to handle that ourselves.
    let posint = u64::from_str_radix(raw.slice, raw.base)
        .map_err(|_| parse_error(ErrorKind::MalformedInteger, raw.slice))?;

    if raw.neg {
        // i64 has a larger positive range than negative range, so if we
        // survive the conversion to i64 then unary `-` must succeed.
        let negint: i64 = try_into_int(posint, raw.slice)?;
        Ok(Value::Nint(-negint))
    } else {
        Ok(Value::Uint(posint))
    }
}

// A rough approximation of nom's offset() function, allowing us to
// do something like nom's recognize() without losing the inner types.
fn offset(whole: &str, part: &str) -> usize {
    part.as_ptr() as usize - whole.as_ptr() as usize
}

// This is similar to nom's `recognize` function.
// The difference is that it doesn't throw away the inner parser's result;
// it returns a tuple (slice, result) so you can have both.
fn recognizer<'a, O, E, F>(
    mut parser: F,
) -> impl FnMut(&'a str) -> nom::IResult<&'a str, (&'a str, O), E>
where
    E: nom::error::ParseError<&'a str>,
    F: FnMut(&'a str) -> nom::IResult<&'a str, O, E>,
{
    move |input: &'a str| match parser(input) {
        Ok((i, output)) => {
            let index = offset(input, i);
            let output_slice = &input[..index];
            let output_tuple = (output_slice, output);
            Ok((i, output_tuple))
        }
        Err(e) => Err(e),
    }
}

// int ["." fraction] ["e" exponent ]
// (must have at least one of the latter two to be a float)
fn float_or_int(input: &str) -> JResult<&str, Value> {
    let f = recognizer(tuple((int, opt(dot_fraction), opt(e_exponent))));
    map_res_fail(f, |(recognized, output)| {
        let (firstint, frac, exp) = output;

        // If we picked up a '.' or 'e' we are parsing a float; if neither we
        // are parsing an integer.
        let dot_or_e = frac.is_some() || exp.is_some();
        if dot_or_e {
            parse_float(recognized)
        } else {
            parse_int(firstint)
        }
    })(input)
}

// bytes = [bsqual] %x27 *BCHAR %x27
// BCHAR = %x20-26 / %x28-5B / %x5D-10FFFD / SESC / CRLF
// bsqual = "h" / "b64"
//
// Byte strings can come in 3 forms:
// - 'abc\n' <= utf8 string interpreted as a byte string (and escaping is allowed)
// - h'1234' or h'12 34' <= hex (base16) bytes; all whitespace is ignored.
// - 'SGVsbG8gd29ybGQ=' <= base64 encoded byte string.
// Also, byte strings can be concatenated, i.e. 'Hello ' 'world' == 'Hello world'.
// See the RFC for details.

#[rustfmt::skip]
fn is_unescaped_bchar(c: char) -> bool {
    let ranges = [
        (0x20 ..= 0x26),
        (0x28 ..= 0x5B),
        (0x5D ..= 0x7E),
        (0x80 ..= 0x10FFD),
    ];
    let cv = c as u32;

    ranges.iter().any(|range| range.contains(&cv))
}

// One or more unescaped byte-string characters
#[rustfmt::skip]
fn unescaped_bchar(input: &str) -> JResult<&str, &str> {
    take_while1(is_unescaped_bchar)
    (input)
}

// FIXME: should also permit included CRLF
// Zero or more byte-string characters
#[rustfmt::skip]
fn bchar(input: &str) -> JResult<&str, &str> {
    recognize(
        many0(
            alt((
                unescaped_bchar,
                sesc
            ))
        )
    )
    (input)
}

// This is basically identical to `text_literal` except that
// it uses single-quotes.
#[rustfmt::skip]
fn bytestring_utf8(input: &str) -> JResult<&str, String> {
    let f = delimited(
        tag("'"),
        bchar,
        tag("'")
    );

    map_res_fail(f, |s| {
        unescape(s).map_err(|_| parse_error(ErrorKind::MalformedText, s) )
    })
    (input)
}

// This doesn't check that only the right characters are used;
// that will be done later during parsing of the string.
#[rustfmt::skip]
fn bytestring_hex(input: &str) -> JResult<&str, &str> {
    delimited(
        tag("h'"),
        bchar,
        tag("\'")
    )(input)
}

// This doesn't check that only the right characters are used;
// that will be done later during parsing of the string.
#[rustfmt::skip]
fn bytestring_base64(input: &str) -> JResult<&str, &str> {
    delimited(
        tag("b64'"),
        bchar,
        charx('\'')
    )(input)
}

// A helper function for parsing hex digits to bytes, while
// ignoring whitespace and mapping to the right error type.
fn parse_hex(s: &str) -> Result<Vec<u8>, CowParseError> {
    // strip whitespace
    // FIXME: this consumes more chars than the RFC says we should.
    let s: String = s.chars().filter(|c| !c.is_ascii_whitespace()).collect();

    hex::decode(&s).map_err(|_| parse_error(ErrorKind::MalformedHex, s))
}

#[rustfmt::skip]
fn bytestring(input: &str) -> JResult<&str, Vec<u8>> {
    alt((
        map(bytestring_utf8, |s| s.as_bytes().into()),
        map_res_fail(bytestring_hex, parse_hex),
        map_res_fail(bytestring_base64, |s| {
            use base64::{engine::general_purpose::URL_SAFE, Engine as _};
            URL_SAFE.decode(s).map_err(|_| {
                parse_error(ErrorKind::MalformedBase64, s)
            })
        }),
    ))
    (input)
}

// text = %x22 *SCHAR %x22
// SCHAR = %x20-21 / %x23-5B / %x5D-7E / %x80-10FFFD / SESC
// SESC = "\" (%x20-7E / %x80-10FFFD)

#[rustfmt::skip]
fn is_unescaped_schar(c: char) -> bool {
    let ranges = [
        (0x20 ..= 0x21),
        (0x23 ..= 0x5B),
        (0x5D ..= 0x7E),
        (0x80 ..= 0x10FFD),
    ];
    let cv = c as u32;

    ranges.iter().any(|range| range.contains(&cv))
}

// One or more unescaped text characters
#[rustfmt::skip]
fn unescaped_schar(input: &str) -> JResult<&str, &str> {
    take_while1(is_unescaped_schar)
    (input)
}

// A single escaped character
#[rustfmt::skip]
fn sesc(input: &str) -> JResult<&str, &str> {
    // FIXME: allow only (%x20-7E / %x80-10FFFD)
    preceded(charx('\\'), recognize(anychar))
    (input)
}

// Zero or more text characters
#[rustfmt::skip]
fn schar(input: &str) -> JResult<&str, &str> {
    recognize(
        many0(
            alt((
                unescaped_schar,
                sesc
            ))
        )
    )
    (input)
}

#[rustfmt::skip]
fn text_literal(input: &str) -> JResult<&str, String> {
    let f = delimited(
        charx('"'),
        schar,
        charx('"')
    );

    map_res_fail(f, |s| {
        unescape(s).map_err(|_| parse_error(ErrorKind::MalformedText, s) )
    })
    (input)
}

// value = number / text / bytes
// number = hexfloat / (int ["." fraction] ["e" exponent ])
#[rustfmt::skip]
fn value(input: &str) -> JResult<&str, Value> {
    alt((
        float_or_int,
        map(text_literal, Value::Text),
        map(bytestring, Value::Bytes),
    ))(input)
}

// Match the ": Y" part of an "X : Y" memberkey.
#[rustfmt::skip]
fn grpent_memberkey_tail(input: &str) -> JResult<&str, Type> {
    preceded(
        pair(
            tag(":"),
            ws,
        ),
        ty,
    )(input)
}

// A helper function for grpent_member for assembling the Member
// from the key and value when using the "X:Y" syntax.
//
// The key must be a Type2::Value or Type2::Typename or this function
// will panic.
fn assemble_basic_member(key: Type1, value: Type) -> Result<Member, CowParseError<'static>> {
    let member_key = match key {
        Type1::Simple(Type2::Value(v)) => MemberKeyVal::Value(v),
        Type1::Simple(Type2::Typename(s)) => {
            // Because we used the "key:value" syntax, the typename may
            // only be a plain string, without generic parameters.
            // If generic parameters are desired, the "key=>value" syntax
            // would be required (and we would not have arrived here).

            if !s.generic_args.is_empty() {
                return Err(parse_error(
                    ErrorKind::Unparseable,
                    "Bareword with generic arguments",
                ));
            }
            MemberKeyVal::Bareword(s.name)
        }
        _ => panic!("assemble_basic_member wrong key type"),
    };
    Ok(Member {
        key: Some(MemberKey {
            val: member_key,
            cut: true,
        }),
        value,
    })
}

// grpent = [occur S] [memberkey S] type
//        / [occur S] groupname [genericarg]  ; preempted by above
//        / [occur S] "(" S group S ")"
//
// memberkey = type1 S ["^" S] "=>"
//           / bareword S ":"
//           / value S ":"
//
// Parsing the memberkey syntax is tricky, because it's easy to fall into
// 2^N iterations due to backtracking.
//
// The problem arises when we see something like "[[[[ int ]]]]".
// If we first search for a memberkey, we can go on an adventure trying
// many different partial matches of the first memberkey type
// ( type1 S ["^" S] "=>" ) before discarding that work because there is
// no trailing "=>".
//
// The root problem is that "int" will match both the with-memberkey and
// without-memberkey grammars, because it's both a type and a type1.
// if we start wrapping it in N sets of array-brackets or map-brackets,
// there are 2^N possible ways for that type1 to be parsed before we
// discover the missing "=>".
//
// The solution is to change the parser to match this equivalent grammar:
//
// grpent = [occur S] member_type1
//        / [occur S] groupname [genericarg]  ; preempted by above
//        / [occur S] "(" S group S ")"
//
// member_type1 = bareword S ":" type
//              / value S ":" type
//              / type1 S ["^" S] "=>" type
//
// In this grammar, it's easier to see that the bareword and value nodes
// also match the type1 node.  We avoid backtracking by matching the
// type1 node first, and then peeking at which variant it is to see if
// the ":" syntax is allowed.  If it's an id (bareword) or value, then
// we attempt to match that syntax before trying the others.
//
#[rustfmt::skip]
fn grpent_member(input: &str) -> JResult<&str, Member> {

    // The leading Type1 is required for this parser.
    let (input, first_type1) = terminated(type1, ws)(input)?;

    // If the type1 matches is a plain value or typename (aka id), then we may
    // be looking at a memberkey followed by a ":".  That syntax isn't allowed
    // for other Type1 patterns.

    match first_type1 {
        Type1::Simple(Type2::Value(_)) |
        Type1::Simple(Type2::Typename(_)) => {
            // Next, try to match ":" ws ty
            if let Ok((input, tail_type)) = grpent_memberkey_tail(input) {
                let member = assemble_basic_member(first_type1, tail_type);
                match member {
                    Ok(member) => return Ok((input, member)),
                    Err(e) => return Err(nom::Err::Failure(e)),
                }

            }
        }
        // "X:Y" isn't allowed when X is some other Type1 variant.
        _ => {}
    }

    // grpent with memberkey followed by "=>" (with optional cut symbol "^")
    // should return a Member, with key: Some(MemberKey{val, cut})
    // and value: Type

    if let Ok((input, matched_tuple)) = tuple((
        opt(terminated(tag("^"), ws)),
        tag("=>"),
        ws,
        ty,
    ))(input) {
        let (cut, _, _, val) = matched_tuple;
        let member = Member {
            key: Some(MemberKey {
                val: MemberKeyVal::Type1(first_type1),
                cut: cut.is_some(),
            }),
            value: val,
        };
        return Ok((input, member));
    }

    // grpent without memberkey
    // should return a Member, with key: None and value: Type containing Vec<Type1>
    // This is a lot like the fn `ty` but we will concatenate this with first_type1

    if let Ok((input, mut ty1s)) = many0(
        preceded(
            delimited(ws, tag("/"), ws),
            type1
        )
    )(input) {
        // insert the first type1 (from the top of this function)
        ty1s.insert(0, first_type1);
        let member = Member {
            key: None,
            value: Type(ty1s),
        };
        return Ok((input, member));
    }

    Err(nom::Err::Error(parse_error(ErrorKind::Unparseable, "grpent_member")))
}

#[rustfmt::skip]
fn grpent_parens(input: &str) -> JResult<&str, Group> {
    delimited(
        charx('('),
        delimited(
            ws,
            group,
            ws,
        ),
        charx(')')
    )(input)
}

#[rustfmt::skip]
fn grpent_val(input: &str) -> JResult<&str, GrpEntVal> {
    alt((
        map(grpent_member, GrpEntVal::Member),
        map(ident, |s| GrpEntVal::Groupname(s.into())),
        map(grpent_parens, GrpEntVal::Parenthesized),
    ))
    (input)
}

// A helper function that does u64->usize conversion, returning
// CowParseError(MalformedInteger) on failure.
fn try_into_int<T, U>(x: T, source: &str) -> Result<U, CowParseError>
where
    U: TryFrom<T>,
{
    <U>::try_from(x).map_err(|_| parse_error(ErrorKind::MalformedInteger, source))
}

// occur = [uint] "*" [uint]
//       / "+"
//       / "?"
#[rustfmt::skip]
fn occur_star(input: &str) -> JResult<&str, Occur> {
    let f = tuple((
        opt(uint_u64),
        tag("*"),
        opt(uint_u64),
    ));
    // FIXME: it's really not the parser's business to be inventing an upper
    // limit here.  Plus, the use of usize::MAX is kind of gross.
    // The parser should leave these as Option and leave it to others to
    // decide what to do with that.
    map_res(f, |tup| -> Result<Occur, CowParseError> {
        if tup.0.is_none() && tup.2.is_none() {
            Ok(Occur::ZeroOrMore)
        } else {
            let lower: usize = match tup.0 {
                Some(n) => try_into_int(n, input)?,
                None => 0,
            };
            let upper: usize = match tup.2 {
                Some(n) => try_into_int(n, input)?,
                None => usize::MAX,
            };
            Ok(Occur::Numbered(lower, upper))
        }
    })
    (input)
}

#[rustfmt::skip]
fn occur(input: &str) -> JResult<&str, Occur> {
    alt((
        occur_star,
        valuex(Occur::OneOrMore, tag("+")),
        valuex(Occur::Optional, tag("?"))
    ))
    (input)
}

// grpent = [occur S] [memberkey S] type
//        / [occur S] groupname [genericarg]  ; preempted by above
//        / [occur S] "(" S group S ")"

#[rustfmt::skip]
fn grpent(input: &str) -> JResult<&str, GrpEnt> {
    let f = pair(
        opt(terminated(occur, ws)),
        grpent_val
    );
    map(f, |(occur, val)| GrpEnt{ occur, val } )
    (input)
}

// grpchoice = zero-or-more "grpent optional-comma"
#[rustfmt::skip]
fn grpchoice(input: &str) -> JResult<&str, GrpChoice> {
    let f = many0(
        terminated(grpent, optcom)
    );
    map(f, GrpChoice)
    (input)
}

// group = grpchoice *(S "//" S grpchoice)
#[rustfmt::skip]
fn group(input: &str) -> JResult<&str, Group> {

    // It would have been great to write this as
    //  separated_list1(
    //      tag("//"),
    //      grpchoice)
    // but separated_list1 returns an error if the
    // list-item succeeds on "", which grpchoice does.

    let f = pair(
        grpchoice,
        many0(preceded(
            delimited(
                ws,
                tag("//"),
                ws,
            ),
            grpchoice
        ))
    );

    map(f, |(first, mut rest)| {
        // Build a new vector containing all the grpchoice elements.
        let mut gcs = vec![first];
        gcs.append(&mut rest);
        Group(gcs)
    })(input)
}

// "(" S type S ")"
#[rustfmt::skip]
fn type2_parens(input: &str) -> JResult<&str, Type> {
    delimited(
        charx('('),
        delimited(
            ws,
            ty,
            ws,
        ),
        charx(')')
    )(input)
}

// "{" S group S "}"
#[rustfmt::skip]
fn type2_map(input: &str) -> JResult<&str, Group> {
    delimited(
        charx('{'),
        delimited(
            ws,
            group,
            ws,
        ),
        charx('}')
    )(input)
}

// "[" S group S "]"
#[rustfmt::skip]
fn type2_array(input: &str) -> JResult<&str, Group> {
    delimited(
        charx('['),
        delimited(
            ws,
            group,
            ws,
        ),
        charx(']')
    )(input)
}

// "~" S typename [genericarg]
#[rustfmt::skip]
fn type2_unwrap(input: &str) -> JResult<&str, NameGeneric> {
    preceded(
        tag("~"),
        preceded(
            ws,
            name_generic
        )
    )
    (input)
}

// "&" S groupname [genericarg]
// I call the & operator "choice-ify". RFC 8610 (see 2.2.2.2) doesn't say
// what that operator should be called, and "group choice" already means
// something different.
#[rustfmt::skip]
fn type2_choiceify(input: &str) -> JResult<&str, NameGeneric> {
    preceded(
        tag("&"),
        preceded(
            ws,
            name_generic
        )
    )
    (input)
}

// "&" S "(" S group S ")"
#[rustfmt::skip]
fn type2_choiceify_inline(input: &str) -> JResult<&str, Group> {
    preceded(
        tag("&"),
        preceded(
            ws,
            delimited(
                charx('('),
                delimited(
                    ws,
                    group,
                    ws,
                ),
                charx(')')
            )
        )
    )
    (input)
}

// type2 = value
//       / typename [genericarg]
//       / "(" S type S ")"
//       / "{" S group S "}"
//       / "[" S group S "]"
//       / "~" S typename [genericarg]
//       / "&" S "(" S group S ")"
//       / "&" S groupname [genericarg]
//       / "#" "6" ["." uint] "(" S type S ")"
//       / "#" DIGIT ["." uint]
//       / "#"
#[rustfmt::skip]
fn type2(input: &str) -> JResult<&str, Type2> {
    alt((
        map(value, Type2::Value),
        map(name_generic, Type2::Typename),
        map(type2_parens, Type2::Parethesized),
        map(type2_map, Type2::Map),
        map(type2_array, Type2::Array),
        map(type2_unwrap, Type2::Unwrap),
        map(type2_choiceify_inline, Type2::ChoiceifyInline),
        map(type2_choiceify, Type2::Choiceify),
    ))
    (input)
}

// Returns the string containing the control identifier
#[rustfmt::skip]
fn control_op(input: &str) -> JResult<&str, &str> {
    preceded(
        tag("."),
        ident
    )
    (input)
}

// Returns the range or control operator string
// (either ".." or "..." or the control identifier)
#[rustfmt::skip]
fn range_or_control_op(input: &str) -> JResult<&str, (&str, Type2)> {
    pair(
        alt((
            tag("..."),
            tag(".."),
            control_op
        )),
        preceded(
            ws,
            type2
        )
    )
    (input)
}

// type1 = type2 [S (rangeop / ctlop) S type2]
#[rustfmt::skip]
fn type1(input: &str) -> JResult<&str, Type1> {
    let f = pair(
        type2,
        opt(
            preceded(
                ws,
                range_or_control_op
            )
        )
    );
    map(f, |ty1| match ty1 {
        (ty2, None) => Type1::Simple(ty2),
        (start, Some(("..", end))) => Type1::Range(TypeRange {
            start,
            inclusive: true,
            end,
        }),
        (start, Some(("...", end))) => Type1::Range(TypeRange {
            start,
            inclusive: false,
            end,
        }),
        (target, Some((op, arg))) => Type1::Control(TypeControl {
            target,
            op: op.into(),
            arg,
        }),
    })
    (input)
}

// type = type1 [ / type1 ... ]  (skipping over type1 for now)
#[rustfmt::skip]
fn ty(input: &str) -> JResult<&str, Type> {
    let f = separated_list1(
        delimited(ws, tag("/"), ws),
        type1
    );
    map(f, Type)
    (input)
}

// rule = typename [genericparm] S assignt S type
//      / groupname [genericparm] S assigng S grpent
// Note that the first one ends with "type", while
// the second one ends with "group".
// So "foo = (bar)" will be forced down the second path.
//
// The most efficient parsing would be
// 1. name [genericparm] ws
// 2. = type
//    = grpent
//    /= type
//    //= grpent
//

// This is the right side of a rule: one of:
//     assignt S type
//     assigng S grpent
#[rustfmt::skip]
fn rule_val(input: &str) -> JResult<&str, RuleVal> {
    let f = separated_pair(
        tag("="),
        ws,
        alt((
            map(ty, RuleVal::AssignType),
            map(grpent, RuleVal::AssignGroup)
        ))
    );
    // We're just throwing away the operator for now, but we'll need it
    // later when we implement extend operators /= //=
    map(f, |(_op, val)| val )
    (input)
}

// genericparm = "<" S id S *("," S id S ) ">"
#[rustfmt::skip]
fn generic_parm(input: &str) -> JResult<&str, Vec<&str>> {
    delimited(
        pair(tag("<"), ws),
        separated_list1(
            pair(tag(","), ws),
            terminated(ident, ws)),
        tag(">"),
    )(input)
}

// genericarg = "<" S type1 S *("," S type1 S ) ">"
#[rustfmt::skip]
fn generic_arg(input: &str) -> JResult<&str, Vec<Type1>> {
    delimited(
        pair(tag("<"), ws),
        separated_list1(
            pair(tag(","), ws),
            terminated(type1, ws)),
        tag(">"),
    )(input)
}

// A type or group name, followed by optional generic arguments.
#[rustfmt::skip]
fn name_generic(input: &str) -> JResult<&str, NameGeneric> {
    let f = pair(ident, opt(generic_arg));
    map(f, |(name, generic)| {
        // Replace None with empty Vec.
        let generic_args = generic.unwrap_or_default();
        NameGeneric {
            name: name.to_string(),
            generic_args,
        }
    })
    (input)
}

#[rustfmt::skip]
fn rule(input: &str) -> JResult<&str, Rule> {
    let f = separated_pair(
        pair(
            ident,
            opt(generic_parm)
        ),
        ws,
        rule_val
    );
    map(f, |((name, gp), val)| Rule {
        name: name.into(),
        // turn Vec<&str> into Vec<String>
        generic_parms: gp.unwrap_or_default().drain(..).map(|s| s.to_string()).collect(),
        val,
    })(input)
}

// cddl = S 1*(rule S)
#[rustfmt::skip]
fn cddl(input: &str) -> JResult<&str, Cddl> {
    let f = preceded(ws,
        many1(
            terminated(rule, ws)
        )
    );
    map(f, |r| Cddl{rules: r})
    (input)
}

#[rustfmt::skip]
fn cddl_slice(input: &str) -> JResult<&str, CddlSlice> {
    let f = preceded(ws,
        many1(
            terminated(
                map(recognizer(rule), |(s, r)| {
                    (r, s.to_string())
                }),
                ws
            )
        )
    );
    map(f, |r| CddlSlice{rules: r})
    (input)
}

/// The main entry point for parsing CDDL text.
///
/// If successful, it will return a [`Cddl`] instance containing all the rules
/// from the input text.
///
/// # Examples
/// ```
/// use cddl_cat::parse_cddl;
///
/// let input = "map = { name: tstr }";
/// assert!(parse_cddl(input).is_ok());
/// ```
///
pub fn parse_cddl(input: &str) -> Result<Cddl, ParseError> {
    let result = all_consuming(cddl)(input)?;
    Ok(result.1)
}

/// An entry point for parsing CDDL text, preserving rule strings
///
/// This operates exactly like [`parse_cddl`], but stores a copy of the rule's
/// original CDDL text.
pub fn slice_parse_cddl(input: &str) -> Result<CddlSlice, ParseError> {
    let result = all_consuming(cddl_slice)(input)?;
    Ok(result.1)
}

// Useful utilities for testing the parser.
#[cfg(test)]
#[macro_use]
mod test_utils {
    use super::*;

    // Generate a Vec<String> the same way we would generate a Vec<&str>.
    macro_rules! vec_strings {
        ($($str:expr),*) => ({
            vec![$(String::from($str),)*] as Vec<String>
        });
    }

    // Given a string, generate a NameGeneric containing a type name.
    impl From<&str> for NameGeneric {
        fn from(s: &str) -> Self {
            NameGeneric {
                name: s.to_string(),
                generic_args: Vec::new(),
            }
        }
    }

    // Given a string, generate a Type2 containing a type name.
    impl From<&str> for Type2 {
        fn from(s: &str) -> Self {
            Type2::Typename(s.into())
        }
    }

    // Given a string, generate a Type1 containing a type name.
    impl From<&str> for Type1 {
        fn from(s: &str) -> Self {
            Type1::Simple(Type2::Typename(s.into()))
        }
    }

    // Given a Value, generate a Type1.
    impl From<Value> for Type1 {
        fn from(v: Value) -> Self {
            Type1::Simple(Type2::Value(v))
        }
    }

    // Given a string, generate a Member containing a no-key value.
    impl From<&str> for Member {
        fn from(s: &str) -> Self {
            Member {
                key: None,
                value: s.into(),
            }
        }
    }

    // Given a Value, generate a Member containing a no-key value.
    impl From<Value> for Member {
        fn from(v: Value) -> Self {
            Member {
                key: None,
                value: Type1::from(v).into(),
            }
        }
    }

    // Given a string, generate a GrpEnt containing that type name.
    impl From<&str> for GrpEnt {
        fn from(s: &str) -> GrpEnt {
            GrpEnt {
                occur: None,
                val: GrpEntVal::Member(s.into()),
            }
        }
    }

    // A trait for generating literals.
    pub trait CreateLiteral {
        fn literal(self) -> Value;
    }

    // Create a literal string.
    impl CreateLiteral for &str {
        fn literal(self) -> Value {
            Value::Text(self.to_string())
        }
    }

    // Create a literal integer.
    impl CreateLiteral for i64 {
        fn literal(self) -> Value {
            if self >= 0 {
                Value::Uint(self as u64)
            } else {
                Value::Nint(self)
            }
        }
    }

    pub fn bareword(s: &str) -> MemberKeyVal {
        MemberKeyVal::Bareword(s.into())
    }

    impl From<Value> for Type2 {
        fn from(x: Value) -> Type2 {
            Type2::Value(x)
        }
    }

    // Given a Value (a literal), generate a MemberKeyVal.
    impl From<Value> for MemberKeyVal {
        fn from(k: Value) -> MemberKeyVal {
            MemberKeyVal::Value(k)
        }
    }

    // Given a Type1, generate a MemberKeyVal.
    impl From<Type1> for MemberKeyVal {
        fn from(t: Type1) -> MemberKeyVal {
            MemberKeyVal::Type1(t)
        }
    }

    // Given a string, generate a MemberKeyVal (treating it as a type name).
    impl From<&str> for MemberKeyVal {
        fn from(k: &str) -> MemberKeyVal {
            MemberKeyVal::Type1(k.into())
        }
    }

    #[derive(Copy, Clone)]
    pub enum MemberCut {
        Cut,
        NoCut,
    }
    pub use MemberCut::*;

    impl From<MemberCut> for bool {
        fn from(c: MemberCut) -> bool {
            match c {
                Cut => true,
                NoCut => false,
            }
        }
    }

    pub fn kv_member<K, V>(k: K, v: V, cut: MemberCut) -> Member
    where
        K: Into<MemberKeyVal>,
        V: Into<Type1>,
    {
        let v: Type1 = v.into();
        Member {
            key: Some(MemberKey {
                val: k.into(),
                cut: cut.into(),
            }),
            value: v.into(),
        }
    }

    pub fn kv<K, V>(k: K, v: V, cut: MemberCut) -> GrpEnt
    where
        K: Into<MemberKeyVal>,
        V: Into<Type1>,
    {
        GrpEnt {
            occur: None,
            val: GrpEntVal::Member(kv_member(k, v, cut)),
        }
    }

    pub fn gen_group<T: Into<GrpEnt>>(mut members: Vec<T>) -> Group {
        // convert the members into individual GrpEnt structs
        let grpents: Vec<GrpEnt> = members.drain(..).map(|x| x.into()).collect();
        // construct a Group containing one GrpChoice.
        Group(vec![GrpChoice(grpents)])
    }

    pub fn gen_array<T: Into<GrpEnt>>(members: Vec<T>) -> Type1 {
        Type1::Simple(Type2::Array(gen_group(members)))
    }

    pub fn gen_map<T: Into<GrpEnt>>(members: Vec<T>) -> Type1 {
        Type1::Simple(Type2::Map(gen_group(members)))
    }

    // Generate a single-Type1 Type struct.
    impl From<Type1> for Type {
        fn from(x: Type1) -> Self {
            Type(vec![x])
        }
    }

    // Generate a single-Type1 Type struct from a plain string (as a type name).
    impl From<&str> for Type {
        fn from(s: &str) -> Self {
            Type(vec![Type1::from(s)])
        }
    }

    // Create a type name with generic arguments
    pub fn generic<T: Into<Type1>>(name: &str, mut generic_args: Vec<T>) -> Type1 {
        Type1::Simple(Type2::Typename(NameGeneric {
            name: name.to_string(),
            generic_args: generic_args.drain(..).map(|x| x.into()).collect(),
        }))
    }
}

#[cfg(test)]
mod tests {
    use super::test_utils::*;
    use super::*;

    #[test]
    fn test_whitespace() {
        let cddl = "  ; a comment\n        \r\n; another;;;comment\n  ";
        let (remainder, _result) = ws(cddl).unwrap();
        assert_eq!(remainder, "");
    }

    #[test]
    fn test_ident() {
        assert_eq!(ident("a"), Ok(("", "a")));
        assert_eq!(ident("a1"), Ok(("", "a1")));
        assert_eq!(ident("a.1"), Ok(("", "a.1")));
        assert_eq!(ident("a1."), Ok((".", "a1")));
        assert_eq!(ident("@a1"), Ok(("", "@a1")));
        assert_eq!(ident("a..b"), Ok(("", "a..b")));
        assert!(ident("1a").is_err());
    }

    #[test]
    fn test_uint() {
        assert_eq!(uint_u64("999"), Ok(("", 999)));
        assert_eq!(uint_u64("0"), Ok(("", 0)));
        assert_eq!(uint_u64("0x100"), Ok(("", 256)));
        assert_eq!(uint_u64("0b101"), Ok(("", 5)));
        // We're not supposed to parse leading zeros.
        assert_eq!(uint_u64("00"), Ok(("0", 0)));
    }

    #[test]
    fn test_float_or_int() {
        assert_eq!(float_or_int("0.0"), Ok(("", Value::Float(0.0))));
        assert_eq!(float_or_int("1e99"), Ok(("", Value::Float(1e99))));
        assert_eq!(float_or_int("-1e-99"), Ok(("", Value::Float(-1e-99))));
        assert_eq!(float_or_int("123"), Ok(("", Value::Uint(123))));
        assert_eq!(float_or_int("-123"), Ok(("", Value::Nint(-123))));
        assert_eq!(float_or_int("1e"), Ok(("e", Value::Uint(1))));
        assert_eq!(float_or_int("1."), Ok((".", Value::Uint(1))));
        assert!(float_or_int("abc").is_err());

        assert_eq!(float_or_int("0x100"), Ok(("", Value::Uint(256))));
        assert_eq!(float_or_int("0b101"), Ok(("", Value::Uint(5))));
        // We're not supposed to parse leading zeros.
        assert_eq!(float_or_int("00"), Ok(("0", Value::Uint(0))));

        assert_eq!(float_or_int("-0x100"), Ok(("", Value::Nint(-256))));
        assert_eq!(float_or_int("-0b101"), Ok(("", Value::Nint(-5))));

        // While this is allowed in the CDDL grammar, it doesn't make logical sense
        // so we want to return an error.
        assert!(float_or_int("0b1e99").is_err());
        assert!(float_or_int("0b1.1").is_err());
    }

    #[test]
    fn test_bytestring() {
        let result1 = bytestring("'abc'");
        let result = format!("{:?}", result1);
        assert_eq!(result, r#"Ok(("", [97, 98, 99]))"#);

        // Same thing, in hex format
        assert_eq!(result1, bytestring("h'61 62 63'"));
        assert_eq!(result1, bytestring("h' 6 1626 3  '"));

        // Same thing, in base64 format
        assert_eq!(result1, bytestring("b64'YWJj'"));

        // bytestring in UTF-8 with escapes
        assert_eq!(bytestring(r#"'a\nb'"#), Ok(("", "a\nb".into())));
        assert_eq!(bytestring(r#"'\uD834\uDD1E'"#), Ok(("", "𝄞".into())));

        // Non-text bytes
        let result2 = vec![0u8, 0xFF, 1, 0x7F];
        assert_eq!(Ok(("", result2.clone())), bytestring("h'00FF017f'"));
        assert_eq!(Ok(("", result2)), bytestring("b64'AP8Bfw=='"));

        // Empty inputs
        assert_eq!(Ok(("", vec![])), bytestring("h''"));
        assert_eq!(Ok(("", vec![])), bytestring("b64''"));

        fn fail_kind(e: nom::Err<CowParseError>) -> ErrorKind {
            match e {
                nom::Err::Failure(e) => e.kind,
                _ => panic!("expected nom::err::Failure, got {:?}", e),
            }
        }

        // Bad hex character
        assert_eq!(
            fail_kind(bytestring("h'0g1234'").unwrap_err()),
            ErrorKind::MalformedHex
        );

        // Bad base64 character "!"
        assert_eq!(
            fail_kind(bytestring("b64'AP!Bfw=='").unwrap_err()),
            ErrorKind::MalformedBase64
        );

        // wrong flavor of base64: CDDL requires the "base64url" encoding.
        assert_eq!(
            // base64 encoding of FBEF00 using the wrong encoder.
            fail_kind(bytestring("b64'++8A'").unwrap_err()),
            ErrorKind::MalformedBase64
        );
        assert_eq!(
            // base64 encoding of FFFFFF using the wrong encoder.
            fail_kind(bytestring("b64'////'").unwrap_err()),
            ErrorKind::MalformedBase64
        );
    }

    #[test]
    fn test_text() {
        assert!(is_unescaped_schar('A'));
        assert!(is_unescaped_schar('の'));
        assert!(is_unescaped_schar(std::char::from_u32(0x10FF0).unwrap()));
        assert!(!is_unescaped_schar(0x7Fu8 as char));

        assert_eq!(unescaped_schar("Aの"), Ok(("", "Aの")));

        assert_eq!(sesc(r#"\n"#), Ok(("", "n")));
        assert_eq!(sesc(r#"\nn"#), Ok(("n", "n")));
        assert_eq!(sesc(r#"\の"#), Ok(("", "の")));

        // FIXME: sesc is allowing characters it shouldn't.
        // assert_eq!(sesc("\\\x7F"), Ok(("\\\x7F", "")));

        assert_eq!(schar(r#"Ab! \c の \\"#), Ok(("", r#"Ab! \c の \\"#)));
        assert_eq!(schar(r#"a\nb"#), Ok(("", r#"a\nb"#)));
        assert_eq!(schar("a\nb"), Ok(("\nb", "a")));

        assert!(text_literal("\"a\nb").is_err());
        assert!(text_literal("abc").is_err());

        assert_eq!(text_literal(r#""""#), Ok(("", "".into())));
        assert_eq!(text_literal(r#""a\nb""#), Ok(("", "a\nb".into())));
        assert_eq!(text_literal(r#""\uD834\uDD1E""#), Ok(("", "𝄞".into())));
        assert_eq!(text_literal(r#""の""#), Ok(("", "の".into())));
    }

    #[test]
    fn test_value() {
        assert_eq!(value("123"), Ok(("", Value::Uint(123))));
        assert_eq!(value(r#""abc""#), Ok(("", Value::Text("abc".into()))));
        assert!(value("abc").is_err());
    }

    #[test]
    fn test_member() {
        let result = grpent_member("a:b");
        assert_eq!(
            result.unwrap().1,
            kv_member(MemberKeyVal::Bareword("a".into()), "b", Cut)
        );

        let result = grpent_member("foo");
        assert_eq!(result.unwrap().1, "foo".into());

        let result = grpent_member("a => b");
        assert_eq!(result.unwrap().1, kv_member("a", "b", NoCut));

        let result = grpent_member("42 ^ => b");
        assert_eq!(
            result.unwrap().1,
            kv_member(Type1::from(42.literal()), "b", Cut)
        );

        let result = grpent_member("abc<T> => def");
        assert_eq!(
            result.unwrap().1,
            kv_member(generic("abc", vec!["T"]), "def", NoCut)
        );

        // Generic arguments not allowed with ":"
        grpent_member("abc<T> : def").unwrap_err();
    }

    #[test]
    fn test_grpent_parens() {
        let result = grpent_parens("()");
        assert_eq!(result.unwrap().1, Group(vec![GrpChoice(vec![])]));
    }

    #[test]
    fn test_grpent_val() {
        let result = grpent_val("foo");
        assert_eq!(result.unwrap().1, GrpEntVal::Member("foo".into()));

        let result = grpent_val("17");
        assert_eq!(result.unwrap().1, GrpEntVal::Member(17.literal().into()));
    }

    #[test]
    fn test_occur() {
        assert_eq!(occur("?"), Ok(("", Occur::Optional)));
        assert_eq!(occur("+"), Ok(("", Occur::OneOrMore)));
        assert_eq!(occur("*"), Ok(("", Occur::ZeroOrMore)));
        assert_eq!(occur("*9"), Ok(("", Occur::Numbered(0, 9))));
        assert_eq!(occur("7*"), Ok(("", Occur::Numbered(7, usize::MAX))));
        assert_eq!(occur("7*9"), Ok(("", Occur::Numbered(7, 9))));
        assert_eq!(occur("0b100*0x10"), Ok(("", Occur::Numbered(4, 16))));
    }

    #[test]
    fn test_grpent() {
        let result = grpent("foo").unwrap();
        assert_eq!(result.1, "foo".into());

        let result = grpent("foo: bar").unwrap();
        assert_eq!(
            result.1,
            kv(MemberKeyVal::Bareword("foo".into()), "bar", Cut)
        );
    }

    #[test]
    fn test_grpchoice_empty() {
        let result = grpchoice("").unwrap();
        assert_eq!(result.1, GrpChoice(vec![]));
    }

    #[test]
    fn test_group_empty() {
        let result = group("").unwrap();
        assert_eq!(result.1, Group(vec![GrpChoice(vec![])]));
    }

    #[test]
    fn test_type1() {
        let result = type1("1 .. 9");
        assert_eq!(
            result.unwrap().1,
            Type1::Range(TypeRange {
                start: 1.literal().into(),
                end: 9.literal().into(),
                inclusive: true
            })
        );

        let result = type1("0x10 .. 0x1C");
        assert_eq!(
            result.unwrap().1,
            Type1::Range(TypeRange {
                start: 16.literal().into(),
                end: 28.literal().into(),
                inclusive: true
            })
        );

        let result = type1("1 ... 9");
        assert_eq!(
            result.unwrap().1,
            Type1::Range(TypeRange {
                start: 1.literal().into(),
                end: 9.literal().into(),
                inclusive: false
            })
        );

        let result = type1("uint .size 3");
        assert_eq!(
            result.unwrap().1,
            Type1::Control(TypeControl {
                target: "uint".into(),
                op: "size".to_string(),
                arg: 3.literal().into(),
            })
        );

        // RFC8610 2.2.2.1 points out that "min..max" is not a range, but an identifier
        // (because '.' is a valid ident character).
        let result = type2("min..max");
        assert_eq!(result.unwrap().1, "min..max".into());
    }

    #[test]
    fn test_grpchoice() {
        let result = grpchoice("abc").unwrap();
        assert_eq!(result.1, GrpChoice(vec!["abc".into()]));

        let result = grpchoice("abc, def").unwrap();
        assert_eq!(result.1, GrpChoice(vec!["abc".into(), "def".into(),]));
    }

    #[test]
    fn test_generic_parm() {
        assert!(generic_parm("").is_err());

        assert!(generic_parm("<>").is_err());

        let result = generic_parm("<foo>").unwrap();
        assert_eq!(result.1, vec!["foo"]);

        let result = generic_parm("<foo,bar>").unwrap();
        assert_eq!(result.1, vec!["foo", "bar"]);

        let result = generic_parm("< foo , _bar_ >").unwrap();
        assert_eq!(result.1, vec!["foo", "_bar_"]);
    }

    #[test]
    fn test_generic_arg() {
        assert!(generic_arg("").is_err());

        assert!(generic_arg("<>").is_err());

        let result = generic_arg("<foo>").unwrap();
        assert_eq!(result.1, vec!["foo".into()]);

        let result = generic_arg("<foo,bar>").unwrap();
        assert_eq!(result.1, vec!["foo".into(), "bar".into()]);

        let result = generic_arg("< foo , _bar_ >").unwrap();
        assert_eq!(result.1, vec!["foo".into(), "_bar_".into()]);
    }

    #[test]
    fn choiceify() {
        assert_eq!(
            type2("&foo").unwrap().1,
            Type2::Choiceify(NameGeneric {
                name: "foo".into(),
                generic_args: vec![],
            })
        );
        assert_eq!(
            type2("&(a:1)").unwrap().1,
            Type2::ChoiceifyInline(gen_group(vec![kv(bareword("a"), 1.literal(), Cut),]))
        );
    }

    #[test]
    fn test_rule() {
        let result = rule("foo=bar").unwrap().1;

        assert_eq!(
            result,
            Rule {
                name: "foo".into(),
                generic_parms: vec![],
                val: RuleVal::AssignType("bar".into())
            }
        );

        let result = rule("foo=(bar, baz)").unwrap().1;
        assert_eq!(
            result,
            Rule {
                name: "foo".into(),
                generic_parms: vec![],
                val: RuleVal::AssignGroup(GrpEnt {
                    occur: None,
                    val: GrpEntVal::Parenthesized(gen_group(vec!["bar", "baz"])),
                })
            }
        );

        let result = rule("message<t, v> = [t, v]").unwrap().1;
        assert_eq!(
            result,
            Rule {
                name: "message".into(),
                generic_parms: vec_strings!["t", "v"],
                val: RuleVal::AssignType(gen_array(vec!["t", "v"]).into())
            }
        );
    }

    #[test]
    fn test_cddl() {
        let result = parse_cddl("foo = {\"a\": bar,\n b => baz}");

        assert_eq!(
            result.unwrap(),
            Cddl {
                rules: vec![Rule {
                    name: "foo".into(),
                    generic_parms: vec![],
                    val: RuleVal::AssignType(Type(vec![gen_map(vec![
                        kv("a".literal(), "bar", Cut),
                        kv("b", "baz", NoCut)
                    ])]))
                }]
            }
        );
    }

    #[test]
    fn test_cddl_slice() {
        let result = slice_parse_cddl(" foo = { a: tstr } bar = \n[ int ] ").unwrap();
        assert_eq!(result.rules[0].1, "foo = { a: tstr }");
        assert_eq!(result.rules[1].1, "bar = \n[ int ]");
    }

    // FIXME: these are things I discovered while validating cbor.  Move them to their own tests?
    #[test]
    fn test_stuff() {
        parse_cddl("thing = { foo : tstr }").unwrap();
        parse_cddl("bar = (c: int)").unwrap(); // This is a rule containing a group assignment.
        parse_cddl("thing = {agroup empty} agroup = (age: int, name: tstr) empty = ()").unwrap();
        parse_cddl(
            r#"
            address = { delivery }

            delivery = (
            street: tstr, ? "number": uint, city //
            po_box: uint, city //
            per_pickup: true )

            city = (
            name: tstr, zip_code: uint
            )"#,
        )
        .unwrap();
    }

    #[test]
    fn test_errors() {
        let err = parse_cddl("x=9999999999999999999999999999999").unwrap_err();
        assert_eq!(err.kind, ErrorKind::MalformedInteger);

        let err = parse_cddl(r#"x="\ud800""#).unwrap_err();
        assert_eq!(err.kind, ErrorKind::MalformedText);

        let err = parse_cddl("x=h'61 62 6'").unwrap_err();
        assert_eq!(err.kind, ErrorKind::MalformedHex);
    }
}
