use crate::span::Span;
use std::fmt;
#[derive(Debug, Clone, PartialEq)]
pub struct Token {
pub kind: TokenKind,
pub span: Span,
}
impl Token {
pub const fn new(kind: TokenKind, span: Span) -> Self {
Self { kind, span }
}
}
impl fmt::Display for Token {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:?} @ {}", self.kind, self.span)
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum TokenKind {
Datasource,
Generator,
Model,
Enum,
Type,
True,
False,
Ident(String),
String(String),
Number(String),
At,
AtAt,
LBrace,
RBrace,
LBracket,
RBracket,
LParen,
RParen,
Comma,
Colon,
Equal,
Question,
Bang,
Dot,
Star,
Plus,
Minus,
Slash,
Pipe,
DoublePipe,
LAngle,
RAngle,
Percent,
LessEqual,
GreaterEqual,
BangEqual,
Newline,
Eof,
}
impl TokenKind {
pub fn is_keyword(&self) -> bool {
matches!(
self,
TokenKind::Datasource
| TokenKind::Generator
| TokenKind::Model
| TokenKind::Enum
| TokenKind::Type
| TokenKind::True
| TokenKind::False
)
}
pub fn from_ident(ident: &str) -> Self {
match ident {
"datasource" => TokenKind::Datasource,
"generator" => TokenKind::Generator,
"model" => TokenKind::Model,
"enum" => TokenKind::Enum,
"type" => TokenKind::Type,
"true" => TokenKind::True,
"false" => TokenKind::False,
_ => TokenKind::Ident(ident.to_string()),
}
}
}
impl fmt::Display for TokenKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TokenKind::Datasource => write!(f, "datasource"),
TokenKind::Generator => write!(f, "generator"),
TokenKind::Model => write!(f, "model"),
TokenKind::Enum => write!(f, "enum"),
TokenKind::Type => write!(f, "type"),
TokenKind::True => write!(f, "true"),
TokenKind::False => write!(f, "false"),
TokenKind::Ident(s) => write!(f, "identifier '{}'", s),
TokenKind::String(s) => write!(f, "string \"{}\"", s),
TokenKind::Number(s) => write!(f, "number {}", s),
TokenKind::At => write!(f, "@"),
TokenKind::AtAt => write!(f, "@@"),
TokenKind::LBrace => write!(f, "{{"),
TokenKind::RBrace => write!(f, "}}"),
TokenKind::LBracket => write!(f, "["),
TokenKind::RBracket => write!(f, "]"),
TokenKind::LParen => write!(f, "("),
TokenKind::RParen => write!(f, ")"),
TokenKind::Comma => write!(f, ","),
TokenKind::Colon => write!(f, ":"),
TokenKind::Equal => write!(f, "="),
TokenKind::Question => write!(f, "?"),
TokenKind::Bang => write!(f, "!"),
TokenKind::Dot => write!(f, "."),
TokenKind::Star => write!(f, "*"),
TokenKind::Plus => write!(f, "+"),
TokenKind::Minus => write!(f, "-"),
TokenKind::Slash => write!(f, "/"),
TokenKind::Pipe => write!(f, "|"),
TokenKind::DoublePipe => write!(f, "||"),
TokenKind::LAngle => write!(f, "<"),
TokenKind::RAngle => write!(f, ">"),
TokenKind::Percent => write!(f, "%"),
TokenKind::LessEqual => write!(f, "<="),
TokenKind::GreaterEqual => write!(f, ">="),
TokenKind::BangEqual => write!(f, "!="),
TokenKind::Newline => write!(f, "newline"),
TokenKind::Eof => write!(f, "end of file"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keyword_detection() {
assert!(TokenKind::Datasource.is_keyword());
assert!(TokenKind::Model.is_keyword());
assert!(!TokenKind::Ident("foo".to_string()).is_keyword());
}
#[test]
fn test_from_ident() {
assert_eq!(TokenKind::from_ident("model"), TokenKind::Model);
assert_eq!(TokenKind::from_ident("datasource"), TokenKind::Datasource);
assert_eq!(
TokenKind::from_ident("User"),
TokenKind::Ident("User".to_string())
);
}
#[test]
fn test_token_display() {
let token = Token::new(TokenKind::Model, Span::new(0, 5));
let display = token.to_string();
assert!(display.contains("Model"));
}
}