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 date_time;
13pub mod geometry;
14pub mod hash;
15pub mod math;
16pub mod mathematics;
17pub mod physics;
18pub mod string_methods;
19
20pub use string_methods::MethodFunction;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
25pub enum FunctionCategory {
26 Constant, Mathematical, Astronomical, Chemical, Date, String, Aggregate, }
34
35impl fmt::Display for FunctionCategory {
36 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37 match self {
38 FunctionCategory::Constant => write!(f, "Constant"),
39 FunctionCategory::Mathematical => write!(f, "Mathematical"),
40 FunctionCategory::Astronomical => write!(f, "Astronomical"),
41 FunctionCategory::Chemical => write!(f, "Chemical"),
42 FunctionCategory::Date => write!(f, "Date"),
43 FunctionCategory::String => write!(f, "String"),
44 FunctionCategory::Aggregate => write!(f, "Aggregate"),
45 }
46 }
47}
48
49#[derive(Debug, Clone)]
51pub enum ArgCount {
52 Fixed(usize),
54 Range(usize, usize),
56 Variadic,
58}
59
60impl ArgCount {
61 #[must_use]
62 pub fn is_valid(&self, count: usize) -> bool {
63 match self {
64 ArgCount::Fixed(n) => count == *n,
65 ArgCount::Range(min, max) => count >= *min && count <= *max,
66 ArgCount::Variadic => true,
67 }
68 }
69
70 #[must_use]
71 pub fn description(&self) -> String {
72 match self {
73 ArgCount::Fixed(0) => "no arguments".to_string(),
74 ArgCount::Fixed(1) => "1 argument".to_string(),
75 ArgCount::Fixed(n) => format!("{n} arguments"),
76 ArgCount::Range(min, max) => format!("{min} to {max} arguments"),
77 ArgCount::Variadic => "any number of arguments".to_string(),
78 }
79 }
80}
81
82#[derive(Debug, Clone)]
84pub struct FunctionSignature {
85 pub name: &'static str,
86 pub category: FunctionCategory,
87 pub arg_count: ArgCount,
88 pub description: &'static str,
89 pub returns: &'static str,
90 pub examples: Vec<&'static str>,
91}
92
93pub trait SqlFunction: Send + Sync {
95 fn signature(&self) -> FunctionSignature;
97
98 fn evaluate(&self, args: &[DataValue]) -> Result<DataValue>;
100
101 fn validate_args(&self, args: &[DataValue]) -> Result<()> {
103 let sig = self.signature();
104 if !sig.arg_count.is_valid(args.len()) {
105 return Err(anyhow!(
106 "{}() expects {}, got {}",
107 sig.name,
108 sig.arg_count.description(),
109 args.len()
110 ));
111 }
112 Ok(())
113 }
114}
115
116pub struct FunctionRegistry {
118 functions: HashMap<String, Box<dyn SqlFunction>>,
119 by_category: HashMap<FunctionCategory, Vec<String>>,
120 methods: HashMap<String, Arc<dyn MethodFunction>>,
121}
122
123impl FunctionRegistry {
124 #[must_use]
126 pub fn new() -> Self {
127 let mut registry = Self {
128 functions: HashMap::new(),
129 by_category: HashMap::new(),
130 methods: HashMap::new(),
131 };
132
133 registry.register_constants();
135 registry.register_astronomical_functions();
136 registry.register_chemical_functions();
137 registry.register_mathematical_functions();
138 registry.register_geometry_functions();
139 registry.register_physics_functions();
140 registry.register_date_time_functions();
141 registry.register_string_methods();
142 registry.register_hash_functions();
143 registry.register_comparison_functions();
144
145 registry
146 }
147
148 pub fn register(&mut self, func: Box<dyn SqlFunction>) {
150 let sig = func.signature();
151 let name = sig.name.to_uppercase();
152 let category = sig.category;
153
154 self.functions.insert(name.clone(), func);
156
157 self.by_category.entry(category).or_default().push(name);
159 }
160
161 #[must_use]
163 pub fn get(&self, name: &str) -> Option<&dyn SqlFunction> {
164 self.functions
165 .get(&name.to_uppercase())
166 .map(std::convert::AsRef::as_ref)
167 }
168
169 #[must_use]
171 pub fn contains(&self, name: &str) -> bool {
172 self.functions.contains_key(&name.to_uppercase())
173 }
174
175 #[must_use]
177 pub fn autocomplete(&self, prefix: &str) -> Vec<FunctionSignature> {
178 let prefix_upper = prefix.to_uppercase();
179 self.functions
180 .iter()
181 .filter(|(name, _)| name.starts_with(&prefix_upper))
182 .map(|(_, func)| func.signature())
183 .collect()
184 }
185
186 #[must_use]
188 pub fn get_by_category(&self, category: FunctionCategory) -> Vec<FunctionSignature> {
189 self.by_category
190 .get(&category)
191 .map(|names| {
192 names
193 .iter()
194 .filter_map(|name| self.functions.get(name))
195 .map(|func| func.signature())
196 .collect()
197 })
198 .unwrap_or_default()
199 }
200
201 #[must_use]
203 pub fn all_functions(&self) -> Vec<FunctionSignature> {
204 self.functions
205 .values()
206 .map(|func| func.signature())
207 .collect()
208 }
209
210 pub fn register_method(&mut self, method: Arc<dyn MethodFunction>) {
212 let method_name = method.method_name().to_uppercase();
213 self.methods.insert(method_name, method);
214 }
215
216 #[must_use]
218 pub fn get_method(&self, name: &str) -> Option<Arc<dyn MethodFunction>> {
219 if let Some(method) = self.methods.get(&name.to_uppercase()) {
221 return Some(Arc::clone(method));
222 }
223
224 for method in self.methods.values() {
226 if method.handles_method(name) {
227 return Some(Arc::clone(method));
228 }
229 }
230
231 None
232 }
233
234 #[must_use]
236 pub fn has_method(&self, name: &str) -> bool {
237 self.get_method(name).is_some()
238 }
239
240 #[must_use]
242 pub fn generate_markdown_docs(&self) -> String {
243 use std::fmt::Write;
244 let mut doc = String::new();
245
246 writeln!(&mut doc, "# SQL CLI Function Reference\n").unwrap();
247 writeln!(
248 &mut doc,
249 "This document is auto-generated from the function registry.\n"
250 )
251 .unwrap();
252
253 let mut categories: Vec<FunctionCategory> = self.by_category.keys().copied().collect();
255 categories.sort_by_key(|c| format!("{c:?}"));
256
257 for category in categories {
258 let functions = self.get_by_category(category);
259 if functions.is_empty() {
260 continue;
261 }
262
263 writeln!(&mut doc, "## {category} Functions\n").unwrap();
264
265 let mut functions = functions;
267 functions.sort_by_key(|f| f.name);
268
269 for func in functions {
270 writeln!(&mut doc, "### {}()\n", func.name).unwrap();
271 writeln!(&mut doc, "**Description:** {}\n", func.description).unwrap();
272 writeln!(
273 &mut doc,
274 "**Arguments:** {}\n",
275 func.arg_count.description()
276 )
277 .unwrap();
278 writeln!(&mut doc, "**Returns:** {}\n", func.returns).unwrap();
279
280 if !func.examples.is_empty() {
281 writeln!(&mut doc, "**Examples:**").unwrap();
282 writeln!(&mut doc, "```sql").unwrap();
283 for example in &func.examples {
284 writeln!(&mut doc, "{example}").unwrap();
285 }
286 writeln!(&mut doc, "```\n").unwrap();
287 }
288 }
289 }
290
291 doc
292 }
293
294 #[must_use]
296 pub fn generate_function_help(&self, name: &str) -> Option<String> {
297 self.get(name).map(|func| {
298 let sig = func.signature();
299 let mut help = String::new();
300 use std::fmt::Write;
301
302 writeln!(&mut help, "Function: {}()", sig.name).unwrap();
303 writeln!(&mut help, "Category: {}", sig.category).unwrap();
304 writeln!(&mut help, "Description: {}", sig.description).unwrap();
305 writeln!(&mut help, "Arguments: {}", sig.arg_count.description()).unwrap();
306 writeln!(&mut help, "Returns: {}", sig.returns).unwrap();
307
308 if !sig.examples.is_empty() {
309 writeln!(&mut help, "\nExamples:").unwrap();
310 for example in &sig.examples {
311 writeln!(&mut help, " {example}").unwrap();
312 }
313 }
314
315 help
316 })
317 }
318
319 #[must_use]
321 pub fn list_functions(&self) -> String {
322 use std::fmt::Write;
323 let mut list = String::new();
324
325 writeln!(&mut list, "Available SQL Functions:\n").unwrap();
326
327 let mut categories: Vec<FunctionCategory> = self.by_category.keys().copied().collect();
328 categories.sort_by_key(|c| format!("{c:?}"));
329
330 for category in categories {
331 let functions = self.get_by_category(category);
332 if functions.is_empty() {
333 continue;
334 }
335
336 writeln!(&mut list, "{category} Functions:").unwrap();
337
338 let mut functions = functions;
339 functions.sort_by_key(|f| f.name);
340
341 for func in functions {
342 writeln!(
343 &mut list,
344 " {:20} - {}",
345 format!("{}()", func.name),
346 func.description
347 )
348 .unwrap();
349 }
350 writeln!(&mut list).unwrap();
351 }
352
353 list
354 }
355
356 fn register_constants(&mut self) {
358 use constants::{
359 EFunction, HbarFunction, MassElectronFunction, MeFunction, PhiFunction, PiFunction,
360 TauFunction,
361 };
362
363 self.register(Box::new(PiFunction));
364 self.register(Box::new(EFunction));
365 self.register(Box::new(MeFunction)); self.register(Box::new(MassElectronFunction)); self.register(Box::new(TauFunction));
368 self.register(Box::new(PhiFunction));
369 self.register(Box::new(HbarFunction));
370 }
371
372 fn register_astronomical_functions(&mut self) {
374 use astronomy::{
375 AuFunction, DistJupiterFunction, DistMarsFunction, DistMercuryFunction,
376 DistNeptuneFunction, DistSaturnFunction, DistUranusFunction, DistVenusFunction,
377 LightYearFunction, MassEarthFunction, MassJupiterFunction, MassMarsFunction,
378 MassMercuryFunction, MassMoonFunction, MassNeptuneFunction, MassSaturnFunction,
379 MassSunFunction, MassUranusFunction, MassVenusFunction, ParsecFunction,
380 RadiusEarthFunction, RadiusJupiterFunction, RadiusMarsFunction, RadiusMercuryFunction,
381 RadiusMoonFunction, RadiusNeptuneFunction, RadiusSaturnFunction, RadiusSunFunction,
382 RadiusUranusFunction, RadiusVenusFunction,
383 };
384
385 self.register(Box::new(MassEarthFunction));
386 self.register(Box::new(MassSunFunction));
387 self.register(Box::new(MassMoonFunction));
388 self.register(Box::new(AuFunction)); self.register(Box::new(LightYearFunction));
390 self.register(Box::new(ParsecFunction));
391
392 self.register(Box::new(MassMercuryFunction));
394 self.register(Box::new(MassVenusFunction));
395 self.register(Box::new(MassMarsFunction));
396 self.register(Box::new(MassJupiterFunction));
397 self.register(Box::new(MassSaturnFunction));
398 self.register(Box::new(MassUranusFunction));
399 self.register(Box::new(MassNeptuneFunction));
400
401 self.register(Box::new(RadiusSunFunction));
403 self.register(Box::new(RadiusEarthFunction));
404 self.register(Box::new(RadiusMoonFunction));
405 self.register(Box::new(RadiusMercuryFunction));
406 self.register(Box::new(RadiusVenusFunction));
407 self.register(Box::new(RadiusMarsFunction));
408 self.register(Box::new(RadiusJupiterFunction));
409 self.register(Box::new(RadiusSaturnFunction));
410 self.register(Box::new(RadiusUranusFunction));
411 self.register(Box::new(RadiusNeptuneFunction));
412
413 self.register(Box::new(DistMercuryFunction));
415 self.register(Box::new(DistVenusFunction));
416 self.register(Box::new(DistMarsFunction));
417 self.register(Box::new(DistJupiterFunction));
418 self.register(Box::new(DistSaturnFunction));
419 self.register(Box::new(DistUranusFunction));
420 self.register(Box::new(DistNeptuneFunction));
421 }
422
423 fn register_chemical_functions(&mut self) {
425 use chemistry::{
426 AtomicMassFunction, AtomicNumberFunction, AvogadroFunction, MoleculeFormulaFunction,
427 };
428
429 self.register(Box::new(AvogadroFunction));
430 self.register(Box::new(AtomicMassFunction));
431 self.register(Box::new(AtomicNumberFunction));
432 self.register(Box::new(MoleculeFormulaFunction));
433 }
434
435 fn register_string_methods(&mut self) {
437 string_methods::register_string_methods(self);
438 }
439
440 fn register_geometry_functions(&mut self) {
442 use geometry::{
443 CircleAreaFunction, CircleCircumferenceFunction, Distance2DFunction,
444 PythagorasFunction, SphereSurfaceAreaFunction, SphereVolumeFunction,
445 TriangleAreaFunction,
446 };
447
448 self.register(Box::new(PythagorasFunction));
449 self.register(Box::new(CircleAreaFunction));
450 self.register(Box::new(CircleCircumferenceFunction));
451 self.register(Box::new(SphereVolumeFunction));
452 self.register(Box::new(SphereSurfaceAreaFunction));
453 self.register(Box::new(TriangleAreaFunction));
454 self.register(Box::new(Distance2DFunction));
455 }
456
457 fn register_hash_functions(&mut self) {
459 use hash::{Md5Function, Sha1Function, Sha256Function, Sha512Function};
460
461 self.register(Box::new(Md5Function));
462 self.register(Box::new(Sha1Function));
463 self.register(Box::new(Sha256Function));
464 self.register(Box::new(Sha512Function));
465 }
466
467 fn register_comparison_functions(&mut self) {
469 comparison::register_comparison_functions(self);
470 }
471
472 fn register_mathematical_functions(&mut self) {
474 use mathematics::{
475 IsPrimeFunction, NextPrimeFunction, NthPrimeFunction, PrevPrimeFunction,
476 PrimeCountFunction, PrimeFunction, PrimePiFunction,
477 };
478
479 self.register(Box::new(PrimeFunction));
481 self.register(Box::new(NthPrimeFunction)); self.register(Box::new(IsPrimeFunction));
483 self.register(Box::new(PrimeCountFunction));
484 self.register(Box::new(PrimePiFunction)); self.register(Box::new(NextPrimeFunction));
486 self.register(Box::new(PrevPrimeFunction));
487
488 math::register_math_functions(self);
490 }
491
492 fn register_physics_functions(&mut self) {
494 physics::register_physics_functions(self);
495 }
496
497 fn register_date_time_functions(&mut self) {
499 date_time::register_date_time_functions(self);
500 }
501}
502
503impl Default for FunctionRegistry {
504 fn default() -> Self {
505 Self::new()
506 }
507}
508
509#[cfg(test)]
510mod tests {
511 use super::*;
512
513 #[test]
514 fn test_registry_creation() {
515 let registry = FunctionRegistry::new();
516
517 assert!(registry.contains("PI"));
519 assert!(registry.contains("MASS_EARTH"));
520 assert!(registry.contains("ME"));
521 }
522
523 #[test]
524 fn test_case_insensitive_lookup() {
525 let registry = FunctionRegistry::new();
526
527 assert!(registry.get("pi").is_some());
528 assert!(registry.get("PI").is_some());
529 assert!(registry.get("Pi").is_some());
530 }
531
532 #[test]
533 fn test_autocomplete() {
534 let registry = FunctionRegistry::new();
535
536 let mass_functions = registry.autocomplete("MASS");
537 assert!(!mass_functions.is_empty());
538
539 let names: Vec<&str> = mass_functions.iter().map(|sig| sig.name).collect();
541 assert!(names.contains(&"MASS_EARTH"));
542 assert!(names.contains(&"MASS_SUN"));
543 }
544}