1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4use core::{fmt, str::FromStr};
5use std::error::Error;
6
7#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
9pub enum SqlKeyword {
10 Select,
11 Insert,
12 Update,
13 Delete,
14 Create,
15 Alter,
16 Drop,
17 Table,
18 View,
19 Index,
20 Where,
21 From,
22 Join,
23 Group,
24 Order,
25 Limit,
26 Offset,
27 Returning,
28 Primary,
29 Foreign,
30 Key,
31 Unique,
32 Not,
33 Null,
34 Check,
35 Default,
36}
37
38impl SqlKeyword {
39 #[must_use]
41 pub const fn as_str(self) -> &'static str {
42 match self {
43 Self::Select => "SELECT",
44 Self::Insert => "INSERT",
45 Self::Update => "UPDATE",
46 Self::Delete => "DELETE",
47 Self::Create => "CREATE",
48 Self::Alter => "ALTER",
49 Self::Drop => "DROP",
50 Self::Table => "TABLE",
51 Self::View => "VIEW",
52 Self::Index => "INDEX",
53 Self::Where => "WHERE",
54 Self::From => "FROM",
55 Self::Join => "JOIN",
56 Self::Group => "GROUP",
57 Self::Order => "ORDER",
58 Self::Limit => "LIMIT",
59 Self::Offset => "OFFSET",
60 Self::Returning => "RETURNING",
61 Self::Primary => "PRIMARY",
62 Self::Foreign => "FOREIGN",
63 Self::Key => "KEY",
64 Self::Unique => "UNIQUE",
65 Self::Not => "NOT",
66 Self::Null => "NULL",
67 Self::Check => "CHECK",
68 Self::Default => "DEFAULT",
69 }
70 }
71
72 #[must_use]
74 pub const fn kind(self) -> SqlKeywordKind {
75 match self {
76 Self::Select => SqlKeywordKind::DataQuery,
77 Self::Insert | Self::Update | Self::Delete => SqlKeywordKind::DataMutation,
78 Self::Create | Self::Alter | Self::Drop => SqlKeywordKind::Definition,
79 Self::Table | Self::View | Self::Index => SqlKeywordKind::Object,
80 Self::Where
81 | Self::From
82 | Self::Join
83 | Self::Group
84 | Self::Order
85 | Self::Limit
86 | Self::Offset
87 | Self::Returning => SqlKeywordKind::Clause,
88 Self::Primary | Self::Foreign | Self::Key | Self::Unique | Self::Check => {
89 SqlKeywordKind::Constraint
90 },
91 Self::Not | Self::Null | Self::Default => SqlKeywordKind::Modifier,
92 }
93 }
94}
95
96impl fmt::Display for SqlKeyword {
97 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
98 formatter.write_str(self.as_str())
99 }
100}
101
102impl FromStr for SqlKeyword {
103 type Err = SqlKeywordParseError;
104
105 fn from_str(input: &str) -> Result<Self, Self::Err> {
106 match normalized_word(input)?.as_str() {
107 "SELECT" => Ok(Self::Select),
108 "INSERT" => Ok(Self::Insert),
109 "UPDATE" => Ok(Self::Update),
110 "DELETE" => Ok(Self::Delete),
111 "CREATE" => Ok(Self::Create),
112 "ALTER" => Ok(Self::Alter),
113 "DROP" => Ok(Self::Drop),
114 "TABLE" => Ok(Self::Table),
115 "VIEW" => Ok(Self::View),
116 "INDEX" => Ok(Self::Index),
117 "WHERE" => Ok(Self::Where),
118 "FROM" => Ok(Self::From),
119 "JOIN" => Ok(Self::Join),
120 "GROUP" => Ok(Self::Group),
121 "ORDER" => Ok(Self::Order),
122 "LIMIT" => Ok(Self::Limit),
123 "OFFSET" => Ok(Self::Offset),
124 "RETURNING" => Ok(Self::Returning),
125 "PRIMARY" => Ok(Self::Primary),
126 "FOREIGN" => Ok(Self::Foreign),
127 "KEY" => Ok(Self::Key),
128 "UNIQUE" => Ok(Self::Unique),
129 "NOT" => Ok(Self::Not),
130 "NULL" => Ok(Self::Null),
131 "CHECK" => Ok(Self::Check),
132 "DEFAULT" => Ok(Self::Default),
133 _ => Err(SqlKeywordParseError::Unknown),
134 }
135 }
136}
137
138impl TryFrom<&str> for SqlKeyword {
139 type Error = SqlKeywordParseError;
140
141 fn try_from(value: &str) -> Result<Self, Self::Error> {
142 value.parse()
143 }
144}
145
146#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
148pub enum SqlKeywordKind {
149 #[default]
150 DataQuery,
151 DataMutation,
152 Definition,
153 Object,
154 Clause,
155 Constraint,
156 Modifier,
157}
158
159#[derive(Clone, Copy, Debug, Eq, PartialEq)]
161pub enum SqlKeywordParseError {
162 Empty,
163 Unknown,
164}
165
166impl fmt::Display for SqlKeywordParseError {
167 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
168 match self {
169 Self::Empty => formatter.write_str("SQL keyword cannot be empty"),
170 Self::Unknown => formatter.write_str("unknown SQL keyword"),
171 }
172 }
173}
174
175impl Error for SqlKeywordParseError {}
176
177#[must_use]
179pub fn is_common_keyword(input: &str) -> bool {
180 input.parse::<SqlKeyword>().is_ok()
181}
182
183#[must_use]
185pub fn is_reserved_like(input: &str) -> bool {
186 is_common_keyword(input)
187}
188
189fn normalized_word(input: &str) -> Result<String, SqlKeywordParseError> {
190 let trimmed = input.trim();
191 if trimmed.is_empty() {
192 Err(SqlKeywordParseError::Empty)
193 } else {
194 Ok(trimmed.to_ascii_uppercase())
195 }
196}
197
198#[cfg(test)]
199mod tests {
200 use super::{SqlKeyword, SqlKeywordKind, SqlKeywordParseError, is_common_keyword};
201
202 #[test]
203 fn parses_common_keywords() -> Result<(), SqlKeywordParseError> {
204 let keyword: SqlKeyword = "select".parse()?;
205 assert_eq!(keyword, SqlKeyword::Select);
206 assert_eq!(keyword.kind(), SqlKeywordKind::DataQuery);
207 assert_eq!(keyword.to_string(), "SELECT");
208 assert!(is_common_keyword("where"));
209 Ok(())
210 }
211
212 #[test]
213 fn rejects_unknown_keywords() {
214 assert_eq!("".parse::<SqlKeyword>(), Err(SqlKeywordParseError::Empty));
215 assert_eq!(
216 "UPSERT".parse::<SqlKeyword>(),
217 Err(SqlKeywordParseError::Unknown)
218 );
219 }
220}