use super::Lexer;
use crate::error::RableError;
use crate::token::TokenType;
#[allow(clippy::unwrap_used)]
fn collect_tokens(source: &str) -> Vec<(TokenType, String)> {
let mut lexer = Lexer::new(source, false);
let mut tokens = Vec::new();
loop {
let tok = lexer.next_token().unwrap();
if tok.kind == TokenType::Eof {
break;
}
tokens.push((tok.kind, tok.value));
}
tokens
}
#[test]
fn invalid_body_becomes_opaque_word() {
let tokens = collect_tokens("`else echo`");
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0].0, TokenType::Word);
assert_eq!(tokens[0].1, "`else echo`");
}
#[test]
fn escape_does_not_terminate() {
let tokens = collect_tokens("`else \\`then\\` echo`");
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0].0, TokenType::Word);
assert_eq!(tokens[0].1, "`else \\`then\\` echo`");
}
#[test]
fn literal_newline_escape_consumes_two_bytes() {
let tokens = collect_tokens("`else a\\nb`");
assert_eq!(tokens.len(), 1);
assert_eq!(tokens[0].0, TokenType::Word);
assert_eq!(tokens[0].1, "`else a\\nb`");
}
#[test]
fn trailing_backslash_at_eof_surfaces_error() {
let mut lexer = Lexer::new("`else\\", false);
assert!(matches!(
lexer.next_token(),
Err(RableError::MatchedPair { .. }),
));
}
#[test]
fn unterminated_body_surfaces_error() {
let mut lexer = Lexer::new("`else echo", false);
assert!(matches!(
lexer.next_token(),
Err(RableError::MatchedPair { .. }),
));
}
#[test]
#[allow(clippy::unwrap_used)]
fn newlines_in_body_advance_line_counter() {
let mut lexer = Lexer::new("`else\necho\n`\nok", false);
let bt = lexer.next_token().unwrap();
assert_eq!(bt.kind, TokenType::Word);
assert_eq!(bt.value, "`else\necho\n`");
let nl = lexer.next_token().unwrap();
assert_eq!(nl.kind, TokenType::Newline);
let ok = lexer.next_token().unwrap();
assert_eq!(ok.value, "ok");
assert_eq!(ok.line, 4);
}