use crate::token::{
cursor::Cursor,
token::{Literal::Text, Token, TokenKind},
};
pub fn scan_text<'b>(cursor: &mut Cursor<'b>) -> Option<Token<'b>> {
let quote = cursor.peek()?;
if quote != '\'' && quote != '"' {
return None;
}
let start_pos = cursor.pos();
let start_line = cursor.line();
let start_column = cursor.column();
cursor.consume();
let text_start = cursor.pos();
while let Some(ch) = cursor.peek() {
if ch == quote {
let text_end = cursor.pos();
cursor.consume();
return Some(Token {
kind: TokenKind::Literal(Text),
fragment: cursor.make_utf8_fragment(
text_start,
text_end,
start_line,
start_column + 1,
start_pos,
),
});
}
cursor.consume();
}
None }
#[cfg(test)]
pub mod tests {
use super::*;
use crate::{
bump::Bump,
token::{token::Literal::Number, tokenize},
};
#[test]
fn test_text_single_quotes() {
let bump = Bump::new();
let tokens = tokenize(&bump, "'hello'").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Text));
assert_eq!(tokens[0].fragment.text(), "hello");
}
#[test]
fn test_text_double_quotes() {
let bump = Bump::new();
let tokens = tokenize(&bump, "\"hello\"").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Text));
assert_eq!(tokens[0].fragment.text(), "hello");
}
#[test]
fn test_text_single_quotes_with_double_inside() {
let bump = Bump::new();
let tokens = tokenize(&bump, "'some text\"xx\"no problem'").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Text));
assert_eq!(tokens[0].fragment.text(), "some text\"xx\"no problem");
}
#[test]
fn test_text_double_quotes_with_single_inside() {
let bump = Bump::new();
let tokens = tokenize(&bump, "\"some text'xx'no problem\"").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Text));
assert_eq!(tokens[0].fragment.text(), "some text'xx'no problem");
}
#[test]
fn test_text_with_trailing() {
let bump = Bump::new();
let tokens = tokenize(&bump, "'data' 123").unwrap();
assert_eq!(tokens[0].fragment.text(), "data");
assert_eq!(tokens[1].kind, TokenKind::Literal(Number));
assert_eq!(tokens[1].fragment.text(), "123");
}
#[test]
fn test_text_double_quotes_with_trailing() {
let bump = Bump::new();
let tokens = tokenize(&bump, "\"data\" 123").unwrap();
assert_eq!(tokens[0].fragment.text(), "data");
assert_eq!(tokens[1].kind, TokenKind::Literal(Number));
assert_eq!(tokens[1].fragment.text(), "123");
}
#[test]
fn test_text_single_unterminated_fails() {
let bump = Bump::new();
let tokens = tokenize(&bump, "'not closed");
assert!(tokens.is_err() || tokens.unwrap().iter().all(|t| !matches!(t.kind, TokenKind::Literal(Text))));
}
#[test]
fn test_text_double_unterminated_fails() {
let bump = Bump::new();
let tokens = tokenize(&bump, "\"not closed");
assert!(tokens.is_err() || tokens.unwrap().iter().all(|t| !matches!(t.kind, TokenKind::Literal(Text))));
}
#[test]
fn test_text_empty_single_quotes() {
let bump = Bump::new();
let tokens = tokenize(&bump, "''").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Text));
assert_eq!(tokens[0].fragment.text(), "");
}
#[test]
fn test_text_empty_double_quotes() {
let bump = Bump::new();
let tokens = tokenize(&bump, "\"\"").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Text));
assert_eq!(tokens[0].fragment.text(), "");
}
#[test]
fn test_text_mixed_quotes_comptokenize() {
let bump = Bump::new();
let tokens = tokenize(&bump, "'He said \"Hello\" and she replied \"Hi\"'").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Text));
assert_eq!(tokens[0].fragment.text(), "He said \"Hello\" and she replied \"Hi\"");
}
#[test]
fn test_text_multiple_nested_quotes() {
let bump = Bump::new();
let tokens = tokenize(&bump, "\"It's a 'nice' day, isn't it?\"").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Text));
assert_eq!(tokens[0].fragment.text(), "It's a 'nice' day, isn't it?");
}
}