1pub const PYTHON_KEYWORDS: &[&str] = &[
19 "False", "None", "True", "and", "as", "assert", "async", "await", "break", "class", "continue", "def", "del",
20 "elif", "else", "except", "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", "nonlocal",
21 "not", "or", "pass", "raise", "return", "try", "type", "while", "with", "yield",
22];
23
24pub const PYTHON_STR_METHODS: &[&str] = &[
31 "capitalize",
32 "casefold",
33 "center",
34 "count",
35 "encode",
36 "endswith",
37 "expandtabs",
38 "find",
39 "format",
40 "format_map",
41 "index",
42 "isalnum",
43 "isalpha",
44 "isascii",
45 "isdecimal",
46 "isdigit",
47 "isidentifier",
48 "islower",
49 "isnumeric",
50 "isprintable",
51 "isspace",
52 "istitle",
53 "isupper",
54 "join",
55 "ljust",
56 "lower",
57 "lstrip",
58 "maketrans",
59 "partition",
60 "removeprefix",
61 "removesuffix",
62 "replace",
63 "rfind",
64 "rindex",
65 "rjust",
66 "rpartition",
67 "rsplit",
68 "rstrip",
69 "split",
70 "splitlines",
71 "startswith",
72 "strip",
73 "swapcase",
74 "title",
75 "translate",
76 "upper",
77 "zfill",
78];
79
80pub const JAVA_KEYWORDS: &[&str] = &[
82 "abstract",
83 "assert",
84 "boolean",
85 "break",
86 "byte",
87 "case",
88 "catch",
89 "char",
90 "class",
91 "const",
92 "continue",
93 "default",
94 "do",
95 "double",
96 "else",
97 "enum",
98 "extends",
99 "final",
100 "finally",
101 "float",
102 "for",
103 "goto",
104 "if",
105 "implements",
106 "import",
107 "instanceof",
108 "int",
109 "interface",
110 "long",
111 "native",
112 "new",
113 "package",
114 "private",
115 "protected",
116 "public",
117 "return",
118 "short",
119 "static",
120 "strictfp",
121 "super",
122 "switch",
123 "synchronized",
124 "this",
125 "throw",
126 "throws",
127 "transient",
128 "try",
129 "void",
130 "volatile",
131 "while",
132];
133
134pub const CSHARP_KEYWORDS: &[&str] = &[
136 "abstract",
137 "as",
138 "base",
139 "bool",
140 "break",
141 "byte",
142 "case",
143 "catch",
144 "char",
145 "checked",
146 "class",
147 "const",
148 "continue",
149 "decimal",
150 "default",
151 "delegate",
152 "do",
153 "double",
154 "else",
155 "enum",
156 "event",
157 "explicit",
158 "extern",
159 "false",
160 "finally",
161 "fixed",
162 "float",
163 "for",
164 "foreach",
165 "goto",
166 "if",
167 "implicit",
168 "in",
169 "int",
170 "interface",
171 "internal",
172 "is",
173 "lock",
174 "long",
175 "namespace",
176 "new",
177 "null",
178 "object",
179 "operator",
180 "out",
181 "override",
182 "params",
183 "private",
184 "protected",
185 "public",
186 "readonly",
187 "ref",
188 "return",
189 "sbyte",
190 "sealed",
191 "short",
192 "sizeof",
193 "stackalloc",
194 "static",
195 "string",
196 "struct",
197 "switch",
198 "this",
199 "throw",
200 "true",
201 "try",
202 "typeof",
203 "uint",
204 "ulong",
205 "unchecked",
206 "unsafe",
207 "ushort",
208 "using",
209 "virtual",
210 "void",
211 "volatile",
212 "while",
213];
214
215pub const PHP_KEYWORDS: &[&str] = &[
217 "abstract",
218 "and",
219 "as",
220 "break",
221 "callable",
222 "case",
223 "catch",
224 "class",
225 "clone",
226 "const",
227 "continue",
228 "declare",
229 "default",
230 "die",
231 "do",
232 "echo",
233 "else",
234 "elseif",
235 "empty",
236 "enddeclare",
237 "endfor",
238 "endforeach",
239 "endif",
240 "endswitch",
241 "endwhile",
242 "eval",
243 "exit",
244 "extends",
245 "final",
246 "finally",
247 "fn",
248 "for",
249 "foreach",
250 "function",
251 "global",
252 "goto",
253 "if",
254 "implements",
255 "include",
256 "instanceof",
257 "insteadof",
258 "interface",
259 "isset",
260 "list",
261 "match",
262 "namespace",
263 "new",
264 "or",
265 "print",
266 "private",
267 "protected",
268 "public",
269 "readonly",
270 "require",
271 "return",
272 "static",
273 "switch",
274 "throw",
275 "trait",
276 "try",
277 "unset",
278 "use",
279 "var",
280 "while",
281 "xor",
282 "yield",
283];
284
285pub const RUBY_KEYWORDS: &[&str] = &[
287 "__ENCODING__",
288 "__FILE__",
289 "__LINE__",
290 "BEGIN",
291 "END",
292 "alias",
293 "and",
294 "begin",
295 "break",
296 "case",
297 "class",
298 "def",
299 "defined?",
300 "do",
301 "else",
302 "elsif",
303 "end",
304 "ensure",
305 "false",
306 "for",
307 "if",
308 "in",
309 "module",
310 "next",
311 "nil",
312 "not",
313 "or",
314 "redo",
315 "rescue",
316 "retry",
317 "return",
318 "self",
319 "super",
320 "then",
321 "true",
322 "undef",
323 "unless",
324 "until",
325 "when",
326 "while",
327 "yield",
328];
329
330pub const ELIXIR_KEYWORDS: &[&str] = &[
332 "after", "and", "catch", "do", "else", "end", "false", "fn", "in", "nil", "not", "or", "rescue", "true", "when",
333];
334
335pub const GO_KEYWORDS: &[&str] = &[
337 "break",
338 "case",
339 "chan",
340 "const",
341 "continue",
342 "default",
343 "defer",
344 "else",
345 "fallthrough",
346 "for",
347 "func",
348 "go",
349 "goto",
350 "if",
351 "import",
352 "interface",
353 "map",
354 "package",
355 "range",
356 "return",
357 "select",
358 "struct",
359 "switch",
360 "type",
361 "var",
362];
363
364pub const JS_KEYWORDS: &[&str] = &[
366 "abstract",
367 "arguments",
368 "await",
369 "boolean",
370 "break",
371 "byte",
372 "case",
373 "catch",
374 "char",
375 "class",
376 "const",
377 "continue",
378 "debugger",
379 "default",
380 "delete",
381 "do",
382 "double",
383 "else",
384 "enum",
385 "eval",
386 "export",
387 "extends",
388 "false",
389 "final",
390 "finally",
391 "float",
392 "for",
393 "function",
394 "goto",
395 "if",
396 "implements",
397 "import",
398 "in",
399 "instanceof",
400 "int",
401 "interface",
402 "let",
403 "long",
404 "native",
405 "new",
406 "null",
407 "package",
408 "private",
409 "protected",
410 "public",
411 "return",
412 "short",
413 "static",
414 "super",
415 "switch",
416 "synchronized",
417 "this",
418 "throw",
419 "throws",
420 "transient",
421 "true",
422 "try",
423 "typeof",
424 "var",
425 "void",
426 "volatile",
427 "while",
428 "with",
429 "yield",
430];
431
432pub const R_KEYWORDS: &[&str] = &[
434 "FALSE", "Inf", "NA", "NaN", "NULL", "TRUE", "break", "else", "for", "function", "if", "in", "next", "repeat",
435 "return", "while",
436];
437
438pub const KOTLIN_KEYWORDS: &[&str] = &[
440 "as",
441 "break",
442 "class",
443 "continue",
444 "do",
445 "else",
446 "false",
447 "for",
448 "fun",
449 "if",
450 "in",
451 "interface",
452 "is",
453 "null",
454 "object",
455 "package",
456 "return",
457 "super",
458 "this",
459 "throw",
460 "true",
461 "try",
462 "typealias",
463 "typeof",
464 "val",
465 "var",
466 "when",
467 "while",
468 "by",
470 "init",
471 "constructor",
472 "field",
473 "value",
474 "where",
475];
476
477pub const SWIFT_KEYWORDS: &[&str] = &[
479 "associatedtype",
480 "class",
481 "deinit",
482 "enum",
483 "extension",
484 "fileprivate",
485 "func",
486 "import",
487 "init",
488 "inout",
489 "internal",
490 "let",
491 "open",
492 "operator",
493 "private",
494 "protocol",
495 "public",
496 "rethrows",
497 "static",
498 "struct",
499 "subscript",
500 "typealias",
501 "var",
502 "break",
503 "case",
504 "continue",
505 "default",
506 "defer",
507 "do",
508 "else",
509 "fallthrough",
510 "for",
511 "guard",
512 "if",
513 "in",
514 "repeat",
515 "return",
516 "switch",
517 "where",
518 "while",
519 "as",
520 "Any",
521 "catch",
522 "false",
523 "is",
524 "nil",
525 "super",
526 "self",
527 "Self",
528 "throw",
529 "throws",
530 "true",
531 "try",
532 "_",
533];
534
535pub const DART_KEYWORDS: &[&str] = &[
537 "abstract",
538 "as",
539 "assert",
540 "async",
541 "await",
542 "break",
543 "case",
544 "catch",
545 "class",
546 "const",
547 "continue",
548 "covariant",
549 "default",
550 "deferred",
551 "do",
552 "dynamic",
553 "else",
554 "enum",
555 "export",
556 "extends",
557 "extension",
558 "external",
559 "factory",
560 "false",
561 "final",
562 "finally",
563 "for",
564 "Function",
565 "get",
566 "hide",
567 "if",
568 "implements",
569 "import",
570 "in",
571 "interface",
572 "is",
573 "late",
574 "library",
575 "mixin",
576 "new",
577 "null",
578 "of",
579 "on",
580 "operator",
581 "part",
582 "required",
583 "rethrow",
584 "return",
585 "set",
586 "show",
587 "static",
588 "super",
589 "switch",
590 "sync",
591 "this",
592 "throw",
593 "true",
594 "try",
595 "typedef",
596 "var",
597 "void",
598 "when",
599 "while",
600 "with",
601 "yield",
602];
603
604pub const GLEAM_KEYWORDS: &[&str] = &[
606 "as",
607 "assert",
608 "auto",
609 "case",
610 "const",
611 "delegate",
612 "derive",
613 "echo",
614 "else",
615 "fn",
616 "if",
617 "implement",
618 "import",
619 "let",
620 "macro",
621 "opaque",
622 "panic",
623 "pub",
624 "test",
625 "todo",
626 "type",
627 "use",
628];
629
630pub const ZIG_KEYWORDS: &[&str] = &[
632 "addrspace",
633 "align",
634 "allowzero",
635 "and",
636 "anyframe",
637 "anytype",
638 "asm",
639 "async",
640 "await",
641 "break",
642 "callconv",
643 "catch",
644 "comptime",
645 "const",
646 "continue",
647 "defer",
648 "else",
649 "enum",
650 "errdefer",
651 "error",
652 "export",
653 "extern",
654 "fn",
655 "for",
656 "if",
657 "inline",
658 "linksection",
659 "noalias",
660 "noinline",
661 "nosuspend",
662 "or",
663 "orelse",
664 "packed",
665 "pub",
666 "resume",
667 "return",
668 "struct",
669 "suspend",
670 "switch",
671 "test",
672 "threadlocal",
673 "try",
674 "union",
675 "unreachable",
676 "usingnamespace",
677 "var",
678 "volatile",
679 "while",
680];
681
682pub const RUST_KEYWORDS: &[&str] = &[
689 "as", "break", "const", "continue", "crate", "else", "enum", "extern", "false", "fn", "for", "if", "impl", "in",
691 "let", "loop", "match", "mod", "move", "mut", "pub", "ref", "return", "self", "Self", "static", "struct", "super",
692 "trait", "true", "type", "unsafe", "use", "where", "while", "async", "await", "dyn",
694 "abstract", "become", "box", "do", "final", "macro", "override", "priv", "typeof", "unsized", "virtual", "yield",
696 "try",
697];
698
699pub fn rust_raw_ident_safe(name: &str) -> Option<String> {
709 if RUST_KEYWORDS.contains(&name) {
710 Some(format!("r#{name}"))
711 } else {
712 None
713 }
714}
715
716pub fn rust_raw_ident(name: &str) -> String {
719 rust_raw_ident_safe(name).unwrap_or_else(|| name.to_string())
720}
721
722pub fn is_valid_rust_ident_chars(name: &str) -> bool {
730 if name.is_empty() {
731 return false;
732 }
733 let mut chars = name.chars();
734 let first = chars.next().expect("non-empty string has a first char");
735 if !first.is_alphabetic() && first != '_' {
736 return false;
737 }
738 chars.all(|c| c.is_alphanumeric() || c == '_')
739}
740
741pub fn python_safe_name(name: &str) -> Option<String> {
749 if PYTHON_KEYWORDS.contains(&name) {
750 Some(format!("{name}_"))
751 } else {
752 None
753 }
754}
755
756pub fn python_ident(name: &str) -> String {
759 python_safe_name(name).unwrap_or_else(|| name.to_string())
760}
761
762pub fn python_str_enum_safe_name(name: &str) -> Option<String> {
768 if PYTHON_KEYWORDS.contains(&name) || PYTHON_STR_METHODS.contains(&name) {
769 Some(format!("{name}_"))
770 } else {
771 None
772 }
773}
774
775pub fn python_str_enum_ident(name: &str) -> String {
778 python_str_enum_safe_name(name).unwrap_or_else(|| name.to_string())
779}
780
781pub fn kotlin_safe_name(name: &str) -> Option<String> {
783 if KOTLIN_KEYWORDS.contains(&name) {
784 Some(format!("{name}_"))
785 } else {
786 None
787 }
788}
789
790pub fn kotlin_ident(name: &str) -> String {
792 kotlin_safe_name(name).unwrap_or_else(|| name.to_string())
793}
794
795pub fn swift_safe_name(name: &str) -> Option<String> {
797 if SWIFT_KEYWORDS.contains(&name) {
798 Some(format!("{name}_"))
799 } else {
800 None
801 }
802}
803
804pub fn swift_ident(name: &str) -> String {
806 swift_safe_name(name).unwrap_or_else(|| name.to_string())
807}
808
809pub fn swift_case_safe_name(name: &str) -> Option<String> {
818 if SWIFT_KEYWORDS.contains(&name) {
819 Some(format!("`{name}`"))
820 } else {
821 None
822 }
823}
824
825pub fn swift_case_ident(name: &str) -> String {
832 swift_case_safe_name(name).unwrap_or_else(|| name.to_string())
833}
834
835pub fn dart_safe_name(name: &str) -> Option<String> {
837 if DART_KEYWORDS.contains(&name) {
838 Some(format!("{name}_"))
839 } else {
840 None
841 }
842}
843
844pub fn dart_ident(name: &str) -> String {
846 dart_safe_name(name).unwrap_or_else(|| name.to_string())
847}
848
849pub fn gleam_safe_name(name: &str) -> Option<String> {
851 if GLEAM_KEYWORDS.contains(&name) {
852 Some(format!("{name}_"))
853 } else {
854 None
855 }
856}
857
858pub fn gleam_ident(name: &str) -> String {
860 gleam_safe_name(name).unwrap_or_else(|| name.to_string())
861}
862
863pub fn zig_safe_name(name: &str) -> Option<String> {
865 if ZIG_KEYWORDS.contains(&name) {
866 Some(format!("{name}_"))
867 } else {
868 None
869 }
870}
871
872pub fn zig_ident(name: &str) -> String {
881 let mut sanitized = String::with_capacity(name.len() + 1);
882 for ch in name.chars() {
883 if ch.is_ascii_alphanumeric() || ch == '_' {
884 sanitized.push(ch);
885 } else {
886 sanitized.push('_');
887 }
888 }
889 if sanitized.chars().next().is_some_and(|ch| ch.is_ascii_digit()) {
890 sanitized.insert(0, '_');
891 }
892 zig_safe_name(&sanitized).unwrap_or(sanitized)
893}
894
895#[cfg(test)]
896mod tests {
897 use super::*;
898
899 #[test]
900 fn python_class_is_reserved() {
901 assert_eq!(python_safe_name("class"), Some("class_".to_string()));
902 }
903
904 #[test]
905 fn python_ordinary_name_is_none() {
906 assert_eq!(python_safe_name("layout_class"), None);
907 }
908
909 #[test]
910 fn python_ident_reserved() {
911 assert_eq!(python_ident("class"), "class_");
912 }
913
914 #[test]
915 fn python_ident_ordinary() {
916 assert_eq!(python_ident("layout_class"), "layout_class");
917 }
918
919 #[test]
920 fn kotlin_class_is_reserved() {
921 assert_eq!(kotlin_safe_name("class"), Some("class_".to_string()));
922 assert_eq!(kotlin_safe_name("fun"), Some("fun_".to_string()));
923 assert_eq!(kotlin_safe_name("ordinary"), None);
924 assert_eq!(kotlin_ident("typealias"), "typealias_");
925 }
926
927 #[test]
928 fn swift_init_is_reserved() {
929 assert_eq!(swift_safe_name("init"), Some("init_".to_string()));
930 assert_eq!(swift_safe_name("Self"), Some("Self_".to_string()));
931 assert_eq!(swift_safe_name("normal"), None);
932 assert_eq!(swift_ident("protocol"), "protocol_");
933 }
934
935 #[test]
936 fn swift_case_ident_backtick_escapes_reserved_keywords() {
937 assert_eq!(swift_case_ident("default"), "`default`");
943 assert_eq!(swift_case_ident("protocol"), "`protocol`");
944 assert_eq!(swift_case_ident("init"), "`init`");
945 assert_eq!(swift_case_ident("Self"), "`Self`");
946 assert_eq!(swift_case_ident("Any"), "`Any`");
947 assert_eq!(swift_case_ident("class"), "`class`");
948 assert_eq!(swift_case_ident("inout"), "`inout`");
949 assert_eq!(swift_case_ident("rethrows"), "`rethrows`");
950 assert_eq!(swift_case_ident("gitHub"), "gitHub");
952 assert_eq!(swift_case_ident("normal"), "normal");
953 assert_eq!(swift_case_ident("dracula"), "dracula");
954 }
955
956 #[test]
957 fn swift_case_safe_name_returns_some_for_reserved() {
958 assert_eq!(swift_case_safe_name("default"), Some("`default`".to_string()));
959 assert_eq!(swift_case_safe_name("normal"), None);
960 }
961
962 #[test]
963 fn dart_async_is_reserved() {
964 assert_eq!(dart_safe_name("async"), Some("async_".to_string()));
965 assert_eq!(dart_safe_name("late"), Some("late_".to_string()));
966 assert_eq!(dart_safe_name("normal"), None);
967 assert_eq!(dart_ident("required"), "required_");
968 }
969
970 #[test]
971 fn gleam_pub_is_reserved() {
972 assert_eq!(gleam_safe_name("pub"), Some("pub_".to_string()));
973 assert_eq!(gleam_safe_name("opaque"), Some("opaque_".to_string()));
974 assert_eq!(gleam_safe_name("normal"), None);
975 assert_eq!(gleam_ident("type"), "type_");
976 }
977
978 #[test]
979 fn zig_comptime_is_reserved() {
980 assert_eq!(zig_safe_name("comptime"), Some("comptime_".to_string()));
981 assert_eq!(zig_safe_name("errdefer"), Some("errdefer_".to_string()));
982 assert_eq!(zig_safe_name("normal"), None);
983 assert_eq!(zig_ident("usingnamespace"), "usingnamespace_");
984 }
985
986 #[test]
987 fn python_keywords_covers_common_cases() {
988 for kw in &[
989 "def", "return", "yield", "pass", "import", "from", "type", "None", "True", "False",
990 ] {
991 assert!(
992 python_safe_name(kw).is_some(),
993 "expected {kw:?} to be a Python reserved keyword"
994 );
995 }
996 }
997
998 #[test]
999 fn python_str_enum_ident_escapes_str_methods() {
1000 assert_eq!(python_str_enum_ident("title"), "title_");
1002 assert_eq!(python_str_enum_ident("lower"), "lower_");
1003 assert_eq!(python_str_enum_ident("upper"), "upper_");
1004 assert_eq!(python_str_enum_ident("count"), "count_");
1005 assert_eq!(python_str_enum_ident("capitalize"), "capitalize_");
1006 assert_eq!(python_str_enum_ident("split"), "split_");
1007 }
1008
1009 #[test]
1010 fn python_str_enum_ident_escapes_python_keywords() {
1011 assert_eq!(python_str_enum_ident("del"), "del_");
1013 assert_eq!(python_str_enum_ident("class"), "class_");
1014 assert_eq!(python_str_enum_ident("return"), "return_");
1015 }
1016
1017 #[test]
1018 fn python_str_enum_ident_passes_through_ordinary_names() {
1019 assert_eq!(python_str_enum_ident("body"), "body");
1021 assert_eq!(python_str_enum_ident("div"), "div");
1022 assert_eq!(python_str_enum_ident("paragraph"), "paragraph");
1023 }
1024
1025 #[test]
1026 fn python_str_enum_safe_name_returns_some_for_reserved() {
1027 assert_eq!(python_str_enum_safe_name("title"), Some("title_".to_string()));
1028 assert_eq!(python_str_enum_safe_name("del"), Some("del_".to_string()));
1029 }
1030
1031 #[test]
1032 fn python_str_enum_safe_name_returns_none_for_ordinary() {
1033 assert_eq!(python_str_enum_safe_name("body"), None);
1034 assert_eq!(python_str_enum_safe_name("content"), None);
1035 }
1036
1037 #[test]
1038 fn rust_raw_ident_escapes_rust_keywords() {
1039 assert_eq!(rust_raw_ident("type"), "r#type");
1040 assert_eq!(rust_raw_ident("match"), "r#match");
1041 assert_eq!(rust_raw_ident("fn"), "r#fn");
1042 assert_eq!(rust_raw_ident("loop"), "r#loop");
1043 assert_eq!(rust_raw_ident("struct"), "r#struct");
1044 assert_eq!(rust_raw_ident("move"), "r#move");
1045 assert_eq!(rust_raw_ident("ref"), "r#ref");
1046 assert_eq!(rust_raw_ident("async"), "r#async");
1047 }
1048
1049 #[test]
1050 fn rust_raw_ident_passes_through_ordinary_names() {
1051 assert_eq!(rust_raw_ident("content"), "content");
1052 assert_eq!(rust_raw_ident("item_type"), "item_type");
1053 assert_eq!(rust_raw_ident("model"), "model");
1054 }
1055
1056 #[test]
1057 fn rust_raw_ident_safe_returns_some_for_keywords() {
1058 assert_eq!(rust_raw_ident_safe("type"), Some("r#type".to_string()));
1059 assert_eq!(rust_raw_ident_safe("fn"), Some("r#fn".to_string()));
1060 }
1061
1062 #[test]
1063 fn rust_raw_ident_safe_returns_none_for_ordinary() {
1064 assert_eq!(rust_raw_ident_safe("content"), None);
1065 assert_eq!(rust_raw_ident_safe("model"), None);
1066 }
1067
1068 #[test]
1069 fn is_valid_rust_ident_chars_accepts_valid_identifiers() {
1070 assert!(is_valid_rust_ident_chars("content"));
1071 assert!(is_valid_rust_ident_chars("self_harm"));
1072 assert!(is_valid_rust_ident_chars("_private"));
1073 assert!(is_valid_rust_ident_chars("type")); assert!(is_valid_rust_ident_chars("CamelCase"));
1075 }
1076
1077 #[test]
1078 fn is_valid_rust_ident_chars_rejects_invalid_identifiers() {
1079 assert!(!is_valid_rust_ident_chars("self-harm")); assert!(!is_valid_rust_ident_chars("self-harm/intent")); assert!(!is_valid_rust_ident_chars("sexual/minors")); assert!(!is_valid_rust_ident_chars("")); assert!(!is_valid_rust_ident_chars("123abc")); }
1085}