teacat_lib 0.5.1

Tools for working with TeaCat files
Documentation
use pest::iterators::{Pair, Pairs};

use super::{Moo, TeaPair, grammar::Rule};

pub fn proc_str(pair: TeaPair) -> Moo {
	assert_eq!(pair.as_rule(), Rule::word);

	if should_proc_str(&pair.clone().into_inner()) {
		let mut str = String::with_capacity(pair.as_str().len());

		for pair in pair.into_inner() {
			match pair.as_rule() {
				Rule::esc => str.push(handle_esc_code(pair)),
				Rule::char => str.push_str(pair.as_str()),

				other => panic!("Unexpected rule in word: {other:?}"),
			}
		}

		str.into()
	} else {
		pair.as_str().into()
	}
}

fn should_proc_str(pairs: &Pairs<Rule>) -> bool {
	for pair in pairs.clone() {
		if pair.as_rule() == Rule::esc {
			return true;
		}
	}
	false
}

fn handle_esc_code(pair: Pair<Rule>) -> char {
	assert_eq!(pair.as_rule(), Rule::esc);
	let inner = pair
		.into_inner()
		.next()
		.expect("escape code should contain pairs!");

	match inner.as_rule() {
		Rule::esc_unicode => unicode_escape(inner.as_str()),
		Rule::esc_ascii => ascii_escape(inner.as_str()),
		Rule::esc_tc => inner
			.as_str()
			.chars()
			.next()
			.expect("escape code should not be empty!"),

		other => panic!("Unexpected rule in esc: {other:?}"),
	}
}

fn ascii_escape(str: &str) -> char {
	match str {
		"t" => '\t',
		"n" => '\n',

		other => panic!("Unexpected ascii esc: \\{other}"),
	}
}

fn unicode_escape(str: &str) -> char {
	let hex = &str[2..str.len() - 1];
	let num = u32::from_str_radix(hex, 16).expect("Unicode hex should be valid!");
	char::from_u32(num).unwrap_or_else(|| panic!("Invalid unicode hex: {hex}"))
}