token-parser-derive 0.5.0

Derive macros to make structs parsable into tokens
Documentation
use token_parser::{ErrorKind, Parser, Span, Unit};
use token_parser_derive::{Parsable, SymbolParsable};

fn symbol(name: &str) -> Unit {
    Unit::Symbol(name.into(), Span::default())
}

fn list<I: IntoIterator<Item = Unit>>(items: I) -> Unit {
    Unit::Parser(Parser::new(items))
}

fn parse_one<T: token_parser::Parsable<()>>(unit: Unit) -> token_parser::Result<T> {
    let mut parser = Parser::new([unit]);
    parser.parse_next(&())
}

#[derive(Parsable, Debug, PartialEq)]
struct Pair(String, String);

#[derive(Parsable, Debug, PartialEq, Default)]
struct Config {
    label: String,
    flag: bool,
}

#[derive(Parsable, Debug, PartialEq)]
struct Empty;

#[derive(SymbolParsable, Debug, PartialEq)]
struct Flag(bool);

#[derive(Parsable, Debug, PartialEq)]
struct Generic<T>(T);

#[test]
fn tuple_struct() {
    let result: Pair = parse_one(list([symbol("a"), symbol("b")])).unwrap();
    assert_eq!(result, Pair("a".into(), "b".into()));
}

#[test]
fn tuple_struct_missing_element() {
    let error = parse_one::<Pair>(list([symbol("a")])).unwrap_err();
    assert!(matches!(error.kind, ErrorKind::NotEnoughElements(2)));
    assert_eq!(error.context.as_deref(), Some("field 1 of `Pair`"));
}

#[test]
fn named_struct() {
    let result: Config = parse_one(list([
        list([symbol("flag"), symbol("true")]),
        list([symbol("label"), symbol("x")]),
    ]))
    .unwrap();
    assert_eq!(
        result,
        Config {
            label: "x".into(),
            flag: true,
        }
    );
}

#[test]
fn named_struct_missing_fields_keep_default() {
    let result: Config = parse_one(list([list([symbol("label"), symbol("x")])])).unwrap();
    assert_eq!(
        result,
        Config {
            label: "x".into(),
            flag: false,
        }
    );
}

#[test]
fn named_struct_unknown_field() {
    let error = parse_one::<Config>(list([list([symbol("bogus"), symbol("x")])])).unwrap_err();
    assert!(matches!(error.kind, ErrorKind::UnknownField(name) if name.as_ref() == "bogus"));
}

#[test]
fn named_struct_field_error_context() {
    let error = parse_one::<Config>(list([list([symbol("flag"), symbol("maybe")])])).unwrap_err();
    assert!(matches!(error.kind, ErrorKind::StringParsing("bool")));
    assert_eq!(error.context.as_deref(), Some("field `flag` of `Config`"));
}

#[test]
fn unit_struct() {
    let result: Empty = parse_one(list([])).unwrap();
    assert_eq!(result, Empty);
}

#[test]
fn symbol_struct() {
    let result: Flag = parse_one(symbol("true")).unwrap();
    assert_eq!(result, Flag(true));
}

#[test]
fn symbol_struct_invalid() {
    let error = parse_one::<Flag>(symbol("maybe")).unwrap_err();
    assert!(matches!(error.kind, ErrorKind::StringParsing("bool")));
}

#[test]
fn generic_struct() {
    let result: Generic<String> = parse_one(list([symbol("a")])).unwrap();
    assert_eq!(result, Generic("a".into()));
}