1use anyhow::{anyhow, Result};
2use std::collections::HashMap;
3use std::fmt;
4use std::sync::Arc;
5
6use crate::data::datatable::DataValue;
7
8pub mod astronomy;
9pub mod case_convert;
10pub mod chemistry;
11pub mod comparison;
12pub mod constants;
13pub mod convert;
14pub mod date_time;
15pub mod financial;
16pub mod format;
17pub mod format_number;
18pub mod geometry;
19pub mod group_num;
20pub mod hash;
21pub mod math;
22pub mod mathematics;
23pub mod number_words;
24pub mod particle_charges;
25pub mod physics;
26pub mod random;
27pub mod roman;
28pub mod solar_system;
29pub mod statistics;
30pub mod string_fun;
31pub mod string_methods;
32pub mod string_utils;
33pub mod text_processing;
34pub mod type_checking;
35
36pub use string_methods::MethodFunction;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
41pub enum FunctionCategory {
42 Constant, Mathematical, Statistical, Astronomical, Chemical, Date, String, Aggregate, Conversion, }
52
53impl fmt::Display for FunctionCategory {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 FunctionCategory::Constant => write!(f, "Constant"),
57 FunctionCategory::Mathematical => write!(f, "Mathematical"),
58 FunctionCategory::Statistical => write!(f, "Statistical"),
59 FunctionCategory::Astronomical => write!(f, "Astronomical"),
60 FunctionCategory::Chemical => write!(f, "Chemical"),
61 FunctionCategory::Date => write!(f, "Date"),
62 FunctionCategory::String => write!(f, "String"),
63 FunctionCategory::Aggregate => write!(f, "Aggregate"),
64 FunctionCategory::Conversion => write!(f, "Conversion"),
65 }
66 }
67}
68
69#[derive(Debug, Clone)]
71pub enum ArgCount {
72 Fixed(usize),
74 Range(usize, usize),
76 Variadic,
78}
79
80impl ArgCount {
81 #[must_use]
82 pub fn is_valid(&self, count: usize) -> bool {
83 match self {
84 ArgCount::Fixed(n) => count == *n,
85 ArgCount::Range(min, max) => count >= *min && count <= *max,
86 ArgCount::Variadic => true,
87 }
88 }
89
90 #[must_use]
91 pub fn description(&self) -> String {
92 match self {
93 ArgCount::Fixed(0) => "no arguments".to_string(),
94 ArgCount::Fixed(1) => "1 argument".to_string(),
95 ArgCount::Fixed(n) => format!("{n} arguments"),
96 ArgCount::Range(min, max) => format!("{min} to {max} arguments"),
97 ArgCount::Variadic => "any number of arguments".to_string(),
98 }
99 }
100}
101
102#[derive(Debug, Clone)]
104pub struct FunctionSignature {
105 pub name: &'static str,
106 pub category: FunctionCategory,
107 pub arg_count: ArgCount,
108 pub description: &'static str,
109 pub returns: &'static str,
110 pub examples: Vec<&'static str>,
111}
112
113pub trait SqlFunction: Send + Sync {
115 fn signature(&self) -> FunctionSignature;
117
118 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue>;
120
121 fn validate_args(&self, args: &[DataValue]) -> Result<()> {
123 let sig = self.signature();
124 if !sig.arg_count.is_valid(args.len()) {
125 return Err(anyhow!(
126 "{}() expects {}, got {}",
127 sig.name,
128 sig.arg_count.description(),
129 args.len()
130 ));
131 }
132 Ok(())
133 }
134}
135
136pub struct FunctionRegistry {
138 functions: HashMap<String, Box<dyn SqlFunction>>,
139 by_category: HashMap<FunctionCategory, Vec<String>>,
140 methods: HashMap<String, Arc<dyn MethodFunction>>,
141}
142
143impl FunctionRegistry {
144 #[must_use]
146 pub fn new() -> Self {
147 let mut registry = Self {
148 functions: HashMap::new(),
149 by_category: HashMap::new(),
150 methods: HashMap::new(),
151 };
152
153 registry.register_constants();
155 registry.register_astronomical_functions();
156 registry.register_chemical_functions();
157 registry.register_mathematical_functions();
158 registry.register_statistical_functions();
159 registry.register_geometry_functions();
160 registry.register_physics_functions();
161 registry.register_date_time_functions();
162 registry.register_string_methods();
163 registry.register_financial_functions();
164 registry.register_conversion_functions();
165 registry.register_hash_functions();
166 registry.register_comparison_functions();
167 registry.register_aggregate_functions();
168 registry.register_random_functions();
169 registry.register_format_functions();
170 registry.register_type_checking_functions();
171
172 registry
173 }
174
175 pub fn register(&mut self, func: Box<dyn SqlFunction>) {
177 let sig = func.signature();
178 let name = sig.name.to_uppercase();
179 let category = sig.category;
180
181 self.functions.insert(name.clone(), func);
183
184 self.by_category.entry(category).or_default().push(name);
186 }
187
188 #[must_use]
190 pub fn get(&self, name: &str) -> Option<&dyn SqlFunction> {
191 self.functions
192 .get(&name.to_uppercase())
193 .map(std::convert::AsRef::as_ref)
194 }
195
196 #[must_use]
198 pub fn contains(&self, name: &str) -> bool {
199 self.functions.contains_key(&name.to_uppercase())
200 }
201
202 #[must_use]
204 pub fn autocomplete(&self, prefix: &str) -> Vec<FunctionSignature> {
205 let prefix_upper = prefix.to_uppercase();
206 self.functions
207 .iter()
208 .filter(|(name, _)| name.starts_with(&prefix_upper))
209 .map(|(_, func)| func.signature())
210 .collect()
211 }
212
213 #[must_use]
215 pub fn get_by_category(&self, category: FunctionCategory) -> Vec<FunctionSignature> {
216 self.by_category
217 .get(&category)
218 .map(|names| {
219 names
220 .iter()
221 .filter_map(|name| self.functions.get(name))
222 .map(|func| func.signature())
223 .collect()
224 })
225 .unwrap_or_default()
226 }
227
228 #[must_use]
230 pub fn all_functions(&self) -> Vec<FunctionSignature> {
231 self.functions
232 .values()
233 .map(|func| func.signature())
234 .collect()
235 }
236
237 pub fn register_method(&mut self, method: Arc<dyn MethodFunction>) {
239 let method_name = method.method_name().to_uppercase();
240 self.methods.insert(method_name, method);
241 }
242
243 #[must_use]
245 pub fn get_method(&self, name: &str) -> Option<Arc<dyn MethodFunction>> {
246 if let Some(method) = self.methods.get(&name.to_uppercase()) {
248 return Some(Arc::clone(method));
249 }
250
251 for method in self.methods.values() {
253 if method.handles_method(name) {
254 return Some(Arc::clone(method));
255 }
256 }
257
258 None
259 }
260
261 #[must_use]
263 pub fn has_method(&self, name: &str) -> bool {
264 self.get_method(name).is_some()
265 }
266
267 #[must_use]
269 pub fn generate_markdown_docs(&self) -> String {
270 use std::fmt::Write;
271 let mut doc = String::new();
272
273 writeln!(&mut doc, "# SQL CLI Function Reference\n").unwrap();
274 writeln!(
275 &mut doc,
276 "This document is auto-generated from the function registry.\n"
277 )
278 .unwrap();
279
280 let mut categories: Vec<FunctionCategory> = self.by_category.keys().copied().collect();
282 categories.sort_by_key(|c| format!("{c:?}"));
283
284 for category in categories {
285 let functions = self.get_by_category(category);
286 if functions.is_empty() {
287 continue;
288 }
289
290 writeln!(&mut doc, "## {category} Functions\n").unwrap();
291
292 let mut functions = functions;
294 functions.sort_by_key(|f| f.name);
295
296 for func in functions {
297 writeln!(&mut doc, "### {}()\n", func.name).unwrap();
298 writeln!(&mut doc, "**Description:** {}\n", func.description).unwrap();
299 writeln!(
300 &mut doc,
301 "**Arguments:** {}\n",
302 func.arg_count.description()
303 )
304 .unwrap();
305 writeln!(&mut doc, "**Returns:** {}\n", func.returns).unwrap();
306
307 if !func.examples.is_empty() {
308 writeln!(&mut doc, "**Examples:**").unwrap();
309 writeln!(&mut doc, "```sql").unwrap();
310 for example in &func.examples {
311 writeln!(&mut doc, "{example}").unwrap();
312 }
313 writeln!(&mut doc, "```\n").unwrap();
314 }
315 }
316 }
317
318 doc
319 }
320
321 #[must_use]
323 pub fn generate_function_help(&self, name: &str) -> Option<String> {
324 self.get(name).map(|func| {
325 let sig = func.signature();
326 let mut help = String::new();
327 use std::fmt::Write;
328
329 writeln!(&mut help, "Function: {}()", sig.name).unwrap();
330 writeln!(&mut help, "Category: {}", sig.category).unwrap();
331 writeln!(&mut help, "Description: {}", sig.description).unwrap();
332 writeln!(&mut help, "Arguments: {}", sig.arg_count.description()).unwrap();
333 writeln!(&mut help, "Returns: {}", sig.returns).unwrap();
334
335 if !sig.examples.is_empty() {
336 writeln!(&mut help, "\nExamples:").unwrap();
337 for example in &sig.examples {
338 writeln!(&mut help, " {example}").unwrap();
339 }
340 }
341
342 help
343 })
344 }
345
346 #[must_use]
348 pub fn list_functions(&self) -> String {
349 use std::fmt::Write;
350 let mut list = String::new();
351
352 writeln!(&mut list, "Available SQL Functions:\n").unwrap();
353
354 let mut categories: Vec<FunctionCategory> = self.by_category.keys().copied().collect();
355 categories.sort_by_key(|c| format!("{c:?}"));
356
357 for category in categories {
358 let functions = self.get_by_category(category);
359 if functions.is_empty() {
360 continue;
361 }
362
363 writeln!(&mut list, "{category} Functions:").unwrap();
364
365 let mut functions = functions;
366 functions.sort_by_key(|f| f.name);
367
368 for func in functions {
369 writeln!(
370 &mut list,
371 " {:20} - {}",
372 format!("{}()", func.name),
373 func.description
374 )
375 .unwrap();
376 }
377 writeln!(&mut list).unwrap();
378 }
379
380 list
381 }
382
383 fn register_constants(&mut self) {
385 use constants::{
386 EFunction, HbarFunction, MassElectronFunction, MeFunction, PhiFunction, PiFunction,
387 TauFunction,
388 };
389
390 self.register(Box::new(PiFunction));
391 self.register(Box::new(EFunction));
392 self.register(Box::new(MeFunction)); self.register(Box::new(MassElectronFunction)); self.register(Box::new(TauFunction));
395 self.register(Box::new(PhiFunction));
396 self.register(Box::new(HbarFunction));
397 }
398
399 fn register_astronomical_functions(&mut self) {
401 use astronomy::{
402 AuFunction, DistJupiterFunction, DistMarsFunction, DistMercuryFunction,
403 DistNeptuneFunction, DistSaturnFunction, DistUranusFunction, DistVenusFunction,
404 LightYearFunction, MassEarthFunction, MassJupiterFunction, MassMarsFunction,
405 MassMercuryFunction, MassMoonFunction, MassNeptuneFunction, MassSaturnFunction,
406 MassSunFunction, MassUranusFunction, MassVenusFunction, ParsecFunction,
407 RadiusEarthFunction, RadiusJupiterFunction, RadiusMarsFunction, RadiusMercuryFunction,
408 RadiusMoonFunction, RadiusNeptuneFunction, RadiusSaturnFunction, RadiusSunFunction,
409 RadiusUranusFunction, RadiusVenusFunction,
410 };
411
412 use solar_system::{
413 DensitySolarBodyFunction, DistanceSolarBodyFunction, EscapeVelocitySolarBodyFunction,
414 GravitySolarBodyFunction, MassSolarBodyFunction, MoonsSolarBodyFunction,
415 OrbitalPeriodSolarBodyFunction, RadiusSolarBodyFunction,
416 RotationPeriodSolarBodyFunction,
417 };
418
419 self.register(Box::new(MassEarthFunction));
420 self.register(Box::new(MassSunFunction));
421 self.register(Box::new(MassMoonFunction));
422 self.register(Box::new(AuFunction)); self.register(Box::new(LightYearFunction));
424 self.register(Box::new(ParsecFunction));
425
426 self.register(Box::new(MassMercuryFunction));
428 self.register(Box::new(MassVenusFunction));
429 self.register(Box::new(MassMarsFunction));
430 self.register(Box::new(MassJupiterFunction));
431 self.register(Box::new(MassSaturnFunction));
432 self.register(Box::new(MassUranusFunction));
433 self.register(Box::new(MassNeptuneFunction));
434
435 self.register(Box::new(RadiusSunFunction));
437 self.register(Box::new(RadiusEarthFunction));
438 self.register(Box::new(RadiusMoonFunction));
439 self.register(Box::new(RadiusMercuryFunction));
440 self.register(Box::new(RadiusVenusFunction));
441 self.register(Box::new(RadiusMarsFunction));
442 self.register(Box::new(RadiusJupiterFunction));
443 self.register(Box::new(RadiusSaturnFunction));
444 self.register(Box::new(RadiusUranusFunction));
445 self.register(Box::new(RadiusNeptuneFunction));
446
447 self.register(Box::new(DistMercuryFunction));
449 self.register(Box::new(DistVenusFunction));
450 self.register(Box::new(DistMarsFunction));
451 self.register(Box::new(DistJupiterFunction));
452 self.register(Box::new(DistSaturnFunction));
453 self.register(Box::new(DistUranusFunction));
454 self.register(Box::new(DistNeptuneFunction));
455
456 self.register(Box::new(MassSolarBodyFunction));
458 self.register(Box::new(RadiusSolarBodyFunction));
459 self.register(Box::new(DistanceSolarBodyFunction));
460 self.register(Box::new(OrbitalPeriodSolarBodyFunction));
461 self.register(Box::new(GravitySolarBodyFunction));
462 self.register(Box::new(DensitySolarBodyFunction));
463 self.register(Box::new(EscapeVelocitySolarBodyFunction));
464 self.register(Box::new(RotationPeriodSolarBodyFunction));
465 self.register(Box::new(MoonsSolarBodyFunction));
466 }
467
468 fn register_chemical_functions(&mut self) {
470 use chemistry::{
471 AtomicMassFunction, AtomicNumberFunction, AvogadroFunction, MoleculeFormulaFunction,
472 NeutronsFunction,
473 };
474
475 self.register(Box::new(AvogadroFunction));
476 self.register(Box::new(AtomicMassFunction));
477 self.register(Box::new(AtomicNumberFunction));
478 self.register(Box::new(NeutronsFunction));
479 self.register(Box::new(MoleculeFormulaFunction));
480 }
481
482 fn register_string_methods(&mut self) {
484 use case_convert::{
485 ToCamelCaseFunction, ToConstantCaseFunction, ToKebabCaseFunction, ToPascalCaseFunction,
486 ToSnakeCaseFunction,
487 };
488 use number_words::{ToOrdinal, ToOrdinalWords, ToWords};
489 use string_fun::{
490 InitCapFunction, MorseCodeFunction, PigLatinFunction, ProperFunction, ReverseFunction,
491 Rot13Function, ScrambleFunction, SoundexFunction,
492 };
493 use string_utils::{LPadFunction, RPadFunction, RepeatFunction};
494 use text_processing::{CleanText, ExtractWords, StripPunctuation, Tokenize, WordCount};
495
496 string_methods::register_string_methods(self);
497
498 self.register(Box::new(RepeatFunction));
500 self.register(Box::new(LPadFunction));
501 self.register(Box::new(RPadFunction));
502
503 self.register(Box::new(ToSnakeCaseFunction));
505 self.register(Box::new(ToCamelCaseFunction));
506 self.register(Box::new(ToPascalCaseFunction));
507 self.register(Box::new(ToKebabCaseFunction));
508 self.register(Box::new(ToConstantCaseFunction));
509
510 self.register(Box::new(ReverseFunction));
512 self.register(Box::new(InitCapFunction));
513 self.register(Box::new(ProperFunction));
514 self.register(Box::new(Rot13Function));
515 self.register(Box::new(SoundexFunction));
516 self.register(Box::new(PigLatinFunction));
517 self.register(Box::new(MorseCodeFunction));
518 self.register(Box::new(ScrambleFunction));
519
520 self.register(Box::new(ToWords));
522 self.register(Box::new(ToOrdinal));
523 self.register(Box::new(ToOrdinalWords));
524
525 self.register(Box::new(StripPunctuation));
527 self.register(Box::new(Tokenize));
528 self.register(Box::new(CleanText));
529 self.register(Box::new(ExtractWords));
530 self.register(Box::new(WordCount));
531 }
532
533 fn register_geometry_functions(&mut self) {
535 use geometry::{
536 CircleAreaFunction, CircleCircumferenceFunction, Distance2DFunction,
537 PythagorasFunction, SphereSurfaceAreaFunction, SphereVolumeFunction,
538 TriangleAreaFunction,
539 };
540
541 self.register(Box::new(PythagorasFunction));
542 self.register(Box::new(CircleAreaFunction));
543 self.register(Box::new(CircleCircumferenceFunction));
544 self.register(Box::new(SphereVolumeFunction));
545 self.register(Box::new(SphereSurfaceAreaFunction));
546 self.register(Box::new(TriangleAreaFunction));
547 self.register(Box::new(Distance2DFunction));
548 }
549
550 fn register_hash_functions(&mut self) {
552 use hash::{Md5Function, Sha1Function, Sha256Function, Sha512Function};
553
554 self.register(Box::new(Md5Function));
555 self.register(Box::new(Sha1Function));
556 self.register(Box::new(Sha256Function));
557 self.register(Box::new(Sha512Function));
558 }
559
560 fn register_comparison_functions(&mut self) {
562 comparison::register_comparison_functions(self);
563 }
564
565 fn register_mathematical_functions(&mut self) {
567 use mathematics::{
568 IsPrimeFunction, NextPrimeFunction, NthPrimeFunction, PrevPrimeFunction,
569 PrimeCountFunction, PrimeFunction, PrimePiFunction,
570 };
571
572 self.register(Box::new(PrimeFunction));
574 self.register(Box::new(NthPrimeFunction)); self.register(Box::new(IsPrimeFunction));
576 self.register(Box::new(PrimeCountFunction));
577 self.register(Box::new(PrimePiFunction)); self.register(Box::new(NextPrimeFunction));
579 self.register(Box::new(PrevPrimeFunction));
580
581 math::register_math_functions(self);
583 }
584
585 fn register_physics_functions(&mut self) {
587 physics::register_physics_functions(self);
588
589 use particle_charges::{
591 ChargeDownQuarkFunction, ChargeElectronFunction, ChargeMuonFunction,
592 ChargeNeutronFunction, ChargePositronFunction, ChargeProtonFunction, ChargeTauFunction,
593 ChargeUpQuarkFunction,
594 };
595
596 self.register(Box::new(ChargeElectronFunction));
597 self.register(Box::new(ChargeProtonFunction));
598 self.register(Box::new(ChargeNeutronFunction));
599 self.register(Box::new(ChargeUpQuarkFunction));
600 self.register(Box::new(ChargeDownQuarkFunction));
601 self.register(Box::new(ChargePositronFunction));
602 self.register(Box::new(ChargeMuonFunction));
603 self.register(Box::new(ChargeTauFunction));
604 }
605
606 fn register_date_time_functions(&mut self) {
608 date_time::register_date_time_functions(self);
609 }
610
611 fn register_financial_functions(&mut self) {
613 financial::register_financial_functions(self);
614 }
615
616 fn register_conversion_functions(&mut self) {
618 use convert::ConvertFunction;
619 use roman::{FromRoman, ToRoman};
620
621 self.register(Box::new(ConvertFunction));
622 self.register(Box::new(ToRoman));
623 self.register(Box::new(FromRoman));
624 }
625
626 fn register_statistical_functions(&mut self) {
628 use statistics::{
629 CorrelationFunction, KurtosisFunction, MedianFunction, ModeFunction,
630 PercentileFunction, SkewFunction, VarPopFunction, VarSampFunction, VarianceFunction,
631 };
632
633 self.register(Box::new(MedianFunction));
634 self.register(Box::new(PercentileFunction));
635 self.register(Box::new(ModeFunction));
636 self.register(Box::new(VarianceFunction));
637 self.register(Box::new(VarSampFunction));
638 self.register(Box::new(VarPopFunction));
639 self.register(Box::new(CorrelationFunction));
640 self.register(Box::new(SkewFunction));
641 self.register(Box::new(KurtosisFunction));
642 }
643
644 fn register_aggregate_functions(&mut self) {
646 use group_num::GroupNumFunction;
647
648 self.register(Box::new(GroupNumFunction::new()));
651 }
652
653 fn register_random_functions(&mut self) {
655 use random::{RandIntFunction, RandRangeFunction, RandomFunction};
656
657 self.register(Box::new(RandomFunction));
658 self.register(Box::new(RandIntFunction));
659 self.register(Box::new(RandRangeFunction));
660 }
661
662 fn register_format_functions(&mut self) {
664 use format::{
665 CenterFunction, FormatDateFunction, FormatNumberFunction, LPadFunction, RPadFunction,
666 };
667 use format_number::{FormatCurrencyFunction, RenderNumberFunction};
668
669 self.register(Box::new(FormatNumberFunction));
670 self.register(Box::new(FormatDateFunction));
671 self.register(Box::new(LPadFunction));
672 self.register(Box::new(RPadFunction));
673 self.register(Box::new(CenterFunction));
674 self.register(Box::new(RenderNumberFunction));
675 self.register(Box::new(FormatCurrencyFunction));
676 }
677
678 fn register_type_checking_functions(&mut self) {
680 use type_checking::{
681 IsBoolFunction, IsDateFunction, IsFloatFunction, IsIntegerFunction, IsNotNullFunction,
682 IsNullFunction, IsNumericFunction,
683 };
684
685 self.register(Box::new(IsDateFunction));
686 self.register(Box::new(IsBoolFunction));
687 self.register(Box::new(IsNumericFunction));
688 self.register(Box::new(IsIntegerFunction));
689 self.register(Box::new(IsFloatFunction));
690 self.register(Box::new(IsNullFunction));
691 self.register(Box::new(IsNotNullFunction));
692 }
693}
694
695impl Default for FunctionRegistry {
696 fn default() -> Self {
697 Self::new()
698 }
699}
700
701#[cfg(test)]
702mod tests {
703 use super::*;
704
705 #[test]
706 fn test_registry_creation() {
707 let registry = FunctionRegistry::new();
708
709 assert!(registry.contains("PI"));
711 assert!(registry.contains("MASS_EARTH"));
712 assert!(registry.contains("ME"));
713 }
714
715 #[test]
716 fn test_case_insensitive_lookup() {
717 let registry = FunctionRegistry::new();
718
719 assert!(registry.get("pi").is_some());
720 assert!(registry.get("PI").is_some());
721 assert!(registry.get("Pi").is_some());
722 }
723
724 #[test]
725 fn test_autocomplete() {
726 let registry = FunctionRegistry::new();
727
728 let mass_functions = registry.autocomplete("MASS");
729 assert!(!mass_functions.is_empty());
730
731 let names: Vec<&str> = mass_functions.iter().map(|sig| sig.name).collect();
733 assert!(names.contains(&"MASS_EARTH"));
734 assert!(names.contains(&"MASS_SUN"));
735 }
736}