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