1use phf::phf_map;
59
60pub trait CheckKeyword {
62 fn is_keyword(&self) -> bool;
66
67 fn keyword_status(&self) -> KeywordStatus;
69
70 fn into_safe(self) -> String;
73}
74
75#[derive(Copy, Clone, Eq, PartialEq, Debug)]
77pub enum KeywordStatus {
78 NotKeyword,
80
81 Strict {
83 can_be_raw: bool,
86 },
87
88 Reserved,
91
92 Weak {
98 restriction: WeakRestriction,
100 },
101}
102
103#[derive(Copy, Clone, Eq, PartialEq, Debug)]
105pub enum WeakRestriction {
106 None,
108
109 LifetimeOrLoop,
111
112 Dyn,
115}
116
117use KeywordStatus::*;
118use WeakRestriction::*;
119
120static KEYWORDS: phf::Map<&'static str, KeywordStatus> = phf_map! {
121
122 "as" => Strict { can_be_raw: true },
125 "break" => Strict { can_be_raw: true },
126 "const" => Strict { can_be_raw: true },
127 "continue" => Strict { can_be_raw: true },
128 "crate" => Strict { can_be_raw: false },
129 "else" => Strict { can_be_raw: true },
130 "enum" => Strict { can_be_raw: true },
131 "extern" => Strict { can_be_raw: true },
132 "false" => Strict { can_be_raw: true },
133 "fn" => Strict { can_be_raw: true },
134 "for" => Strict { can_be_raw: true },
135 "if" => Strict { can_be_raw: true },
136 "impl" => Strict { can_be_raw: true },
137 "in" => Strict { can_be_raw: true },
138 "let" => Strict { can_be_raw: true },
139 "loop" => Strict { can_be_raw: true },
140 "match" => Strict { can_be_raw: true },
141 "mod" => Strict { can_be_raw: true },
142 "move" => Strict { can_be_raw: true },
143 "mut" => Strict { can_be_raw: true },
144 "pub" => Strict { can_be_raw: true },
145 "ref" => Strict { can_be_raw: true },
146 "return" => Strict { can_be_raw: true },
147 "self" => Strict { can_be_raw: false },
148 "Self" => Strict { can_be_raw: false },
149 "static" => Strict { can_be_raw: true },
150 "struct" => Strict { can_be_raw: true },
151 "super" => Strict { can_be_raw: false },
152 "trait" => Strict { can_be_raw: true },
153 "true" => Strict { can_be_raw: true },
154 "type" => Strict { can_be_raw: true },
155 "unsafe" => Strict { can_be_raw: true },
156 "use" => Strict { can_be_raw: true },
157 "where" => Strict { can_be_raw: true },
158 "while" => Strict { can_be_raw: true },
159
160 "async" => if cfg!(feature = "2018") { Strict { can_be_raw: true } } else { NotKeyword },
163 "await" => if cfg!(feature = "2018") { Strict { can_be_raw: true } } else { NotKeyword },
164
165 "dyn" => if cfg!(feature = "2018") {
168 Strict { can_be_raw: true }
169 } else {
170 Weak { restriction: Dyn }
171 },
172
173 "abstract" => Reserved,
176 "become" => Reserved,
177 "box" => Reserved,
178 "do" => Reserved,
179 "final" => Reserved,
180 "macro" => Reserved,
181 "override" => Reserved,
182 "priv" => Reserved,
183 "typeof" => Reserved,
184 "unsized" => Reserved,
185 "virtual" => Reserved,
186 "yield" => Reserved,
187
188 "try" => if cfg!(feature = "2018") { Reserved } else { NotKeyword },
191
192 "macro_rules" => Weak { restriction: None },
195 "union" => Weak { restriction: None },
196 "'static" => Weak { restriction: LifetimeOrLoop }
197};
198
199impl<T: AsRef<str>> CheckKeyword for T {
200 fn is_keyword(&self) -> bool {
201 matches!(self.keyword_status(), Strict { .. } | Reserved)
202 }
203
204 fn keyword_status(&self) -> KeywordStatus {
205 *KEYWORDS.get(self.as_ref()).unwrap_or(&NotKeyword)
206 }
207
208 fn into_safe(self) -> String {
209 let self_ref = self.as_ref();
210 match self.keyword_status() {
211 Strict { can_be_raw: false }
212 | Weak {
213 restriction: LifetimeOrLoop,
214 } => format!("{self_ref}_"),
215 Strict { .. } | Reserved | Weak { restriction: Dyn } => format!("r#{self_ref}"),
216 _ => self_ref.to_string(),
217 }
218 }
219}
220
221#[cfg(test)]
222mod tests {
223 use super::*;
224
225 #[test]
226 fn is_keyword() {
227 assert!(String::from("match").is_keyword());
228 assert!(!"hello".is_keyword());
229
230 assert!("crate".is_keyword());
231
232 assert_eq!(String::from("async").is_keyword(), cfg!(feature = "2018"));
233 }
234
235 #[test]
236 fn keyword_status() {
237 assert_eq!("asdf".keyword_status(), NotKeyword);
238
239 assert_eq!(
240 "dyn".keyword_status(),
241 if cfg!(feature = "2018") {
242 Strict { can_be_raw: true }
243 } else {
244 Weak { restriction: Dyn }
245 }
246 );
247
248 assert_eq!(
249 "'static".keyword_status(),
250 Weak {
251 restriction: LifetimeOrLoop
252 }
253 );
254 }
255
256 #[test]
257 fn into_safe() {
258 assert_eq!(String::from("match").into_safe(), "r#match");
259 assert_eq!("asdf".into_safe(), "asdf");
260
261 assert_eq!(
262 "await".into_safe(),
263 if cfg!(feature = "2018") {
264 "r#await"
265 } else {
266 "await"
267 }
268 );
269
270 assert_eq!("self".into_safe(), "self_");
271 }
272}