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 chemistry;
10pub mod comparison;
11pub mod constants;
12pub mod convert;
13pub mod date_time;
14pub mod financial;
15pub mod format;
16pub mod format_number;
17pub mod geometry;
18pub mod group_num;
19pub mod hash;
20pub mod math;
21pub mod mathematics;
22pub mod particle_charges;
23pub mod physics;
24pub mod random;
25pub mod solar_system;
26pub mod string_methods;
27pub mod type_checking;
28
29pub use string_methods::MethodFunction;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub enum FunctionCategory {
35 Constant, Mathematical, Astronomical, Chemical, Date, String, Aggregate, Conversion, }
44
45impl fmt::Display for FunctionCategory {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 FunctionCategory::Constant => write!(f, "Constant"),
49 FunctionCategory::Mathematical => write!(f, "Mathematical"),
50 FunctionCategory::Astronomical => write!(f, "Astronomical"),
51 FunctionCategory::Chemical => write!(f, "Chemical"),
52 FunctionCategory::Date => write!(f, "Date"),
53 FunctionCategory::String => write!(f, "String"),
54 FunctionCategory::Aggregate => write!(f, "Aggregate"),
55 FunctionCategory::Conversion => write!(f, "Conversion"),
56 }
57 }
58}
59
60#[derive(Debug, Clone)]
62pub enum ArgCount {
63 Fixed(usize),
65 Range(usize, usize),
67 Variadic,
69}
70
71impl ArgCount {
72 #[must_use]
73 pub fn is_valid(&self, count: usize) -> bool {
74 match self {
75 ArgCount::Fixed(n) => count == *n,
76 ArgCount::Range(min, max) => count >= *min && count <= *max,
77 ArgCount::Variadic => true,
78 }
79 }
80
81 #[must_use]
82 pub fn description(&self) -> String {
83 match self {
84 ArgCount::Fixed(0) => "no arguments".to_string(),
85 ArgCount::Fixed(1) => "1 argument".to_string(),
86 ArgCount::Fixed(n) => format!("{n} arguments"),
87 ArgCount::Range(min, max) => format!("{min} to {max} arguments"),
88 ArgCount::Variadic => "any number of arguments".to_string(),
89 }
90 }
91}
92
93#[derive(Debug, Clone)]
95pub struct FunctionSignature {
96 pub name: &'static str,
97 pub category: FunctionCategory,
98 pub arg_count: ArgCount,
99 pub description: &'static str,
100 pub returns: &'static str,
101 pub examples: Vec<&'static str>,
102}
103
104pub trait SqlFunction: Send + Sync {
106 fn signature(&self) -> FunctionSignature;
108
109 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue>;
111
112 fn validate_args(&self, args: &[DataValue]) -> Result<()> {
114 let sig = self.signature();
115 if !sig.arg_count.is_valid(args.len()) {
116 return Err(anyhow!(
117 "{}() expects {}, got {}",
118 sig.name,
119 sig.arg_count.description(),
120 args.len()
121 ));
122 }
123 Ok(())
124 }
125}
126
127pub struct FunctionRegistry {
129 functions: HashMap<String, Box<dyn SqlFunction>>,
130 by_category: HashMap<FunctionCategory, Vec<String>>,
131 methods: HashMap<String, Arc<dyn MethodFunction>>,
132}
133
134impl FunctionRegistry {
135 #[must_use]
137 pub fn new() -> Self {
138 let mut registry = Self {
139 functions: HashMap::new(),
140 by_category: HashMap::new(),
141 methods: HashMap::new(),
142 };
143
144 registry.register_constants();
146 registry.register_astronomical_functions();
147 registry.register_chemical_functions();
148 registry.register_mathematical_functions();
149 registry.register_geometry_functions();
150 registry.register_physics_functions();
151 registry.register_date_time_functions();
152 registry.register_string_methods();
153 registry.register_financial_functions();
154 registry.register_conversion_functions();
155 registry.register_hash_functions();
156 registry.register_comparison_functions();
157 registry.register_aggregate_functions();
158 registry.register_random_functions();
159 registry.register_format_functions();
160 registry.register_type_checking_functions();
161
162 registry
163 }
164
165 pub fn register(&mut self, func: Box<dyn SqlFunction>) {
167 let sig = func.signature();
168 let name = sig.name.to_uppercase();
169 let category = sig.category;
170
171 self.functions.insert(name.clone(), func);
173
174 self.by_category.entry(category).or_default().push(name);
176 }
177
178 #[must_use]
180 pub fn get(&self, name: &str) -> Option<&dyn SqlFunction> {
181 self.functions
182 .get(&name.to_uppercase())
183 .map(std::convert::AsRef::as_ref)
184 }
185
186 #[must_use]
188 pub fn contains(&self, name: &str) -> bool {
189 self.functions.contains_key(&name.to_uppercase())
190 }
191
192 #[must_use]
194 pub fn autocomplete(&self, prefix: &str) -> Vec<FunctionSignature> {
195 let prefix_upper = prefix.to_uppercase();
196 self.functions
197 .iter()
198 .filter(|(name, _)| name.starts_with(&prefix_upper))
199 .map(|(_, func)| func.signature())
200 .collect()
201 }
202
203 #[must_use]
205 pub fn get_by_category(&self, category: FunctionCategory) -> Vec<FunctionSignature> {
206 self.by_category
207 .get(&category)
208 .map(|names| {
209 names
210 .iter()
211 .filter_map(|name| self.functions.get(name))
212 .map(|func| func.signature())
213 .collect()
214 })
215 .unwrap_or_default()
216 }
217
218 #[must_use]
220 pub fn all_functions(&self) -> Vec<FunctionSignature> {
221 self.functions
222 .values()
223 .map(|func| func.signature())
224 .collect()
225 }
226
227 pub fn register_method(&mut self, method: Arc<dyn MethodFunction>) {
229 let method_name = method.method_name().to_uppercase();
230 self.methods.insert(method_name, method);
231 }
232
233 #[must_use]
235 pub fn get_method(&self, name: &str) -> Option<Arc<dyn MethodFunction>> {
236 if let Some(method) = self.methods.get(&name.to_uppercase()) {
238 return Some(Arc::clone(method));
239 }
240
241 for method in self.methods.values() {
243 if method.handles_method(name) {
244 return Some(Arc::clone(method));
245 }
246 }
247
248 None
249 }
250
251 #[must_use]
253 pub fn has_method(&self, name: &str) -> bool {
254 self.get_method(name).is_some()
255 }
256
257 #[must_use]
259 pub fn generate_markdown_docs(&self) -> String {
260 use std::fmt::Write;
261 let mut doc = String::new();
262
263 writeln!(&mut doc, "# SQL CLI Function Reference\n").unwrap();
264 writeln!(
265 &mut doc,
266 "This document is auto-generated from the function registry.\n"
267 )
268 .unwrap();
269
270 let mut categories: Vec<FunctionCategory> = self.by_category.keys().copied().collect();
272 categories.sort_by_key(|c| format!("{c:?}"));
273
274 for category in categories {
275 let functions = self.get_by_category(category);
276 if functions.is_empty() {
277 continue;
278 }
279
280 writeln!(&mut doc, "## {category} Functions\n").unwrap();
281
282 let mut functions = functions;
284 functions.sort_by_key(|f| f.name);
285
286 for func in functions {
287 writeln!(&mut doc, "### {}()\n", func.name).unwrap();
288 writeln!(&mut doc, "**Description:** {}\n", func.description).unwrap();
289 writeln!(
290 &mut doc,
291 "**Arguments:** {}\n",
292 func.arg_count.description()
293 )
294 .unwrap();
295 writeln!(&mut doc, "**Returns:** {}\n", func.returns).unwrap();
296
297 if !func.examples.is_empty() {
298 writeln!(&mut doc, "**Examples:**").unwrap();
299 writeln!(&mut doc, "```sql").unwrap();
300 for example in &func.examples {
301 writeln!(&mut doc, "{example}").unwrap();
302 }
303 writeln!(&mut doc, "```\n").unwrap();
304 }
305 }
306 }
307
308 doc
309 }
310
311 #[must_use]
313 pub fn generate_function_help(&self, name: &str) -> Option<String> {
314 self.get(name).map(|func| {
315 let sig = func.signature();
316 let mut help = String::new();
317 use std::fmt::Write;
318
319 writeln!(&mut help, "Function: {}()", sig.name).unwrap();
320 writeln!(&mut help, "Category: {}", sig.category).unwrap();
321 writeln!(&mut help, "Description: {}", sig.description).unwrap();
322 writeln!(&mut help, "Arguments: {}", sig.arg_count.description()).unwrap();
323 writeln!(&mut help, "Returns: {}", sig.returns).unwrap();
324
325 if !sig.examples.is_empty() {
326 writeln!(&mut help, "\nExamples:").unwrap();
327 for example in &sig.examples {
328 writeln!(&mut help, " {example}").unwrap();
329 }
330 }
331
332 help
333 })
334 }
335
336 #[must_use]
338 pub fn list_functions(&self) -> String {
339 use std::fmt::Write;
340 let mut list = String::new();
341
342 writeln!(&mut list, "Available SQL Functions:\n").unwrap();
343
344 let mut categories: Vec<FunctionCategory> = self.by_category.keys().copied().collect();
345 categories.sort_by_key(|c| format!("{c:?}"));
346
347 for category in categories {
348 let functions = self.get_by_category(category);
349 if functions.is_empty() {
350 continue;
351 }
352
353 writeln!(&mut list, "{category} Functions:").unwrap();
354
355 let mut functions = functions;
356 functions.sort_by_key(|f| f.name);
357
358 for func in functions {
359 writeln!(
360 &mut list,
361 " {:20} - {}",
362 format!("{}()", func.name),
363 func.description
364 )
365 .unwrap();
366 }
367 writeln!(&mut list).unwrap();
368 }
369
370 list
371 }
372
373 fn register_constants(&mut self) {
375 use constants::{
376 EFunction, HbarFunction, MassElectronFunction, MeFunction, PhiFunction, PiFunction,
377 TauFunction,
378 };
379
380 self.register(Box::new(PiFunction));
381 self.register(Box::new(EFunction));
382 self.register(Box::new(MeFunction)); self.register(Box::new(MassElectronFunction)); self.register(Box::new(TauFunction));
385 self.register(Box::new(PhiFunction));
386 self.register(Box::new(HbarFunction));
387 }
388
389 fn register_astronomical_functions(&mut self) {
391 use astronomy::{
392 AuFunction, DistJupiterFunction, DistMarsFunction, DistMercuryFunction,
393 DistNeptuneFunction, DistSaturnFunction, DistUranusFunction, DistVenusFunction,
394 LightYearFunction, MassEarthFunction, MassJupiterFunction, MassMarsFunction,
395 MassMercuryFunction, MassMoonFunction, MassNeptuneFunction, MassSaturnFunction,
396 MassSunFunction, MassUranusFunction, MassVenusFunction, ParsecFunction,
397 RadiusEarthFunction, RadiusJupiterFunction, RadiusMarsFunction, RadiusMercuryFunction,
398 RadiusMoonFunction, RadiusNeptuneFunction, RadiusSaturnFunction, RadiusSunFunction,
399 RadiusUranusFunction, RadiusVenusFunction,
400 };
401
402 use solar_system::{
403 DensitySolarBodyFunction, DistanceSolarBodyFunction, EscapeVelocitySolarBodyFunction,
404 GravitySolarBodyFunction, MassSolarBodyFunction, MoonsSolarBodyFunction,
405 OrbitalPeriodSolarBodyFunction, RadiusSolarBodyFunction,
406 RotationPeriodSolarBodyFunction,
407 };
408
409 self.register(Box::new(MassEarthFunction));
410 self.register(Box::new(MassSunFunction));
411 self.register(Box::new(MassMoonFunction));
412 self.register(Box::new(AuFunction)); self.register(Box::new(LightYearFunction));
414 self.register(Box::new(ParsecFunction));
415
416 self.register(Box::new(MassMercuryFunction));
418 self.register(Box::new(MassVenusFunction));
419 self.register(Box::new(MassMarsFunction));
420 self.register(Box::new(MassJupiterFunction));
421 self.register(Box::new(MassSaturnFunction));
422 self.register(Box::new(MassUranusFunction));
423 self.register(Box::new(MassNeptuneFunction));
424
425 self.register(Box::new(RadiusSunFunction));
427 self.register(Box::new(RadiusEarthFunction));
428 self.register(Box::new(RadiusMoonFunction));
429 self.register(Box::new(RadiusMercuryFunction));
430 self.register(Box::new(RadiusVenusFunction));
431 self.register(Box::new(RadiusMarsFunction));
432 self.register(Box::new(RadiusJupiterFunction));
433 self.register(Box::new(RadiusSaturnFunction));
434 self.register(Box::new(RadiusUranusFunction));
435 self.register(Box::new(RadiusNeptuneFunction));
436
437 self.register(Box::new(DistMercuryFunction));
439 self.register(Box::new(DistVenusFunction));
440 self.register(Box::new(DistMarsFunction));
441 self.register(Box::new(DistJupiterFunction));
442 self.register(Box::new(DistSaturnFunction));
443 self.register(Box::new(DistUranusFunction));
444 self.register(Box::new(DistNeptuneFunction));
445
446 self.register(Box::new(MassSolarBodyFunction));
448 self.register(Box::new(RadiusSolarBodyFunction));
449 self.register(Box::new(DistanceSolarBodyFunction));
450 self.register(Box::new(OrbitalPeriodSolarBodyFunction));
451 self.register(Box::new(GravitySolarBodyFunction));
452 self.register(Box::new(DensitySolarBodyFunction));
453 self.register(Box::new(EscapeVelocitySolarBodyFunction));
454 self.register(Box::new(RotationPeriodSolarBodyFunction));
455 self.register(Box::new(MoonsSolarBodyFunction));
456 }
457
458 fn register_chemical_functions(&mut self) {
460 use chemistry::{
461 AtomicMassFunction, AtomicNumberFunction, AvogadroFunction, MoleculeFormulaFunction,
462 NeutronsFunction,
463 };
464
465 self.register(Box::new(AvogadroFunction));
466 self.register(Box::new(AtomicMassFunction));
467 self.register(Box::new(AtomicNumberFunction));
468 self.register(Box::new(NeutronsFunction));
469 self.register(Box::new(MoleculeFormulaFunction));
470 }
471
472 fn register_string_methods(&mut self) {
474 string_methods::register_string_methods(self);
475 }
476
477 fn register_geometry_functions(&mut self) {
479 use geometry::{
480 CircleAreaFunction, CircleCircumferenceFunction, Distance2DFunction,
481 PythagorasFunction, SphereSurfaceAreaFunction, SphereVolumeFunction,
482 TriangleAreaFunction,
483 };
484
485 self.register(Box::new(PythagorasFunction));
486 self.register(Box::new(CircleAreaFunction));
487 self.register(Box::new(CircleCircumferenceFunction));
488 self.register(Box::new(SphereVolumeFunction));
489 self.register(Box::new(SphereSurfaceAreaFunction));
490 self.register(Box::new(TriangleAreaFunction));
491 self.register(Box::new(Distance2DFunction));
492 }
493
494 fn register_hash_functions(&mut self) {
496 use hash::{Md5Function, Sha1Function, Sha256Function, Sha512Function};
497
498 self.register(Box::new(Md5Function));
499 self.register(Box::new(Sha1Function));
500 self.register(Box::new(Sha256Function));
501 self.register(Box::new(Sha512Function));
502 }
503
504 fn register_comparison_functions(&mut self) {
506 comparison::register_comparison_functions(self);
507 }
508
509 fn register_mathematical_functions(&mut self) {
511 use mathematics::{
512 IsPrimeFunction, NextPrimeFunction, NthPrimeFunction, PrevPrimeFunction,
513 PrimeCountFunction, PrimeFunction, PrimePiFunction,
514 };
515
516 self.register(Box::new(PrimeFunction));
518 self.register(Box::new(NthPrimeFunction)); self.register(Box::new(IsPrimeFunction));
520 self.register(Box::new(PrimeCountFunction));
521 self.register(Box::new(PrimePiFunction)); self.register(Box::new(NextPrimeFunction));
523 self.register(Box::new(PrevPrimeFunction));
524
525 math::register_math_functions(self);
527 }
528
529 fn register_physics_functions(&mut self) {
531 physics::register_physics_functions(self);
532
533 use particle_charges::{
535 ChargeDownQuarkFunction, ChargeElectronFunction, ChargeMuonFunction,
536 ChargeNeutronFunction, ChargePositronFunction, ChargeProtonFunction, ChargeTauFunction,
537 ChargeUpQuarkFunction,
538 };
539
540 self.register(Box::new(ChargeElectronFunction));
541 self.register(Box::new(ChargeProtonFunction));
542 self.register(Box::new(ChargeNeutronFunction));
543 self.register(Box::new(ChargeUpQuarkFunction));
544 self.register(Box::new(ChargeDownQuarkFunction));
545 self.register(Box::new(ChargePositronFunction));
546 self.register(Box::new(ChargeMuonFunction));
547 self.register(Box::new(ChargeTauFunction));
548 }
549
550 fn register_date_time_functions(&mut self) {
552 date_time::register_date_time_functions(self);
553 }
554
555 fn register_financial_functions(&mut self) {
557 financial::register_financial_functions(self);
558 }
559
560 fn register_conversion_functions(&mut self) {
562 use convert::ConvertFunction;
563
564 self.register(Box::new(ConvertFunction));
565 }
566
567 fn register_aggregate_functions(&mut self) {
569 use group_num::GroupNumFunction;
570
571 self.register(Box::new(GroupNumFunction::new()));
574 }
575
576 fn register_random_functions(&mut self) {
578 use random::{RandIntFunction, RandRangeFunction, RandomFunction};
579
580 self.register(Box::new(RandomFunction));
581 self.register(Box::new(RandIntFunction));
582 self.register(Box::new(RandRangeFunction));
583 }
584
585 fn register_format_functions(&mut self) {
587 use format::{
588 CenterFunction, FormatDateFunction, FormatNumberFunction, LPadFunction, RPadFunction,
589 };
590 use format_number::{FormatCurrencyFunction, RenderNumberFunction};
591
592 self.register(Box::new(FormatNumberFunction));
593 self.register(Box::new(FormatDateFunction));
594 self.register(Box::new(LPadFunction));
595 self.register(Box::new(RPadFunction));
596 self.register(Box::new(CenterFunction));
597 self.register(Box::new(RenderNumberFunction));
598 self.register(Box::new(FormatCurrencyFunction));
599 }
600
601 fn register_type_checking_functions(&mut self) {
603 use type_checking::{
604 IsBoolFunction, IsDateFunction, IsFloatFunction, IsIntegerFunction, IsNotNullFunction,
605 IsNullFunction, IsNumericFunction,
606 };
607
608 self.register(Box::new(IsDateFunction));
609 self.register(Box::new(IsBoolFunction));
610 self.register(Box::new(IsNumericFunction));
611 self.register(Box::new(IsIntegerFunction));
612 self.register(Box::new(IsFloatFunction));
613 self.register(Box::new(IsNullFunction));
614 self.register(Box::new(IsNotNullFunction));
615 }
616}
617
618impl Default for FunctionRegistry {
619 fn default() -> Self {
620 Self::new()
621 }
622}
623
624#[cfg(test)]
625mod tests {
626 use super::*;
627
628 #[test]
629 fn test_registry_creation() {
630 let registry = FunctionRegistry::new();
631
632 assert!(registry.contains("PI"));
634 assert!(registry.contains("MASS_EARTH"));
635 assert!(registry.contains("ME"));
636 }
637
638 #[test]
639 fn test_case_insensitive_lookup() {
640 let registry = FunctionRegistry::new();
641
642 assert!(registry.get("pi").is_some());
643 assert!(registry.get("PI").is_some());
644 assert!(registry.get("Pi").is_some());
645 }
646
647 #[test]
648 fn test_autocomplete() {
649 let registry = FunctionRegistry::new();
650
651 let mass_functions = registry.autocomplete("MASS");
652 assert!(!mass_functions.is_empty());
653
654 let names: Vec<&str> = mass_functions.iter().map(|sig| sig.name).collect();
656 assert!(names.contains(&"MASS_EARTH"));
657 assert!(names.contains(&"MASS_SUN"));
658 }
659}