1use jmespath::Runtime;
61use std::collections::{HashMap, HashSet};
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
65pub enum Category {
66 Standard,
68 String,
69 Array,
70 Object,
71 Math,
72 Type,
73 Utility,
74 Validation,
75 Path,
76 Expression,
77 Text,
78 Hash,
79 Encoding,
80 Regex,
81 Url,
82 Uuid,
83 Rand,
84 Datetime,
85 Fuzzy,
86 Phonetic,
87 Geo,
88 Semver,
89 Network,
90 Ids,
91 Duration,
92 Color,
93 Computing,
94 MultiMatch,
95 Jsonpatch,
96 Format,
97 Language,
98 Discovery,
99}
100
101impl Category {
102 pub fn all() -> &'static [Category] {
104 &[
105 Category::Standard,
106 Category::String,
107 Category::Array,
108 Category::Object,
109 Category::Math,
110 Category::Type,
111 Category::Utility,
112 Category::Validation,
113 Category::Path,
114 Category::Expression,
115 Category::Text,
116 Category::Hash,
117 Category::Encoding,
118 Category::Regex,
119 Category::Url,
120 Category::Uuid,
121 Category::Rand,
122 Category::Datetime,
123 Category::Fuzzy,
124 Category::Phonetic,
125 Category::Geo,
126 Category::Semver,
127 Category::Network,
128 Category::Ids,
129 Category::Duration,
130 Category::Color,
131 Category::Computing,
132 Category::MultiMatch,
133 Category::Jsonpatch,
134 Category::Format,
135 Category::Language,
136 Category::Discovery,
137 ]
138 }
139
140 pub fn name(&self) -> &'static str {
142 match self {
143 Category::Standard => "standard",
144 Category::String => "string",
145 Category::Array => "array",
146 Category::Object => "object",
147 Category::Math => "math",
148 Category::Type => "type",
149 Category::Utility => "utility",
150 Category::Validation => "validation",
151 Category::Path => "path",
152 Category::Expression => "expression",
153 Category::Text => "text",
154 Category::Hash => "hash",
155 Category::Encoding => "encoding",
156 Category::Regex => "regex",
157 Category::Url => "url",
158 Category::Uuid => "uuid",
159 Category::Rand => "rand",
160 Category::Datetime => "datetime",
161 Category::Fuzzy => "fuzzy",
162 Category::Phonetic => "phonetic",
163 Category::Geo => "geo",
164 Category::Semver => "semver",
165 Category::Network => "network",
166 Category::Ids => "ids",
167 Category::Duration => "duration",
168 Category::Color => "color",
169 Category::Computing => "computing",
170 Category::MultiMatch => "multi-match",
171 Category::Jsonpatch => "jsonpatch",
172 Category::Format => "format",
173 Category::Language => "language",
174 Category::Discovery => "discovery",
175 }
176 }
177
178 pub fn is_available(&self) -> bool {
180 match self {
181 Category::Standard => true,
183 #[cfg(feature = "string")]
184 Category::String => true,
185 #[cfg(feature = "array")]
186 Category::Array => true,
187 #[cfg(feature = "object")]
188 Category::Object => true,
189 #[cfg(feature = "math")]
190 Category::Math => true,
191 #[cfg(feature = "type")]
192 Category::Type => true,
193 #[cfg(feature = "utility")]
194 Category::Utility => true,
195 #[cfg(feature = "validation")]
196 Category::Validation => true,
197 #[cfg(feature = "path")]
198 Category::Path => true,
199 #[cfg(feature = "expression")]
200 Category::Expression => true,
201 #[cfg(feature = "text")]
202 Category::Text => true,
203 #[cfg(feature = "hash")]
204 Category::Hash => true,
205 #[cfg(feature = "encoding")]
206 Category::Encoding => true,
207 #[cfg(feature = "regex")]
208 Category::Regex => true,
209 #[cfg(feature = "url")]
210 Category::Url => true,
211 #[cfg(feature = "uuid")]
212 Category::Uuid => true,
213 #[cfg(feature = "rand")]
214 Category::Rand => true,
215 #[cfg(feature = "datetime")]
216 Category::Datetime => true,
217 #[cfg(feature = "fuzzy")]
218 Category::Fuzzy => true,
219 #[cfg(feature = "phonetic")]
220 Category::Phonetic => true,
221 #[cfg(feature = "geo")]
222 Category::Geo => true,
223 #[cfg(feature = "semver")]
224 Category::Semver => true,
225 #[cfg(feature = "network")]
226 Category::Network => true,
227 #[cfg(feature = "ids")]
228 Category::Ids => true,
229 #[cfg(feature = "duration")]
230 Category::Duration => true,
231 #[cfg(feature = "color")]
232 Category::Color => true,
233 #[cfg(feature = "computing")]
234 Category::Computing => true,
235 #[cfg(feature = "multi-match")]
236 Category::MultiMatch => true,
237 #[cfg(feature = "jsonpatch")]
238 Category::Jsonpatch => true,
239 #[cfg(feature = "format")]
240 Category::Format => true,
241 #[cfg(feature = "language")]
242 Category::Language => true,
243 #[cfg(feature = "discovery")]
244 Category::Discovery => true,
245 #[cfg(not(feature = "string"))]
248 Category::String => false,
249 #[cfg(not(feature = "array"))]
250 Category::Array => false,
251 #[cfg(not(feature = "object"))]
252 Category::Object => false,
253 #[cfg(not(feature = "math"))]
254 Category::Math => false,
255 #[cfg(not(feature = "type"))]
256 Category::Type => false,
257 #[cfg(not(feature = "utility"))]
258 Category::Utility => false,
259 #[cfg(not(feature = "validation"))]
260 Category::Validation => false,
261 #[cfg(not(feature = "path"))]
262 Category::Path => false,
263 #[cfg(not(feature = "expression"))]
264 Category::Expression => false,
265 #[cfg(not(feature = "text"))]
266 Category::Text => false,
267 #[cfg(not(feature = "hash"))]
268 Category::Hash => false,
269 #[cfg(not(feature = "encoding"))]
270 Category::Encoding => false,
271 #[cfg(not(feature = "regex"))]
272 Category::Regex => false,
273 #[cfg(not(feature = "url"))]
274 Category::Url => false,
275 #[cfg(not(feature = "uuid"))]
276 Category::Uuid => false,
277 #[cfg(not(feature = "rand"))]
278 Category::Rand => false,
279 #[cfg(not(feature = "datetime"))]
280 Category::Datetime => false,
281 #[cfg(not(feature = "fuzzy"))]
282 Category::Fuzzy => false,
283 #[cfg(not(feature = "phonetic"))]
284 Category::Phonetic => false,
285 #[cfg(not(feature = "geo"))]
286 Category::Geo => false,
287 #[cfg(not(feature = "semver"))]
288 Category::Semver => false,
289 #[cfg(not(feature = "network"))]
290 Category::Network => false,
291 #[cfg(not(feature = "ids"))]
292 Category::Ids => false,
293 #[cfg(not(feature = "duration"))]
294 Category::Duration => false,
295 #[cfg(not(feature = "color"))]
296 Category::Color => false,
297 #[cfg(not(feature = "computing"))]
298 Category::Computing => false,
299 #[cfg(not(feature = "multi-match"))]
300 Category::MultiMatch => false,
301 #[cfg(not(feature = "jsonpatch"))]
302 Category::Jsonpatch => false,
303 #[cfg(not(feature = "format"))]
304 Category::Format => false,
305 #[cfg(not(feature = "language"))]
306 Category::Language => false,
307 #[cfg(not(feature = "discovery"))]
308 Category::Discovery => false,
309 }
310 }
311}
312
313#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
315pub enum Feature {
316 Spec,
318 Core,
320 Fp,
322 Jep,
324 #[allow(non_camel_case_types)]
326 format,
327 #[allow(non_camel_case_types)]
329 env,
330 #[allow(non_camel_case_types)]
332 discovery,
333}
334
335impl Feature {
336 pub fn all() -> &'static [Feature] {
338 &[
339 Feature::Spec,
340 Feature::Core,
341 Feature::Fp,
342 Feature::Jep,
343 Feature::format,
344 Feature::env,
345 Feature::discovery,
346 ]
347 }
348
349 pub fn name(&self) -> &'static str {
351 match self {
352 Feature::Spec => "spec",
353 Feature::Core => "core",
354 Feature::Fp => "fp",
355 Feature::Jep => "jep",
356 Feature::format => "format",
357 Feature::env => "env",
358 Feature::discovery => "discovery",
359 }
360 }
361}
362
363#[derive(Debug, Clone)]
365pub struct FunctionInfo {
366 pub name: &'static str,
368 pub category: Category,
370 pub description: &'static str,
372 pub signature: &'static str,
374 pub example: &'static str,
376 pub is_standard: bool,
378 pub jep: Option<&'static str>,
381 pub aliases: &'static [&'static str],
383 pub features: &'static [Feature],
385}
386
387#[derive(Debug, Clone)]
389pub struct FunctionRegistry {
390 registered: HashMap<&'static str, FunctionInfo>,
392 disabled: HashSet<String>,
394 categories: HashSet<Category>,
396}
397
398impl Default for FunctionRegistry {
399 fn default() -> Self {
400 Self::new()
401 }
402}
403
404impl FunctionRegistry {
405 pub fn new() -> Self {
407 Self {
408 registered: HashMap::new(),
409 disabled: HashSet::new(),
410 categories: HashSet::new(),
411 }
412 }
413
414 pub fn register_all(&mut self) -> &mut Self {
416 for category in Category::all() {
417 if category.is_available() {
418 self.register_category(*category);
419 }
420 }
421 self
422 }
423
424 pub fn register_category(&mut self, category: Category) -> &mut Self {
426 if !category.is_available() {
427 return self;
428 }
429
430 self.categories.insert(category);
431
432 for info in get_category_functions(category) {
433 self.registered.insert(info.name, info);
434 }
435 self
436 }
437
438 pub fn disable_function(&mut self, name: &str) -> &mut Self {
440 self.disabled.insert(name.to_string());
441 self
442 }
443
444 pub fn enable_function(&mut self, name: &str) -> &mut Self {
446 self.disabled.remove(name);
447 self
448 }
449
450 pub fn is_enabled(&self, name: &str) -> bool {
452 self.registered.contains_key(name) && !self.disabled.contains(name)
453 }
454
455 pub fn get_function(&self, name: &str) -> Option<&FunctionInfo> {
457 if self.disabled.contains(name) {
458 None
459 } else {
460 self.registered.get(name)
461 }
462 }
463
464 pub fn functions(&self) -> impl Iterator<Item = &FunctionInfo> {
466 self.registered
467 .values()
468 .filter(|f| !self.disabled.contains(f.name))
469 }
470
471 pub fn functions_in_category(&self, category: Category) -> impl Iterator<Item = &FunctionInfo> {
473 self.registered
474 .values()
475 .filter(move |f| f.category == category && !self.disabled.contains(f.name))
476 }
477
478 pub fn categories(&self) -> impl Iterator<Item = &Category> {
480 self.categories.iter()
481 }
482
483 pub fn len(&self) -> usize {
485 self.registered.len() - self.disabled.len()
486 }
487
488 pub fn is_empty(&self) -> bool {
490 self.len() == 0
491 }
492
493 pub fn functions_with_feature(&self, feature: Feature) -> impl Iterator<Item = &FunctionInfo> {
495 self.registered
496 .values()
497 .filter(move |f| f.features.contains(&feature) && !self.disabled.contains(f.name))
498 }
499
500 pub fn spec_function_names(&self) -> impl Iterator<Item = &'static str> + '_ {
502 self.functions_with_feature(Feature::Spec).map(|f| f.name)
503 }
504
505 pub fn is_spec_function(&self, name: &str) -> bool {
507 self.registered
508 .get(name)
509 .map(|f| f.features.contains(&Feature::Spec))
510 .unwrap_or(false)
511 }
512
513 pub fn get_function_by_name_or_alias(&self, name: &str) -> Option<&FunctionInfo> {
515 if let Some(info) = self.get_function(name) {
517 return Some(info);
518 }
519 self.registered
521 .values()
522 .find(|f| f.aliases.contains(&name) && !self.disabled.contains(f.name))
523 }
524
525 pub fn all_aliases(&self) -> impl Iterator<Item = (&'static str, &'static str)> + '_ {
527 self.registered
528 .values()
529 .flat_map(|f| f.aliases.iter().map(move |alias| (*alias, f.name)))
530 }
531
532 pub fn apply(&self, runtime: &mut Runtime) {
536 for category in &self.categories {
537 if category.is_available() {
538 self.apply_category(runtime, *category);
539 }
540 }
541 }
542
543 #[allow(unused_variables)]
544 fn apply_category(&self, runtime: &mut Runtime, category: Category) {
545 let enabled_in_category: HashSet<&str> = self
547 .functions_in_category(category)
548 .map(|f| f.name)
549 .collect();
550
551 if enabled_in_category.is_empty() {
552 return;
553 }
554
555 match category {
558 #[cfg(feature = "string")]
559 Category::String => crate::string::register_filtered(runtime, &enabled_in_category),
560 #[cfg(feature = "array")]
561 Category::Array => crate::array::register_filtered(runtime, &enabled_in_category),
562 #[cfg(feature = "object")]
563 Category::Object => crate::object::register_filtered(runtime, &enabled_in_category),
564 #[cfg(feature = "math")]
565 Category::Math => crate::math::register_filtered(runtime, &enabled_in_category),
566 #[cfg(feature = "type")]
567 Category::Type => crate::type_conv::register_filtered(runtime, &enabled_in_category),
568 #[cfg(feature = "utility")]
569 Category::Utility => crate::utility::register_filtered(runtime, &enabled_in_category),
570 #[cfg(feature = "validation")]
571 Category::Validation => {
572 crate::validation::register_filtered(runtime, &enabled_in_category)
573 }
574 #[cfg(feature = "path")]
575 Category::Path => crate::path::register_filtered(runtime, &enabled_in_category),
576 #[cfg(feature = "expression")]
577 Category::Expression => {
578 crate::expression::register_filtered(runtime, &enabled_in_category)
579 }
580 #[cfg(feature = "text")]
581 Category::Text => crate::text::register_filtered(runtime, &enabled_in_category),
582 #[cfg(feature = "hash")]
583 Category::Hash => crate::hash::register_filtered(runtime, &enabled_in_category),
584 #[cfg(feature = "encoding")]
585 Category::Encoding => crate::encoding::register_filtered(runtime, &enabled_in_category),
586 #[cfg(feature = "regex")]
587 Category::Regex => crate::regex_fns::register_filtered(runtime, &enabled_in_category),
588 #[cfg(feature = "url")]
589 Category::Url => crate::url_fns::register_filtered(runtime, &enabled_in_category),
590 #[cfg(feature = "uuid")]
591 Category::Uuid => crate::random::register_filtered(runtime, &enabled_in_category),
592 #[cfg(feature = "rand")]
593 Category::Rand => crate::random::register_filtered(runtime, &enabled_in_category),
594 #[cfg(feature = "datetime")]
595 Category::Datetime => crate::datetime::register_filtered(runtime, &enabled_in_category),
596 #[cfg(feature = "fuzzy")]
597 Category::Fuzzy => crate::fuzzy::register_filtered(runtime, &enabled_in_category),
598 #[cfg(feature = "phonetic")]
599 Category::Phonetic => crate::phonetic::register_filtered(runtime, &enabled_in_category),
600 #[cfg(feature = "geo")]
601 Category::Geo => crate::geo::register_filtered(runtime, &enabled_in_category),
602 #[cfg(feature = "semver")]
603 Category::Semver => crate::semver_fns::register_filtered(runtime, &enabled_in_category),
604 #[cfg(feature = "network")]
605 Category::Network => crate::network::register_filtered(runtime, &enabled_in_category),
606 #[cfg(feature = "ids")]
607 Category::Ids => crate::ids::register_filtered(runtime, &enabled_in_category),
608 #[cfg(feature = "duration")]
609 Category::Duration => crate::duration::register_filtered(runtime, &enabled_in_category),
610 #[cfg(feature = "color")]
611 Category::Color => crate::color::register_filtered(runtime, &enabled_in_category),
612 #[cfg(feature = "computing")]
613 Category::Computing => {
614 crate::computing::register_filtered(runtime, &enabled_in_category)
615 }
616 #[cfg(feature = "multi-match")]
617 Category::MultiMatch => {
618 crate::multi_match::register_filtered(runtime, &enabled_in_category)
619 }
620 #[cfg(feature = "jsonpatch")]
621 Category::Jsonpatch => {
622 crate::jsonpatch::register_filtered(runtime, &enabled_in_category)
623 }
624 #[cfg(feature = "format")]
625 Category::Format => crate::format::register_filtered(runtime, &enabled_in_category),
626 #[cfg(feature = "language")]
627 Category::Language => crate::language::register_filtered(runtime, &enabled_in_category),
628 #[cfg(feature = "discovery")]
629 Category::Discovery => {
630 crate::discovery::register_filtered(runtime, &enabled_in_category)
631 }
632 Category::Standard => {} #[cfg(not(feature = "string"))]
636 Category::String => {}
637 #[cfg(not(feature = "array"))]
638 Category::Array => {}
639 #[cfg(not(feature = "object"))]
640 Category::Object => {}
641 #[cfg(not(feature = "math"))]
642 Category::Math => {}
643 #[cfg(not(feature = "type"))]
644 Category::Type => {}
645 #[cfg(not(feature = "utility"))]
646 Category::Utility => {}
647 #[cfg(not(feature = "validation"))]
648 Category::Validation => {}
649 #[cfg(not(feature = "path"))]
650 Category::Path => {}
651 #[cfg(not(feature = "expression"))]
652 Category::Expression => {}
653 #[cfg(not(feature = "text"))]
654 Category::Text => {}
655 #[cfg(not(feature = "hash"))]
656 Category::Hash => {}
657 #[cfg(not(feature = "encoding"))]
658 Category::Encoding => {}
659 #[cfg(not(feature = "regex"))]
660 Category::Regex => {}
661 #[cfg(not(feature = "url"))]
662 Category::Url => {}
663 #[cfg(not(feature = "uuid"))]
664 Category::Uuid => {}
665 #[cfg(not(feature = "rand"))]
666 Category::Rand => {}
667 #[cfg(not(feature = "datetime"))]
668 Category::Datetime => {}
669 #[cfg(not(feature = "fuzzy"))]
670 Category::Fuzzy => {}
671 #[cfg(not(feature = "phonetic"))]
672 Category::Phonetic => {}
673 #[cfg(not(feature = "geo"))]
674 Category::Geo => {}
675 #[cfg(not(feature = "semver"))]
676 Category::Semver => {}
677 #[cfg(not(feature = "network"))]
678 Category::Network => {}
679 #[cfg(not(feature = "ids"))]
680 Category::Ids => {}
681 #[cfg(not(feature = "duration"))]
682 Category::Duration => {}
683 #[cfg(not(feature = "color"))]
684 Category::Color => {}
685 #[cfg(not(feature = "computing"))]
686 Category::Computing => {}
687 #[cfg(not(feature = "multi-match"))]
688 Category::MultiMatch => {}
689 #[cfg(not(feature = "jsonpatch"))]
690 Category::Jsonpatch => {}
691 #[cfg(not(feature = "format"))]
692 Category::Format => {}
693 #[cfg(not(feature = "language"))]
694 Category::Language => {}
695 #[cfg(not(feature = "discovery"))]
696 Category::Discovery => {}
697 }
698 }
699}
700
701fn get_category_functions(category: Category) -> Vec<FunctionInfo> {
703 generated::FUNCTIONS
704 .iter()
705 .filter(|f| f.category == category)
706 .cloned()
707 .collect()
708}
709
710mod generated {
712 include!(concat!(env!("OUT_DIR"), "/registry_data.rs"));
713}
714
715pub struct SynonymEntry {
721 pub term: &'static str,
723 pub targets: &'static [&'static str],
725}
726
727pub fn get_synonyms() -> &'static [SynonymEntry] {
744 static SYNONYMS: &[SynonymEntry] = &[
745 SynonymEntry {
747 term: "aggregate",
748 targets: &[
749 "group_by",
750 "group_by_expr",
751 "sum",
752 "avg",
753 "count",
754 "reduce",
755 "fold",
756 ],
757 },
758 SynonymEntry {
759 term: "group",
760 targets: &["group_by", "group_by_expr", "chunk", "partition"],
761 },
762 SynonymEntry {
763 term: "collect",
764 targets: &["group_by", "group_by_expr", "flatten", "merge"],
765 },
766 SynonymEntry {
767 term: "bucket",
768 targets: &["group_by", "group_by_expr", "chunk"],
769 },
770 SynonymEntry {
772 term: "count",
773 targets: &["length", "count_by", "count_if", "size"],
774 },
775 SynonymEntry {
776 term: "size",
777 targets: &["length", "count_by"],
778 },
779 SynonymEntry {
780 term: "len",
781 targets: &["length"],
782 },
783 SynonymEntry {
785 term: "concat",
786 targets: &["join", "merge", "combine"],
787 },
788 SynonymEntry {
789 term: "combine",
790 targets: &["join", "merge", "concat"],
791 },
792 SynonymEntry {
793 term: "substring",
794 targets: &["slice", "substr", "mid"],
795 },
796 SynonymEntry {
797 term: "cut",
798 targets: &["slice", "split", "trim"],
799 },
800 SynonymEntry {
801 term: "strip",
802 targets: &["trim", "trim_left", "trim_right"],
803 },
804 SynonymEntry {
805 term: "lowercase",
806 targets: &["lower", "to_lower", "downcase"],
807 },
808 SynonymEntry {
809 term: "uppercase",
810 targets: &["upper", "to_upper", "upcase"],
811 },
812 SynonymEntry {
813 term: "replace",
814 targets: &["substitute", "gsub", "regex_replace"],
815 },
816 SynonymEntry {
817 term: "find",
818 targets: &["contains", "index_of", "search", "match"],
819 },
820 SynonymEntry {
821 term: "search",
822 targets: &["contains", "find", "index_of", "regex_match"],
823 },
824 SynonymEntry {
826 term: "filter",
827 targets: &["select", "where", "find_all", "keep"],
828 },
829 SynonymEntry {
830 term: "select",
831 targets: &["filter", "map", "pluck"],
832 },
833 SynonymEntry {
834 term: "transform",
835 targets: &["map", "transform_values", "map_values"],
836 },
837 SynonymEntry {
838 term: "first",
839 targets: &["head", "take", "front"],
840 },
841 SynonymEntry {
842 term: "last",
843 targets: &["tail", "end", "back"],
844 },
845 SynonymEntry {
846 term: "remove",
847 targets: &["reject", "delete", "drop", "exclude"],
848 },
849 SynonymEntry {
850 term: "unique",
851 targets: &["distinct", "uniq", "dedupe", "deduplicate"],
852 },
853 SynonymEntry {
854 term: "dedupe",
855 targets: &["unique", "distinct", "uniq"],
856 },
857 SynonymEntry {
858 term: "shuffle",
859 targets: &["random", "randomize", "permute"],
860 },
861 SynonymEntry {
863 term: "order",
864 targets: &["sort", "sort_by", "order_by", "arrange"],
865 },
866 SynonymEntry {
867 term: "arrange",
868 targets: &["sort", "sort_by", "order_by"],
869 },
870 SynonymEntry {
871 term: "rank",
872 targets: &["sort", "sort_by", "order_by"],
873 },
874 SynonymEntry {
876 term: "average",
877 targets: &["avg", "mean", "arithmetic_mean"],
878 },
879 SynonymEntry {
880 term: "mean",
881 targets: &["avg", "average"],
882 },
883 SynonymEntry {
884 term: "total",
885 targets: &["sum", "add", "accumulate"],
886 },
887 SynonymEntry {
888 term: "add",
889 targets: &["sum", "plus", "addition"],
890 },
891 SynonymEntry {
892 term: "subtract",
893 targets: &["minus", "difference"],
894 },
895 SynonymEntry {
896 term: "multiply",
897 targets: &["times", "product", "mul"],
898 },
899 SynonymEntry {
900 term: "divide",
901 targets: &["quotient", "div"],
902 },
903 SynonymEntry {
904 term: "remainder",
905 targets: &["mod", "modulo", "modulus"],
906 },
907 SynonymEntry {
908 term: "power",
909 targets: &["pow", "exponent", "exp"],
910 },
911 SynonymEntry {
912 term: "absolute",
913 targets: &["abs", "magnitude"],
914 },
915 SynonymEntry {
916 term: "round",
917 targets: &["round", "round_to", "nearest"],
918 },
919 SynonymEntry {
920 term: "random",
921 targets: &["rand", "random_int", "random_float"],
922 },
923 SynonymEntry {
925 term: "date",
926 targets: &["now", "today", "parse_date", "format_date", "datetime"],
927 },
928 SynonymEntry {
929 term: "time",
930 targets: &["now", "time_now", "parse_time", "datetime"],
931 },
932 SynonymEntry {
933 term: "timestamp",
934 targets: &["now", "epoch", "unix_time", "to_epoch"],
935 },
936 SynonymEntry {
937 term: "format",
938 targets: &["format_date", "strftime", "date_format"],
939 },
940 SynonymEntry {
941 term: "parse",
942 targets: &["parse_date", "parse_time", "strptime", "from_string"],
943 },
944 SynonymEntry {
946 term: "convert",
947 targets: &["to_string", "to_number", "to_array", "type"],
948 },
949 SynonymEntry {
950 term: "cast",
951 targets: &["to_string", "to_number", "to_bool"],
952 },
953 SynonymEntry {
954 term: "stringify",
955 targets: &["to_string", "string", "str"],
956 },
957 SynonymEntry {
958 term: "numberify",
959 targets: &["to_number", "number", "int", "float"],
960 },
961 SynonymEntry {
963 term: "object",
964 targets: &["from_items", "to_object", "merge", "object_from_items"],
965 },
966 SynonymEntry {
967 term: "dict",
968 targets: &["from_items", "to_object", "object"],
969 },
970 SynonymEntry {
971 term: "hash",
972 targets: &["md5", "sha256", "sha1", "crc32"],
973 },
974 SynonymEntry {
975 term: "encrypt",
976 targets: &["md5", "sha256", "sha1", "hmac"],
977 },
978 SynonymEntry {
979 term: "checksum",
980 targets: &["md5", "sha256", "crc32"],
981 },
982 SynonymEntry {
984 term: "encode",
985 targets: &["base64_encode", "url_encode", "hex_encode"],
986 },
987 SynonymEntry {
988 term: "decode",
989 targets: &["base64_decode", "url_decode", "hex_decode"],
990 },
991 SynonymEntry {
992 term: "escape",
993 targets: &["url_encode", "html_escape"],
994 },
995 SynonymEntry {
996 term: "unescape",
997 targets: &["url_decode", "html_unescape"],
998 },
999 SynonymEntry {
1001 term: "check",
1002 targets: &[
1003 "is_string",
1004 "is_number",
1005 "is_array",
1006 "is_object",
1007 "validate",
1008 ],
1009 },
1010 SynonymEntry {
1011 term: "validate",
1012 targets: &["is_email", "is_url", "is_uuid", "is_valid"],
1013 },
1014 SynonymEntry {
1015 term: "test",
1016 targets: &["regex_match", "contains", "starts_with", "ends_with"],
1017 },
1018 SynonymEntry {
1020 term: "default",
1021 targets: &["coalesce", "if_null", "or_else", "default_value"],
1022 },
1023 SynonymEntry {
1024 term: "empty",
1025 targets: &["is_empty", "blank", "null"],
1026 },
1027 SynonymEntry {
1028 term: "null",
1029 targets: &["is_null", "coalesce", "not_null"],
1030 },
1031 SynonymEntry {
1032 term: "fallback",
1033 targets: &["coalesce", "default", "or_else"],
1034 },
1035 SynonymEntry {
1037 term: "equal",
1038 targets: &["eq", "equals", "same"],
1039 },
1040 SynonymEntry {
1041 term: "compare",
1042 targets: &["eq", "lt", "gt", "lte", "gte", "cmp"],
1043 },
1044 SynonymEntry {
1045 term: "between",
1046 targets: &["range", "in_range", "clamp"],
1047 },
1048 SynonymEntry {
1050 term: "copy",
1051 targets: &["clone", "dup", "duplicate"],
1052 },
1053 SynonymEntry {
1054 term: "debug",
1055 targets: &["debug", "inspect", "dump", "print"],
1056 },
1057 SynonymEntry {
1058 term: "reverse",
1059 targets: &["reverse", "flip", "invert"],
1060 },
1061 SynonymEntry {
1062 term: "repeat",
1063 targets: &["repeat", "replicate", "times"],
1064 },
1065 SynonymEntry {
1066 term: "uuid",
1067 targets: &["uuid", "uuid4", "guid", "generate_uuid"],
1068 },
1069 SynonymEntry {
1070 term: "id",
1071 targets: &["uuid", "nanoid", "ulid", "unique_id"],
1072 },
1073 ];
1074 SYNONYMS
1075}
1076
1077pub fn lookup_synonyms(term: &str) -> Option<&'static [&'static str]> {
1092 let term_lower = term.to_lowercase();
1093 get_synonyms()
1094 .iter()
1095 .find(|s| s.term == term_lower)
1096 .map(|s| s.targets)
1097}
1098
1099pub fn expand_search_terms(query: &str) -> Vec<String> {
1114 let mut expanded = Vec::new();
1115
1116 for word in query.split_whitespace() {
1117 let word_lower = word.to_lowercase();
1118 expanded.push(word_lower.clone());
1119
1120 if let Some(targets) = lookup_synonyms(&word_lower) {
1121 for target in targets {
1122 let target_str = (*target).to_string();
1123 if !expanded.contains(&target_str) {
1124 expanded.push(target_str);
1125 }
1126 }
1127 }
1128 }
1129
1130 expanded
1131}