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