use crate::token::{
cursor::Cursor,
token::{Literal, Token, TokenKind},
};
pub fn scan_temporal<'b>(cursor: &mut Cursor<'b>) -> Option<Token<'b>> {
if cursor.peek() != Some('@') {
return None;
}
let start_pos = cursor.pos();
let start_line = cursor.line();
let start_column = cursor.column();
cursor.consume();
let content = cursor.consume_while(|c| {
c.is_ascii_alphanumeric() || c == '-' || c == ':' || c == '.' || c == '+' || c == '/' || c == 'T'
});
if content.is_empty() {
return None;
}
let temporal_start = start_pos + 1;
Some(Token {
kind: TokenKind::Literal(Literal::Temporal),
fragment: cursor.make_fragment(
temporal_start,
start_line,
start_column + 1, ),
})
}
#[cfg(test)]
pub mod tests {
use Literal::Temporal;
use super::*;
use crate::{bump::Bump, token::tokenize};
#[test]
fn test_temporal_date() {
let bump = Bump::new();
let tokens = tokenize(&bump, "@2024-01-15").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Temporal));
assert_eq!(tokens[0].fragment.text(), "2024-01-15");
}
#[test]
fn test_temporal_datetime() {
let bump = Bump::new();
let tokens = tokenize(&bump, "@2024-01-15T10:30:00").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Temporal));
assert_eq!(tokens[0].fragment.text(), "2024-01-15T10:30:00");
}
#[test]
fn test_temporal_with_timezone() {
let bump = Bump::new();
let tokens = tokenize(&bump, "@2024-01-15T10:30:00+05:30").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Temporal));
assert_eq!(tokens[0].fragment.text(), "2024-01-15T10:30:00+05:30");
}
#[test]
fn test_temporal_time_only() {
let bump = Bump::new();
let tokens = tokenize(&bump, "@10:30:00").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Temporal));
assert_eq!(tokens[0].fragment.text(), "10:30:00");
}
#[test]
fn test_temporal_with_microseconds() {
let bump = Bump::new();
let tokens = tokenize(&bump, "@2024-01-15T10:30:00.123456").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Temporal));
assert_eq!(tokens[0].fragment.text(), "2024-01-15T10:30:00.123456");
}
#[test]
fn test_temporal_alternative_format() {
let bump = Bump::new();
let tokens = tokenize(&bump, "@2024/01/15").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Temporal));
assert_eq!(tokens[0].fragment.text(), "2024/01/15");
}
#[test]
fn test_temporal_with_trailing() {
let bump = Bump::new();
let tokens = tokenize(&bump, "@2024-01-15 rest").unwrap();
assert_eq!(tokens[0].kind, TokenKind::Literal(Temporal));
assert_eq!(tokens[0].fragment.text(), "2024-01-15");
assert_eq!(tokens[1].kind, TokenKind::Identifier);
assert_eq!(tokens[1].fragment.text(), "rest");
}
#[test]
fn test_invalid_temporal() {
let bump = Bump::new();
let result = tokenize(&bump, "@");
assert!(result.is_err(), "@ alone should fail to tokenize");
let result = tokenize(&bump, "@#invalid");
assert!(result.is_err(), "@# should fail to tokenize as # is not valid");
let result = tokenize(&bump, "@ 2024");
assert!(result.is_err(), "@ followed by space should fail to tokenize");
}
}