1use crate::span::Span;
4use std::fmt;
5
6#[derive(Debug, Clone, PartialEq)]
8pub struct Token {
9 pub kind: TokenKind,
11 pub span: Span,
13}
14
15impl Token {
16 pub const fn new(kind: TokenKind, span: Span) -> Self {
18 Self { kind, span }
19 }
20}
21
22impl fmt::Display for Token {
23 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
24 write!(f, "{:?} @ {}", self.kind, self.span)
25 }
26}
27
28#[derive(Debug, Clone, PartialEq)]
30pub enum TokenKind {
31 Datasource,
34 Generator,
36 Model,
38 Enum,
40 Type,
42 True,
44 False,
46
47 Ident(String),
50 String(String),
52 Number(String),
54
55 At,
58 AtAt,
60
61 LBrace,
64 RBrace,
66 LBracket,
68 RBracket,
70 LParen,
72 RParen,
74 Comma,
76 Colon,
78 Equal,
80 Question,
82 Bang,
84 Dot,
86
87 Star,
90 Plus,
92 Minus,
94 Slash,
96 Pipe,
98 DoublePipe,
100 LAngle,
102 RAngle,
104 Percent,
106 LessEqual,
108 GreaterEqual,
110 BangEqual,
112
113 Newline,
116 Eof,
118}
119
120impl TokenKind {
121 pub fn is_keyword(&self) -> bool {
123 matches!(
124 self,
125 TokenKind::Datasource
126 | TokenKind::Generator
127 | TokenKind::Model
128 | TokenKind::Enum
129 | TokenKind::Type
130 | TokenKind::True
131 | TokenKind::False
132 )
133 }
134
135 pub fn from_ident(ident: &str) -> Self {
137 match ident {
138 "datasource" => TokenKind::Datasource,
139 "generator" => TokenKind::Generator,
140 "model" => TokenKind::Model,
141 "enum" => TokenKind::Enum,
142 "type" => TokenKind::Type,
143 "true" => TokenKind::True,
144 "false" => TokenKind::False,
145 _ => TokenKind::Ident(ident.to_string()),
146 }
147 }
148}
149
150impl fmt::Display for TokenKind {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 match self {
153 TokenKind::Datasource => write!(f, "datasource"),
154 TokenKind::Generator => write!(f, "generator"),
155 TokenKind::Model => write!(f, "model"),
156 TokenKind::Enum => write!(f, "enum"),
157 TokenKind::Type => write!(f, "type"),
158 TokenKind::True => write!(f, "true"),
159 TokenKind::False => write!(f, "false"),
160 TokenKind::Ident(s) => write!(f, "identifier '{}'", s),
161 TokenKind::String(s) => write!(f, "string \"{}\"", s),
162 TokenKind::Number(s) => write!(f, "number {}", s),
163 TokenKind::At => write!(f, "@"),
164 TokenKind::AtAt => write!(f, "@@"),
165 TokenKind::LBrace => write!(f, "{{"),
166 TokenKind::RBrace => write!(f, "}}"),
167 TokenKind::LBracket => write!(f, "["),
168 TokenKind::RBracket => write!(f, "]"),
169 TokenKind::LParen => write!(f, "("),
170 TokenKind::RParen => write!(f, ")"),
171 TokenKind::Comma => write!(f, ","),
172 TokenKind::Colon => write!(f, ":"),
173 TokenKind::Equal => write!(f, "="),
174 TokenKind::Question => write!(f, "?"),
175 TokenKind::Bang => write!(f, "!"),
176 TokenKind::Dot => write!(f, "."),
177 TokenKind::Star => write!(f, "*"),
178 TokenKind::Plus => write!(f, "+"),
179 TokenKind::Minus => write!(f, "-"),
180 TokenKind::Slash => write!(f, "/"),
181 TokenKind::Pipe => write!(f, "|"),
182 TokenKind::DoublePipe => write!(f, "||"),
183 TokenKind::LAngle => write!(f, "<"),
184 TokenKind::RAngle => write!(f, ">"),
185 TokenKind::Percent => write!(f, "%"),
186 TokenKind::LessEqual => write!(f, "<="),
187 TokenKind::GreaterEqual => write!(f, ">="),
188 TokenKind::BangEqual => write!(f, "!="),
189 TokenKind::Newline => write!(f, "newline"),
190 TokenKind::Eof => write!(f, "end of file"),
191 }
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198
199 #[test]
200 fn test_keyword_detection() {
201 assert!(TokenKind::Datasource.is_keyword());
202 assert!(TokenKind::Model.is_keyword());
203 assert!(!TokenKind::Ident("foo".to_string()).is_keyword());
204 }
205
206 #[test]
207 fn test_from_ident() {
208 assert_eq!(TokenKind::from_ident("model"), TokenKind::Model);
209 assert_eq!(TokenKind::from_ident("datasource"), TokenKind::Datasource);
210 assert_eq!(
211 TokenKind::from_ident("User"),
212 TokenKind::Ident("User".to_string())
213 );
214 }
215
216 #[test]
217 fn test_token_display() {
218 let token = Token::new(TokenKind::Model, Span::new(0, 5));
219 let display = token.to_string();
220 assert!(display.contains("Model"));
221 }
222}