use crate::{utils::CoreError, Result};
use alloc::{format, string::ToString};
use core::str::Chars;
#[cfg(not(feature = "std"))]
extern crate alloc;
#[cfg(feature = "simd")]
use super::simd;
use super::{state::TokenContext, tokens::TokenType};
#[derive(Debug, Clone)]
pub struct CharNavigator<'a> {
source: &'a str,
position: usize,
line: usize,
column: usize,
chars: Chars<'a>,
peek_char: Option<char>,
last_char: Option<char>,
}
impl<'a> CharNavigator<'a> {
#[must_use]
pub fn new(source: &'a str, position: usize, line: usize, column: usize) -> Self {
Self {
source,
position,
line,
column,
chars: source[position..].chars(),
peek_char: None,
last_char: None,
}
}
#[must_use]
pub const fn position(&self) -> usize {
self.position
}
#[must_use]
pub const fn line(&self) -> usize {
self.line
}
#[must_use]
pub const fn column(&self) -> usize {
self.column
}
pub fn peek_char(&mut self) -> Result<char> {
if let Some(ch) = self.peek_char {
Ok(ch)
} else if self.position < self.source.len() {
let ch = self.source[self.position..].chars().next().ok_or_else(|| {
CoreError::parse(format!("Invalid UTF-8 at position {}", self.position))
})?;
self.peek_char = Some(ch);
Ok(ch)
} else {
Err(CoreError::parse("Unexpected end of input".to_string()))
}
}
pub fn peek_next(&self) -> Result<char> {
let mut chars = self.source[self.position..].chars();
chars.next(); chars
.next()
.ok_or_else(|| CoreError::parse("Unexpected end of input".to_string()))
}
pub fn advance_char(&mut self) -> Result<char> {
let ch = self.peek_char()?;
self.peek_char = None;
let _ = self.chars.next();
self.position += ch.len_utf8();
match ch {
'\r' => {
self.line += 1;
self.column = 1;
}
'\n' => {
if self.last_char != Some('\r') {
self.line += 1;
}
self.column = 1;
}
_ => {
self.column += 1;
}
}
self.last_char = Some(ch);
Ok(ch)
}
pub fn skip_whitespace(&mut self) {
while self.position < self.source.len() {
if let Ok(ch) = self.peek_char() {
if ch.is_whitespace() && ch != '\n' && ch != '\r' {
let _ = self.advance_char();
} else {
break;
}
} else {
break;
}
}
}
#[must_use]
pub const fn is_at_end(&self) -> bool {
self.position >= self.source.len()
}
}
#[derive(Debug, Clone)]
pub struct TokenScanner<'a> {
navigator: CharNavigator<'a>,
source: &'a str,
}
impl<'a> TokenScanner<'a> {
#[must_use]
pub fn new(source: &'a str, position: usize, line: usize, column: usize) -> Self {
Self {
navigator: CharNavigator::new(source, position, line, column),
source,
}
}
pub fn navigator_mut(&mut self) -> &mut CharNavigator<'a> {
&mut self.navigator
}
#[must_use]
pub const fn navigator(&self) -> &CharNavigator<'a> {
&self.navigator
}
pub fn scan_section_header(&mut self) -> Result<TokenType> {
self.navigator.advance_char()?;
while !self.navigator.is_at_end() {
let ch = self.navigator.peek_char()?;
if ch == ']' {
break;
}
self.navigator.advance_char()?;
}
Ok(TokenType::SectionHeader)
}
pub fn scan_style_override(&mut self) -> Result<TokenType> {
self.navigator.advance_char()?;
let mut brace_depth = 1;
while !self.navigator.is_at_end() && brace_depth > 0 {
let ch = self.navigator.peek_char()?;
match ch {
'{' => brace_depth += 1,
'}' => brace_depth -= 1,
_ => {}
}
if brace_depth > 0 {
self.navigator.advance_char()?;
}
}
Ok(TokenType::OverrideBlock)
}
pub fn scan_comment(&mut self) -> Result<TokenType> {
while !self.navigator.is_at_end() {
let ch = self.navigator.peek_char()?;
if ch == '\n' || ch == '\r' {
break;
}
self.navigator.advance_char()?;
}
Ok(TokenType::Comment)
}
pub fn scan_text(&mut self, context: TokenContext) -> Result<TokenType> {
let start = self.navigator.position();
#[cfg(feature = "simd")]
{
let use_simd = !matches!(context, TokenContext::FieldValue);
if use_simd {
if let Some(delimiter_pos) = self.scan_delimiters_simd(start) {
self.navigator.position = delimiter_pos;
} else {
self.navigator.position = self.source.len();
}
self.navigator.chars = self.source[self.navigator.position..].chars();
self.navigator.peek_char = None;
}
}
#[cfg(not(feature = "simd"))]
let use_scalar = true;
#[cfg(feature = "simd")]
let use_scalar = matches!(context, TokenContext::FieldValue);
if use_scalar {
while !self.navigator.is_at_end() {
let ch = self.navigator.peek_char()?;
let is_delimiter = match context {
TokenContext::FieldValue => {
matches!(ch, ',' | '{' | '}' | '[' | ']' | '\n' | '\r')
}
_ => {
matches!(ch, ',' | ':' | '{' | '}' | '[' | ']' | '\n' | '\r')
|| (ch == ';' && context == TokenContext::Document)
}
};
if is_delimiter {
break;
}
self.navigator.advance_char()?;
}
}
let span = &self.source[start..self.navigator.position()];
if context == TokenContext::SectionHeader {
Ok(TokenType::SectionName)
} else if Self::is_hex_value(span) {
Ok(TokenType::HexValue)
} else if !span.is_empty()
&& span
.chars()
.all(|c| c.is_ascii_digit() || c == '.' || c == '-')
{
Ok(TokenType::Number)
} else if !span.is_empty() && span.chars().all(char::is_whitespace) {
Ok(TokenType::Whitespace)
} else {
Ok(TokenType::Text)
}
}
fn is_hex_value(span: &str) -> bool {
if let Some(after_prefix) = span.strip_prefix("&H") {
let hex_part = after_prefix
.strip_suffix('&')
.map_or(after_prefix, |stripped| stripped);
if !hex_part.is_empty()
&& hex_part.len() % 2 == 0
&& hex_part.len() <= 8
&& hex_part.chars().all(|c| c.is_ascii_hexdigit())
{
#[cfg(feature = "simd")]
{
return TokenScanner::parse_hex_simd(hex_part).is_some();
}
#[cfg(not(feature = "simd"))]
{
return true;
}
}
}
false
}
#[cfg(feature = "simd")]
fn scan_delimiters_simd(&self, start: usize) -> Option<usize> {
simd::scan_delimiters(&self.source[start..]).map(|offset| start + offset)
}
#[cfg(feature = "simd")]
fn parse_hex_simd(hex_str: &str) -> Option<u32> {
simd::parse_hex_u32(hex_str)
}
pub fn scan_field_value(&mut self) -> Result<TokenType> {
let start = self.navigator.position();
while !self.navigator.is_at_end() {
let ch = self.navigator.peek_char()?;
if ch == ',' || ch == '\n' || ch == '\r' || ch == '{' || ch == '[' {
break;
}
self.navigator.advance_char()?;
}
let span = &self.source[start..self.navigator.position()];
if !span.is_empty()
&& span
.chars()
.all(|c| c.is_ascii_digit() || c == '.' || c == '-' || c == ':')
{
Ok(TokenType::Number)
} else if !span.is_empty() && span.chars().all(char::is_whitespace) {
Ok(TokenType::Whitespace)
} else {
Ok(TokenType::Text)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::vec;
#[test]
fn char_navigator_new() {
let source = "test content";
let nav = CharNavigator::new(source, 5, 2, 3);
assert_eq!(nav.position(), 5);
assert_eq!(nav.line(), 2);
assert_eq!(nav.column(), 3);
}
#[test]
fn char_navigator_peek_char() {
let source = "hello";
let mut nav = CharNavigator::new(source, 0, 1, 1);
assert_eq!(nav.peek_char().unwrap(), 'h');
assert_eq!(nav.peek_char().unwrap(), 'h'); assert_eq!(nav.position(), 0);
}
#[test]
fn char_navigator_peek_next() {
let source = "hello";
let nav = CharNavigator::new(source, 0, 1, 1);
assert_eq!(nav.peek_next().unwrap(), 'e');
}
#[test]
fn char_navigator_advance_char() {
let source = "hello";
let mut nav = CharNavigator::new(source, 0, 1, 1);
assert_eq!(nav.advance_char().unwrap(), 'h');
assert_eq!(nav.position(), 1);
assert_eq!(nav.column(), 2);
}
#[test]
fn char_navigator_advance_newline() {
let source = "line1\nline2";
let mut nav = CharNavigator::new(source, 0, 1, 1);
for _ in 0..5 {
nav.advance_char().unwrap();
}
assert_eq!(nav.advance_char().unwrap(), '\n');
assert_eq!(nav.line(), 2);
assert_eq!(nav.column(), 1);
}
#[test]
fn char_navigator_advance_carriage_return() {
let source = "line1\rline2";
let mut nav = CharNavigator::new(source, 0, 1, 1);
for _ in 0..5 {
nav.advance_char().unwrap();
}
assert_eq!(nav.advance_char().unwrap(), '\r');
assert_eq!(nav.line(), 2);
assert_eq!(nav.column(), 1);
}
#[test]
fn char_navigator_advance_crlf() {
let source = "line1\r\nline2";
let mut nav = CharNavigator::new(source, 0, 1, 1);
for _ in 0..5 {
nav.advance_char().unwrap();
}
assert_eq!(nav.advance_char().unwrap(), '\r');
assert_eq!(nav.line(), 2);
assert_eq!(nav.advance_char().unwrap(), '\n');
assert_eq!(nav.line(), 2); assert_eq!(nav.column(), 1);
}
#[test]
fn char_navigator_skip_whitespace() {
let source = " \t hello";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.skip_whitespace();
assert_eq!(nav.peek_char().unwrap(), 'h');
}
#[test]
fn char_navigator_skip_whitespace_preserves_newlines() {
let source = " \n hello";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.skip_whitespace();
assert_eq!(nav.peek_char().unwrap(), '\n');
}
#[test]
fn char_navigator_is_at_end() {
let source = "hi";
let nav = CharNavigator::new(source, 2, 1, 1);
assert!(nav.is_at_end());
let nav2 = CharNavigator::new(source, 0, 1, 1);
assert!(!nav2.is_at_end());
}
#[test]
fn char_navigator_peek_char_at_end() {
let source = "hi";
let mut nav = CharNavigator::new(source, 2, 1, 1);
assert!(nav.peek_char().is_err());
}
#[test]
fn char_navigator_peek_next_at_end() {
let source = "h";
let nav = CharNavigator::new(source, 0, 1, 1);
assert!(nav.peek_next().is_err());
}
#[test]
fn token_scanner_new() {
let source = "test content";
let scanner = TokenScanner::new(source, 5, 2, 3);
assert_eq!(scanner.navigator().position(), 5);
assert_eq!(scanner.navigator().line(), 2);
assert_eq!(scanner.navigator().column(), 3);
}
#[test]
fn token_scanner_scan_section_header() {
let source = "[Script Info]";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_section_header().unwrap();
assert_eq!(token_type, TokenType::SectionHeader);
}
#[test]
fn token_scanner_scan_style_override() {
let source = "{\\b1\\i1}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_style_override().unwrap();
assert_eq!(token_type, TokenType::OverrideBlock);
}
#[test]
fn token_scanner_scan_style_override_nested() {
let source = "{\\b1{\\i1}\\b0}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_style_override().unwrap();
assert_eq!(token_type, TokenType::OverrideBlock);
}
#[test]
fn token_scanner_scan_comment() {
let source = "; This is a comment\nNext line";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_comment().unwrap();
assert_eq!(token_type, TokenType::Comment);
}
#[test]
fn token_scanner_scan_text_basic() {
let source = "Hello World,";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type, TokenType::Text);
}
#[test]
fn token_scanner_scan_text_number() {
let source = "123.45,";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type, TokenType::Number);
}
#[test]
fn token_scanner_scan_text_hex_value() {
let source = "&HABCDEF&,";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type, TokenType::HexValue);
}
#[test]
fn token_scanner_scan_text_section_name() {
let source = "Script Info]";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::SectionHeader).unwrap();
assert_eq!(token_type, TokenType::SectionName);
}
#[test]
fn token_scanner_scan_text_field_value_context() {
let source = "0:01:23.45,";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(token_type, TokenType::Text);
}
#[test]
fn token_scanner_scan_field_value() {
let source = "Some field value,";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_field_value().unwrap();
assert_eq!(token_type, TokenType::Text);
}
#[test]
fn token_scanner_scan_field_value_number() {
let source = "0:01:23.45,";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_field_value().unwrap();
assert_eq!(token_type, TokenType::Number);
}
#[test]
fn token_scanner_is_hex_value_simple() {
assert!(!TokenScanner::is_hex_value("ABCD"));
assert!(!TokenScanner::is_hex_value("1234"));
assert!(TokenScanner::is_hex_value("&HABCD&"));
assert!(TokenScanner::is_hex_value("&H1234&"));
assert!(!TokenScanner::is_hex_value("&HABCDE&")); assert!(!TokenScanner::is_hex_value("&HGHIJ&")); assert!(!TokenScanner::is_hex_value("")); }
#[test]
fn token_scanner_is_hex_value_with_prefix() {
assert!(TokenScanner::is_hex_value("&HFF00FF&"));
assert!(TokenScanner::is_hex_value("&HFF00FF"));
assert!(!TokenScanner::is_hex_value("&H&")); assert!(!TokenScanner::is_hex_value("&HGHIJ&")); }
#[test]
fn token_scanner_is_hex_value_max_length() {
assert!(!TokenScanner::is_hex_value("ABCDEF")); assert!(!TokenScanner::is_hex_value("00FF00FF")); assert!(!TokenScanner::is_hex_value("1234567890")); assert!(!TokenScanner::is_hex_value(&"A".repeat(100)));
assert!(TokenScanner::is_hex_value("&H00FF00FF&")); assert!(TokenScanner::is_hex_value("&HABCD&")); assert!(!TokenScanner::is_hex_value("&H1234567890&"));
assert!(!TokenScanner::is_hex_value("00")); assert!(!TokenScanner::is_hex_value("123abc")); }
#[test]
fn token_scanner_hex_value_trailing_ampersand_variants() {
assert!(TokenScanner::is_hex_value("&H00FFFFFF&"));
assert!(TokenScanner::is_hex_value("&HFF0000&"));
assert!(TokenScanner::is_hex_value("&H80FF00FF&"));
assert!(TokenScanner::is_hex_value("&H00FFFFFF"));
assert!(TokenScanner::is_hex_value("&HFF0000"));
assert!(TokenScanner::is_hex_value("&H80FF00FF"));
assert!(TokenScanner::is_hex_value("&H00&"));
assert!(TokenScanner::is_hex_value("&H00"));
assert!(!TokenScanner::is_hex_value("&H&")); assert!(!TokenScanner::is_hex_value("&H")); }
#[test]
fn token_scanner_scan_text_hex_value_ampersand_variants() {
let source1 = "&H00FFFFFF&";
let mut scanner1 = TokenScanner::new(source1, 0, 1, 1);
let token_type1 = scanner1.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type1, TokenType::HexValue);
assert_eq!(scanner1.navigator().position(), source1.len());
let source2 = "&H00FFFFFF";
let mut scanner2 = TokenScanner::new(source2, 0, 1, 1);
let token_type2 = scanner2.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type2, TokenType::HexValue);
assert_eq!(scanner2.navigator().position(), source2.len());
let source3 = "&HFF00&";
let mut scanner3 = TokenScanner::new(source3, 0, 1, 1);
let token_type3 = scanner3.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type3, TokenType::HexValue);
let source4 = "&HFF00";
let mut scanner4 = TokenScanner::new(source4, 0, 1, 1);
let token_type4 = scanner4.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type4, TokenType::HexValue);
}
#[test]
fn token_scanner_delimiter_context_field_value() {
let source = "Title: My Script";
let mut scanner = TokenScanner::new(source, 7, 1, 8); let token_type = scanner.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(token_type, TokenType::Text);
}
#[test]
fn token_scanner_delimiter_context_document() {
let source = "Field:Value";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type, TokenType::Text);
assert_eq!(scanner.navigator().position(), 5);
}
#[test]
fn token_scanner_various_delimiters() {
let test_cases = vec![
(",", TokenContext::Document),
("{", TokenContext::Document),
("}", TokenContext::Document),
("[", TokenContext::Document),
("]", TokenContext::Document),
("\n", TokenContext::Document),
("\r", TokenContext::Document),
];
for (delimiter, context) in test_cases {
let source = format!("text{delimiter}more");
let mut scanner = TokenScanner::new(&source, 0, 1, 1);
let _token_type = scanner.scan_text(context).unwrap();
assert_eq!(scanner.navigator().position(), 4); }
}
#[test]
fn token_scanner_navigator_mut() {
let source = "test";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
{
let nav_mut = scanner.navigator_mut();
nav_mut.advance_char().unwrap();
}
assert_eq!(scanner.navigator().position(), 1);
}
#[test]
fn char_navigator_utf8_handling() {
let source = "café";
let mut nav = CharNavigator::new(source, 0, 1, 1);
assert_eq!(nav.advance_char().unwrap(), 'c');
assert_eq!(nav.advance_char().unwrap(), 'a');
assert_eq!(nav.advance_char().unwrap(), 'f');
assert_eq!(nav.advance_char().unwrap(), 'é');
assert_eq!(nav.position(), 5); }
#[test]
fn token_scanner_empty_section_header() {
let source = "[]";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_section_header().unwrap();
assert_eq!(token_type, TokenType::SectionHeader);
}
#[test]
fn token_scanner_unclosed_section_header() {
let source = "[Script Info";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_section_header().unwrap();
assert_eq!(token_type, TokenType::SectionHeader);
}
#[test]
fn token_scanner_empty_style_override() {
let source = "{}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_style_override().unwrap();
assert_eq!(token_type, TokenType::OverrideBlock);
}
#[test]
fn token_scanner_unclosed_style_override() {
let source = "{\\b1\\i1";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_style_override().unwrap();
assert_eq!(token_type, TokenType::OverrideBlock);
}
#[test]
fn token_scanner_comment_at_end() {
let source = "; Comment at end";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_comment().unwrap();
assert_eq!(token_type, TokenType::Comment);
}
#[test]
fn char_navigator_advance_char_error_handling() {
let mut nav = CharNavigator::new("", 0, 1, 1);
assert!(nav.advance_char().is_err());
assert!(nav.peek_char().is_err());
assert!(nav.peek_next().is_err());
}
#[test]
fn char_navigator_peek_operations_edge_cases() {
let source = "a";
let mut nav = CharNavigator::new(source, 0, 1, 1);
assert_eq!(nav.peek_char().unwrap(), 'a');
assert_eq!(nav.peek_char().unwrap(), 'a');
assert!(nav.peek_next().is_err());
nav.advance_char().unwrap();
assert!(nav.peek_char().is_err()); assert!(nav.peek_next().is_err()); }
#[test]
fn char_navigator_line_column_tracking_complex() {
let source = "line1\r\nline2\rline3\nline4";
let mut nav = CharNavigator::new(source, 0, 1, 1);
for _ in "line1".chars() {
nav.advance_char().unwrap();
}
assert_eq!(nav.line(), 1);
assert_eq!(nav.column(), 6);
nav.advance_char().unwrap(); assert_eq!(nav.line(), 2);
assert_eq!(nav.column(), 1);
nav.advance_char().unwrap(); assert_eq!(nav.line(), 2);
assert_eq!(nav.column(), 1);
for _ in "line2".chars() {
nav.advance_char().unwrap();
}
nav.advance_char().unwrap(); assert_eq!(nav.line(), 3);
assert_eq!(nav.column(), 1);
for _ in "line3".chars() {
nav.advance_char().unwrap();
}
nav.advance_char().unwrap(); assert_eq!(nav.line(), 4);
assert_eq!(nav.column(), 1);
}
#[test]
fn char_navigator_skip_whitespace_variations() {
let source = " \t\u{00A0}\u{2000} text"; let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.skip_whitespace();
assert_eq!(nav.peek_char().unwrap(), 't');
let source2 = " \n text";
let mut nav2 = CharNavigator::new(source2, 0, 1, 1);
nav2.skip_whitespace();
assert_eq!(nav2.peek_char().unwrap(), '\n');
}
#[test]
fn token_scanner_scan_text_field_value_context_edge_cases() {
let source = "0:00:30.50";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(token_type, TokenType::Text);
assert_eq!(scanner.navigator().position(), source.len());
let source2 = "text,next";
let mut scanner2 = TokenScanner::new(source2, 0, 1, 1);
let token_type2 = scanner2.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(token_type2, TokenType::Text);
assert_eq!(scanner2.navigator().position(), 4); }
#[test]
fn token_scanner_scan_text_number_detection() {
let source1 = "123.45";
let mut scanner1 = TokenScanner::new(source1, 0, 1, 1);
let token_type1 = scanner1.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type1, TokenType::Number);
let source2 = "-123.45";
let mut scanner2 = TokenScanner::new(source2, 0, 1, 1);
let token_type2 = scanner2.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type2, TokenType::Number);
let source3 = "123";
let mut scanner3 = TokenScanner::new(source3, 0, 1, 1);
let token_type3 = scanner3.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type3, TokenType::Number);
let source4 = "123abc";
let mut scanner4 = TokenScanner::new(source4, 0, 1, 1);
let token_type4 = scanner4.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type4, TokenType::Text);
}
#[test]
fn token_scanner_scan_field_value_comprehensive() {
let source1 = "0:01:30.50,next";
let mut scanner1 = TokenScanner::new(source1, 0, 1, 1);
let token_type1 = scanner1.scan_field_value().unwrap();
assert_eq!(token_type1, TokenType::Number);
assert_eq!(scanner1.navigator().position(), 10);
let source2 = "Some text,next";
let mut scanner2 = TokenScanner::new(source2, 0, 1, 1);
let token_type2 = scanner2.scan_field_value().unwrap();
assert_eq!(token_type2, TokenType::Text);
assert_eq!(scanner2.navigator().position(), 9);
let source3 = "text\nmore";
let mut scanner3 = TokenScanner::new(source3, 0, 1, 1);
let token_type3 = scanner3.scan_field_value().unwrap();
assert_eq!(token_type3, TokenType::Text);
assert_eq!(scanner3.navigator().position(), 4);
let source4 = "text{override}";
let mut scanner4 = TokenScanner::new(source4, 0, 1, 1);
let token_type4 = scanner4.scan_field_value().unwrap();
assert_eq!(token_type4, TokenType::Text);
assert_eq!(scanner4.navigator().position(), 4);
let source5 = "text[section]";
let mut scanner5 = TokenScanner::new(source5, 0, 1, 1);
let token_type5 = scanner5.scan_field_value().unwrap();
assert_eq!(token_type5, TokenType::Text);
assert_eq!(scanner5.navigator().position(), 4); }
#[test]
fn token_scanner_section_header_variations() {
let source1 = "[ Script Info ]";
let mut scanner1 = TokenScanner::new(source1, 1, 1, 2); let token_type1 = scanner1.scan_section_header().unwrap();
assert_eq!(token_type1, TokenType::SectionHeader);
let source2 = "[V4+ Styles]";
let mut scanner2 = TokenScanner::new(source2, 1, 1, 2); let token_type2 = scanner2.scan_section_header().unwrap();
assert_eq!(token_type2, TokenType::SectionHeader);
let source3 = "[Script Info\nNext line";
let mut scanner3 = TokenScanner::new(source3, 1, 1, 2); let token_type3 = scanner3.scan_section_header().unwrap();
assert_eq!(token_type3, TokenType::SectionHeader);
}
#[test]
fn token_scanner_style_override_complex() {
let source1 = "{\\b1{nested}\\i1}";
let mut scanner1 = TokenScanner::new(source1, 1, 1, 2); let token_type1 = scanner1.scan_style_override().unwrap();
assert_eq!(token_type1, TokenType::OverrideBlock);
let source2 = "{ }";
let mut scanner2 = TokenScanner::new(source2, 1, 1, 2); let token_type2 = scanner2.scan_style_override().unwrap();
assert_eq!(token_type2, TokenType::OverrideBlock);
let source3 = "{\\b1\\i1\n";
let mut scanner3 = TokenScanner::new(source3, 1, 1, 2); let token_type3 = scanner3.scan_style_override().unwrap();
assert_eq!(token_type3, TokenType::OverrideBlock);
}
#[test]
fn token_scanner_comment_variations() {
let source1 = "!: This is a comment";
let mut scanner1 = TokenScanner::new(source1, 0, 1, 1);
let token_type1 = scanner1.scan_comment().unwrap();
assert_eq!(token_type1, TokenType::Comment);
let source2 = "; Comment";
let mut scanner2 = TokenScanner::new(source2, 0, 1, 1);
let token_type2 = scanner2.scan_comment().unwrap();
assert_eq!(token_type2, TokenType::Comment);
assert_eq!(scanner2.navigator().position(), source2.len());
let source3 = ";\n";
let mut scanner3 = TokenScanner::new(source3, 0, 1, 1);
let token_type3 = scanner3.scan_comment().unwrap();
assert_eq!(token_type3, TokenType::Comment);
}
#[test]
fn token_scanner_unicode_handling() {
let source1 = "䏿–‡æµ‹è¯•";
let mut scanner1 = TokenScanner::new(source1, 0, 1, 1);
let token_type1 = scanner1.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type1, TokenType::Text);
assert_eq!(scanner1.navigator().position(), source1.len());
let source2 = "[ã‚¹ã‚¯ãƒªãƒ—ãƒˆæƒ…å ±]";
let mut scanner2 = TokenScanner::new(source2, 1, 1, 2); let token_type2 = scanner2.scan_section_header().unwrap();
assert_eq!(token_type2, TokenType::SectionHeader);
let source3 = "ðŸŽðŸŽ¬ðŸŽª";
let mut scanner3 = TokenScanner::new(source3, 0, 1, 1);
let token_type3 = scanner3.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type3, TokenType::Text);
assert_eq!(scanner3.navigator().position(), source3.len());
}
#[test]
fn token_scanner_empty_content_handling() {
let source1 = ",next";
let mut scanner1 = TokenScanner::new(source1, 0, 1, 1);
let token_type1 = scanner1.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type1, TokenType::Text);
assert_eq!(scanner1.navigator().position(), 0);
let source2 = "text";
let mut scanner2 = TokenScanner::new(source2, 4, 1, 5); let token_type2 = scanner2.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type2, TokenType::Text);
}
#[test]
fn char_navigator_boundary_conditions() {
let long_line = "a".repeat(10000);
let mut nav = CharNavigator::new(&long_line, 0, 1, 1);
for i in 1..=10000 {
nav.advance_char().unwrap();
assert_eq!(nav.column(), i + 1);
}
assert!(nav.is_at_end());
let many_lines = "a\n".repeat(1000);
let mut nav2 = CharNavigator::new(&many_lines, 0, 1, 1);
for i in 1..=1000 {
nav2.advance_char().unwrap(); nav2.advance_char().unwrap(); assert_eq!(nav2.line(), i + 1);
assert_eq!(nav2.column(), 1);
}
}
#[test]
fn token_scanner_simd_fallback_coverage() {
let source = "field:value,next";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(token_type, TokenType::Text);
assert_eq!(scanner.navigator().position(), 11);
}
#[test]
fn char_navigator_error_recovery() {
let source = "a";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.advance_char().unwrap();
assert!(nav.is_at_end());
assert!(nav.advance_char().is_err());
assert!(nav.peek_char().is_err());
}
#[test]
fn char_navigator_peek_char_caching() {
let source = "hello";
let mut nav = CharNavigator::new(source, 0, 1, 1);
assert_eq!(nav.peek_char().unwrap(), 'h');
assert_eq!(nav.peek_char().unwrap(), 'h');
assert_eq!(nav.peek_char().unwrap(), 'h');
nav.advance_char().unwrap();
assert_eq!(nav.peek_char().unwrap(), 'e');
}
#[test]
fn char_navigator_last_char_tracking() {
let source = "a\r\nb";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.advance_char().unwrap(); nav.advance_char().unwrap(); assert_eq!(nav.line(), 2);
nav.advance_char().unwrap(); assert_eq!(nav.line(), 2);
assert_eq!(nav.column(), 1);
}
#[test]
fn token_scanner_hex_value_edge_cases() {
assert!(!TokenScanner::is_hex_value("FF"));
assert!(!TokenScanner::is_hex_value("00"));
assert!(!TokenScanner::is_hex_value("ABCDEF"));
assert!(!TokenScanner::is_hex_value("123456"));
assert!(TokenScanner::is_hex_value("&HFF&"));
assert!(TokenScanner::is_hex_value("&HFF"));
assert!(TokenScanner::is_hex_value("&H00FF00FF&"));
assert!(TokenScanner::is_hex_value("&H00FF00FF"));
assert!(!TokenScanner::is_hex_value("F")); assert!(!TokenScanner::is_hex_value("GG")); assert!(!TokenScanner::is_hex_value("&H&")); assert!(!TokenScanner::is_hex_value("&HG&")); assert!(!TokenScanner::is_hex_value("")); }
#[test]
fn scan_text_classification_verification() {
let source1 = "0:00:30.50";
let mut scanner1 = TokenScanner::new(source1, 0, 1, 1);
let token_type1 = scanner1.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(token_type1, TokenType::Text);
let source2 = "123abc";
let mut scanner2 = TokenScanner::new(source2, 0, 1, 1);
let token_type2 = scanner2.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type2, TokenType::Text);
let source3 = "&H00FF00&";
let mut scanner3 = TokenScanner::new(source3, 0, 1, 1);
let token_type3 = scanner3.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type3, TokenType::HexValue);
}
#[test]
fn token_scanner_delimiter_combinations() {
let source = "text:{}[],more";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type, TokenType::Text);
assert_eq!(scanner.navigator().position(), 4); }
#[test]
fn token_scanner_field_value_delimiter_handling() {
let source = "0:00:30.50";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(token_type, TokenType::Text); assert_eq!(scanner.navigator().position(), source.len()); }
#[test]
fn token_scanner_semicolon_context_sensitivity() {
let source = "text;comment";
let mut scanner1 = TokenScanner::new(source, 0, 1, 1);
let token_type1 = scanner1.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type1, TokenType::Text);
assert!(scanner1.navigator().position() >= 4);
let mut scanner2 = TokenScanner::new(source, 0, 1, 1);
let token_type2 = scanner2.scan_text(TokenContext::FieldValue).unwrap();
assert_eq!(token_type2, TokenType::Text);
assert_eq!(scanner2.navigator().position(), source.len());
}
#[test]
fn token_scanner_number_detection_edge_cases() {
let test_cases = vec![
("123", true),
("123.45", true),
("-123", true),
("-123.45", true),
("123.", true),
(".45", true),
("-.45", true),
("123abc", false), ("", false), (".", true), ("-", true), ("--123", true), ("12.34.56", true), ];
for (input, expected_is_number) in test_cases {
let source = format!("{input},");
let mut scanner = TokenScanner::new(&source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::Document).unwrap();
if expected_is_number && !input.is_empty() {
assert_eq!(token_type, TokenType::Number, "Failed for input: {input}");
} else {
assert_ne!(token_type, TokenType::Number, "Failed for input: {input}");
}
}
}
#[test]
fn token_scanner_style_override_brace_depth() {
let source = "{{{{}}}}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_style_override().unwrap();
assert_eq!(token_type, TokenType::OverrideBlock);
assert_eq!(scanner.navigator().position(), 7); }
#[test]
fn token_scanner_style_override_unbalanced() {
let source = "{{{}}"; let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_style_override().unwrap();
assert_eq!(token_type, TokenType::OverrideBlock);
assert_eq!(scanner.navigator().position(), source.len());
}
#[test]
fn char_navigator_whitespace_at_end() {
let source = "text ";
let mut nav = CharNavigator::new(source, 4, 1, 5); nav.skip_whitespace();
assert!(nav.is_at_end());
}
#[test]
fn char_navigator_mixed_newlines() {
let source = "\r\n\n\r";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.advance_char().unwrap();
assert_eq!(nav.line(), 2);
nav.advance_char().unwrap();
assert_eq!(nav.line(), 2);
nav.advance_char().unwrap();
assert_eq!(nav.line(), 3);
nav.advance_char().unwrap();
assert_eq!(nav.line(), 4);
}
#[test]
fn token_scanner_empty_span_handling() {
let source = ",";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(TokenContext::Document).unwrap();
assert_eq!(token_type, TokenType::Text);
assert_eq!(scanner.navigator().position(), 0); }
#[test]
fn token_scanner_field_value_time_format() {
let source = "1:23:45.67";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_field_value().unwrap();
assert_eq!(token_type, TokenType::Number);
assert_eq!(scanner.navigator().position(), source.len());
}
#[test]
fn char_navigator_position_consistency() {
let source = "caféðŸŽ";
let mut nav = CharNavigator::new(source, 0, 1, 1);
let start_pos = nav.position();
nav.advance_char().unwrap(); assert_eq!(nav.position(), start_pos + 1);
nav.advance_char().unwrap(); assert_eq!(nav.position(), start_pos + 2);
nav.advance_char().unwrap(); assert_eq!(nav.position(), start_pos + 3);
nav.advance_char().unwrap(); assert_eq!(nav.position(), start_pos + 5);
nav.advance_char().unwrap(); assert_eq!(nav.position(), start_pos + 9);
}
#[test]
fn token_scanner_all_contexts_coverage() {
let contexts = vec![
TokenContext::Document,
TokenContext::SectionHeader,
TokenContext::FieldValue,
TokenContext::StyleOverride,
];
for context in contexts {
let source = "test:value,more";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let token_type = scanner.scan_text(context).unwrap();
match context {
TokenContext::SectionHeader => assert_eq!(token_type, TokenType::SectionName),
_ => assert!(matches!(
token_type,
TokenType::Text | TokenType::Number | TokenType::HexValue
)),
}
}
}
#[test]
fn char_navigator_column_reset_on_newlines() {
let source = "long line text\nshort\n";
let mut nav = CharNavigator::new(source, 0, 1, 1);
for _ in 0..14 {
nav.advance_char().unwrap();
}
assert_eq!(nav.column(), 15);
nav.advance_char().unwrap(); assert_eq!(nav.line(), 2);
assert_eq!(nav.column(), 1);
for _ in 0..5 {
nav.advance_char().unwrap();
}
assert_eq!(nav.column(), 6);
nav.advance_char().unwrap(); assert_eq!(nav.line(), 3);
assert_eq!(nav.column(), 1);
}
#[test]
fn char_navigator_utf8_error_handling() {
let source = "valid\x7F\x7E";
let mut nav = CharNavigator::new(source, 0, 1, 1);
assert!(nav.advance_char().is_ok());
assert!(nav.advance_char().is_ok());
assert!(nav.advance_char().is_ok());
assert!(nav.advance_char().is_ok());
assert!(nav.advance_char().is_ok());
let result = nav.advance_char();
match result {
Ok(_) | Err(_) => {
assert!(nav.position() > 0);
}
}
}
#[test]
fn char_navigator_peek_char_caching_coverage() {
let source = "abc";
let mut nav = CharNavigator::new(source, 0, 1, 1);
let first_peek = nav.peek_char();
assert_eq!(first_peek, Ok('a'));
let second_peek = nav.peek_char();
assert_eq!(second_peek, Ok('a'));
assert!(nav.advance_char().is_ok());
let third_peek = nav.peek_char();
assert_eq!(third_peek, Ok('b'));
}
#[test]
fn char_navigator_last_char_tracking_coverage() {
let source = "xy\nz";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.advance_char().unwrap(); assert_eq!(nav.last_char, Some('x'));
nav.advance_char().unwrap(); assert_eq!(nav.last_char, Some('y'));
nav.advance_char().unwrap(); assert_eq!(nav.last_char, Some('\n'));
assert_eq!(nav.line(), 2);
nav.advance_char().unwrap(); assert_eq!(nav.last_char, Some('z'));
}
#[test]
fn token_scanner_hex_value_comprehensive_coverage() {
assert!(TokenScanner::is_hex_value("&H1234&"));
assert!(TokenScanner::is_hex_value("&HFFFF&"));
assert!(TokenScanner::is_hex_value("&H00&"));
assert!(TokenScanner::is_hex_value("&H1234"));
assert!(TokenScanner::is_hex_value("&HABCD"));
assert!(!TokenScanner::is_hex_value("&H&"));
assert!(!TokenScanner::is_hex_value("&H"));
assert!(!TokenScanner::is_hex_value("&H123&"));
assert!(!TokenScanner::is_hex_value("&H0&"));
assert!(!TokenScanner::is_hex_value("&H123456789ABCDEF&"));
assert!(!TokenScanner::is_hex_value("&HZ123&"));
assert!(!TokenScanner::is_hex_value("&H12G4&"));
}
#[test]
fn token_scanner_delimiter_context_comprehensive() {
let source = ",{[}]:;\n\r";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value();
assert!(result.is_ok());
let source2 = ";comment";
let scanner2 = TokenScanner::new(source2, 0, 1, 1);
let nav_pos = scanner2.navigator().position();
assert_eq!(nav_pos, 0);
}
#[test]
fn token_scanner_scan_text_number_classification() {
let source = "123.45";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(crate::tokenizer::state::TokenContext::Document);
assert!(result.is_ok());
let token_type = result.unwrap();
assert_eq!(token_type, crate::tokenizer::tokens::TokenType::Number);
}
#[test]
fn token_scanner_section_header_boundary_coverage() {
let source = "[Section]";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_section_header();
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
crate::tokenizer::tokens::TokenType::SectionHeader
);
}
#[test]
fn token_scanner_style_override_brace_matching() {
let source = "{\\b1}text{\\b0}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_style_override();
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
crate::tokenizer::tokens::TokenType::OverrideBlock
);
}
#[test]
fn token_scanner_simd_fallback_forced_coverage() {
let source = "test,delimiter:content";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value();
assert!(result.is_ok());
}
#[test]
fn char_navigator_advance_char_utf8_length_tracking() {
let source = "a🎵b";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.advance_char().unwrap();
assert_eq!(nav.position(), 1);
nav.advance_char().unwrap();
assert_eq!(nav.position(), 5);
nav.advance_char().unwrap();
assert_eq!(nav.position(), 6);
}
#[test]
fn token_scanner_empty_span_edge_cases() {
let source = "";
let scanner = TokenScanner::new(source, 0, 1, 1);
let nav_result = scanner.navigator();
assert!(nav_result.is_at_end());
assert_eq!(nav_result.position(), 0);
assert_eq!(nav_result.line(), 1);
assert_eq!(nav_result.column(), 1);
}
#[test]
fn char_navigator_peek_operations_at_boundaries() {
let source = "a";
let mut nav = CharNavigator::new(source, 0, 1, 1);
assert_eq!(nav.peek_char().unwrap(), 'a');
assert!(nav.peek_next().is_err());
nav.advance_char().unwrap();
assert!(nav.is_at_end());
assert!(nav.peek_char().is_err());
assert!(nav.peek_next().is_err());
}
#[test]
fn token_scanner_all_delimiter_combinations_coverage() {
let delimiters = [',', ':', '{', '}', '[', ']', '\n', '\r'];
for &delimiter in &delimiters {
let source = format!("text{delimiter}more");
let mut scanner = TokenScanner::new(&source, 0, 1, 1);
let result = scanner.scan_field_value();
assert!(result.is_ok());
}
}
#[test]
fn char_navigator_newline_variations_comprehensive() {
let sources = [
"line1\nline2", "line1\rline2", "line1\r\nline2", ];
for source in &sources {
let mut nav = CharNavigator::new(source, 0, 1, 1);
while let Ok(ch) = nav.advance_char() {
if ch == '\n' || ch == '\r' {
break;
}
}
if !nav.is_at_end() {
nav.advance_char().ok(); assert!(nav.line() >= 2);
}
}
}
#[test]
fn char_navigator_carriage_return_line_increment() {
let source = "text\rmore";
let mut nav = CharNavigator::new(source, 0, 1, 1);
for _ in 0..4 {
nav.advance_char().unwrap();
}
let ch = nav.advance_char().unwrap();
assert_eq!(ch, '\r');
assert_eq!(nav.line(), 2);
assert_eq!(nav.column(), 1);
}
#[test]
fn char_navigator_newline_line_increment() {
let source = "text\nmore";
let mut nav = CharNavigator::new(source, 0, 1, 1);
for _ in 0..4 {
nav.advance_char().unwrap();
}
let ch = nav.advance_char().unwrap();
assert_eq!(ch, '\n');
assert_eq!(nav.line(), 2);
assert_eq!(nav.column(), 1);
}
#[test]
fn char_navigator_column_increment_default() {
let source = "abc";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.advance_char().unwrap(); assert_eq!(nav.column(), 2);
nav.advance_char().unwrap(); assert_eq!(nav.column(), 3);
nav.advance_char().unwrap(); assert_eq!(nav.column(), 4);
}
#[test]
fn char_navigator_skip_whitespace_loop() {
let source = " \t\n text";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.skip_whitespace();
assert_eq!(nav.position(), 4); }
#[test]
fn token_scanner_section_header_closing_bracket() {
let source = "[Test]";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_section_header();
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
crate::tokenizer::tokens::TokenType::SectionHeader
);
}
#[test]
fn token_scanner_style_override_closing_brace() {
let source = "{\\b1}";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_style_override();
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
crate::tokenizer::tokens::TokenType::OverrideBlock
);
}
#[test]
fn token_scanner_comment_scanning() {
let source = "! This is a comment";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_comment();
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
crate::tokenizer::tokens::TokenType::Comment
);
}
#[test]
fn token_scanner_scan_text_hex_detection() {
let source = "&H1234&";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(crate::tokenizer::state::TokenContext::Document);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
crate::tokenizer::tokens::TokenType::HexValue
);
}
#[test]
fn token_scanner_scan_text_number_detection_targeted() {
let source = "123.45";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(crate::tokenizer::state::TokenContext::Document);
assert!(result.is_ok());
assert_eq!(result.unwrap(), crate::tokenizer::tokens::TokenType::Number);
}
#[test]
fn token_scanner_scan_text_section_name_context() {
let source = "Script Info";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(crate::tokenizer::state::TokenContext::SectionHeader);
assert!(result.is_ok());
assert_eq!(
result.unwrap(),
crate::tokenizer::tokens::TokenType::SectionName
);
}
#[test]
fn token_scanner_scan_text_field_value_context_targeted() {
let source = "value_text";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(crate::tokenizer::state::TokenContext::FieldValue);
assert!(result.is_ok());
}
#[test]
fn token_scanner_scan_text_default_case() {
let source = "regular_text";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_text(crate::tokenizer::state::TokenContext::Document);
assert!(result.is_ok());
assert_eq!(result.unwrap(), crate::tokenizer::tokens::TokenType::Text);
}
#[test]
fn token_scanner_is_hex_value_ampersand_suffix() {
assert!(TokenScanner::is_hex_value("&H1234&"));
assert!(TokenScanner::is_hex_value("&HABCD&"));
assert!(!TokenScanner::is_hex_value("&H&")); }
#[test]
fn token_scanner_is_hex_value_no_ampersand() {
assert!(TokenScanner::is_hex_value("&H1234"));
assert!(TokenScanner::is_hex_value("&HABCD"));
}
#[test]
fn token_scanner_scan_field_value_basic() {
let source = "field_value,next";
let mut scanner = TokenScanner::new(source, 0, 1, 1);
let result = scanner.scan_field_value();
assert!(result.is_ok());
}
#[test]
fn char_navigator_peek_char_error_path() {
let source = "a";
let mut nav = CharNavigator::new(source, 0, 1, 1);
nav.advance_char().unwrap();
assert!(nav.is_at_end());
let result = nav.peek_char();
assert!(result.is_err());
}
#[test]
fn char_navigator_peek_next_error_path() {
let source = "a";
let nav = CharNavigator::new(source, 0, 1, 1);
let result = nav.peek_next();
assert!(result.is_err());
}
}