token-parser 0.10.0

Utilities for parsing texts into data structures
Documentation
use std::{rc::Rc, sync::Arc};

use token_parser::{ErrorKind, Parsable, Parser, Span, Unit, Unparsable};

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: Parsable<()>>(unit: Unit) -> token_parser::Result<T> {
    let mut parser = Parser::new([unit]);
    parser.parse_next(&())
}

#[test]
fn parse_string() {
    let result: String = parse_one(symbol("hello")).unwrap();
    assert_eq!(result, "hello");
}

#[test]
fn parse_box_str() {
    let result: Box<str> = parse_one(symbol("world")).unwrap();
    assert_eq!(&*result, "world");
}

#[test]
fn parse_vec() {
    let result: Vec<String> = parse_one(list([symbol("a"), symbol("b"), symbol("c")])).unwrap();
    assert_eq!(result, vec!["a".to_string(), "b".into(), "c".into()]);
}

#[test]
fn parse_box() {
    let result: Box<String> = parse_one(symbol("x")).unwrap();
    assert_eq!(&**result, "x");
}

#[test]
fn parse_rc() {
    let result: Rc<String> = parse_one(symbol("x")).unwrap();
    assert_eq!(&**result, "x");
}

#[test]
fn parse_arc() {
    let result: Arc<String> = parse_one(symbol("x")).unwrap();
    assert_eq!(&**result, "x");
}

#[test]
fn parse_arc_nested_list() {
    let result: Arc<Vec<String>> = parse_one(list([symbol("a"), symbol("b")])).unwrap();
    assert_eq!(&**result, &["a".to_string(), "b".into()]);
}

#[test]
fn parse_bool() {
    let result: bool = parse_one(symbol("true")).unwrap();
    assert!(result);
}

#[test]
fn parse_symbol_not_allowed() {
    let error = parse_one::<Vec<String>>(symbol("a")).unwrap_err();
    assert!(matches!(error.kind, ErrorKind::SymbolNotAllowed));
}

#[test]
fn parse_list_not_allowed() {
    let error = parse_one::<String>(list([symbol("a")])).unwrap_err();
    assert!(matches!(error.kind, ErrorKind::ListNotAllowed));
}

#[test]
fn parse_not_enough() {
    let mut parser = Parser::new(Vec::<Unit>::new());
    let error = parser.parse_next::<(), String>(&()).unwrap_err();
    assert!(matches!(error.kind, ErrorKind::NotEnoughElements(_)));
}

#[derive(Debug)]
struct Single;

impl Parsable<()> for Single {
    fn parse_list(parser: &mut Parser, context: &()) -> token_parser::Result<Self> {
        parser.parse_next::<_, String>(context)?;
        Ok(Self)
    }
}

#[test]
fn parse_too_many_with_parse_rest() {
    let mut parser = Parser::new([symbol("a"), symbol("b")]);
    let error = parser.parse_rest::<(), Single>(&()).unwrap_err();
    assert!(matches!(error.kind, ErrorKind::TooManyElements(1)));
}

#[test]
fn parse_rest_keeps_original_error() {
    let mut parser = Parser::new([list([symbol("a")]), symbol("b")]);
    let error = parser.parse_rest::<(), Single>(&()).unwrap_err();
    assert!(matches!(error.kind, ErrorKind::ListNotAllowed));
}

#[test]
fn parser_iterator_exact_size() {
    let mut parser = Parser::new([list([]), list([]), list([])]);
    assert_eq!(parser.len(), 3);
    assert_eq!(parser.size_hint(), (3, Some(3)));
    parser.next().unwrap().unwrap();
    assert_eq!(parser.len(), 2);
    assert_eq!(parser.size_hint(), (2, Some(2)));
    assert_eq!(parser.count(), 2);
}

#[expect(clippy::unwrap_used, reason = "test helper")]
fn round_trip<T>(value: &T) -> T
where
    T: Parsable<()> + Unparsable<()>,
{
    let unit = value.to_unit(&());
    parse_one::<T>(unit).unwrap()
}

#[test]
fn unparse_string_round_trip() {
    assert_eq!(round_trip(&String::from("hello")), "hello");
}

#[test]
fn unparse_bool_round_trip() {
    assert!(round_trip(&true));
    assert!(!round_trip(&false));
}

#[test]
fn unparse_number_round_trip() {
    assert_eq!(round_trip(&42_u32), 42);
    assert_eq!(round_trip(&-7_i32), -7);
    assert_eq!(round_trip(&3.5_f64), 3.5);
}

#[test]
fn unparse_vec_round_trip() {
    let value: Vec<String> = vec!["a".into(), "b".into(), "c".into()];
    assert_eq!(round_trip(&value), value);
}

#[test]
fn unparse_nested_round_trip() {
    let value: Vec<Vec<u32>> = vec![vec![1, 2], vec![], vec![3, 4, 5]];
    assert_eq!(round_trip(&value), value);
}

#[test]
fn unparse_box_delegates() {
    let value: Box<String> = Box::new("wrapped".into());
    let unit = value.to_unit(&());
    let restored: String = parse_one(unit).unwrap();
    assert_eq!(restored, "wrapped");
}

#[test]
fn substitute_replaces_symbols() {
    let mut unit = list([symbol("$x"), symbol("y")]);
    unit.substitute("$x", "z");
    let result: Vec<String> = parse_one(unit).unwrap();
    assert_eq!(result, vec!["z".to_string(), "y".into()]);
}