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_len_and_size_hint() {
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()]);
}