dsq_functions/
lib.rs

1//! Built-in functions for dsq
2//!
3//! This crate provides all built-in functions available in dsq filters,
4//! including jq-compatible functions and DataFrame-specific operations.
5
6pub mod builtin;
7
8// Re-export inventory for use by builtin modules
9pub use inventory;
10
11use dsq_shared::value::{value_from_any_value, Value};
12use dsq_shared::Result;
13use std::collections::HashMap;
14use std::sync::Arc;
15
16use sha2::{Digest, Sha256};
17
18use chrono::{DateTime, NaiveDate, NaiveDateTime, TimeZone, Utc};
19
20/// Built-in function implementation
21pub type BuiltinFunction = Arc<dyn Fn(&[Value]) -> Result<Value> + Send + Sync>;
22
23inventory::collect!(FunctionRegistration);
24
25pub struct FunctionRegistration {
26    pub name: &'static str,
27    pub func: fn(&[Value]) -> Result<Value>,
28}
29
30/// Registry of built-in functions
31///
32/// This struct manages all built-in functions and provides a unified interface
33/// for calling them during filter execution.
34pub struct BuiltinRegistry {
35    functions: HashMap<String, BuiltinFunction>,
36}
37
38impl std::fmt::Debug for BuiltinRegistry {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        write!(
41            f,
42            "BuiltinRegistry {{ functions: {} functions }}",
43            self.functions.len()
44        )
45    }
46}
47
48impl BuiltinRegistry {
49    /// Create a new builtin registry with standard functions
50    pub fn new() -> Self {
51        let mut registry = Self {
52            functions: HashMap::new(),
53        };
54
55        registry.register_standard_functions();
56        registry
57    }
58
59    /// Register all standard built-in functions
60    fn register_standard_functions(&mut self) {
61        // Register functions from inventory
62        for func in inventory::iter::<FunctionRegistration> {
63            self.register(func.name, Arc::new(func.func));
64        }
65    }
66
67    /// Register a built-in function
68    pub fn register(&mut self, name: impl Into<String>, func: BuiltinFunction) {
69        self.functions.insert(name.into(), func);
70    }
71
72    /// Check if a function exists
73    pub fn has_function(&self, name: &str) -> bool {
74        self.functions.contains_key(name)
75    }
76
77    /// Call a built-in function
78    pub fn call_function(&self, name: &str, args: &[Value]) -> Result<Value> {
79        if let Some(func) = self.functions.get(name) {
80            func(args)
81        } else {
82            Err(dsq_shared::error::operation_error(format!(
83                "built-in function '{}'",
84                name
85            )))
86        }
87    }
88
89    /// Get the number of registered functions
90    pub fn function_count(&self) -> usize {
91        self.functions.len()
92    }
93
94    /// Get a built-in function by name
95    pub fn get_function(&self, name: &str) -> Option<BuiltinFunction> {
96        self.functions.get(name).cloned()
97    }
98
99    /// Get all function names
100    pub fn function_names(&self) -> Vec<String> {
101        self.functions.keys().cloned().collect()
102    }
103}
104
105impl Default for BuiltinRegistry {
106    fn default() -> Self {
107        Self::new()
108    }
109}
110
111// Helper function to extract DateTime from various input types
112pub(crate) fn extract_timestamp(value: &Value) -> Result<DateTime<Utc>> {
113    match value {
114        Value::Int(i) => Utc
115            .timestamp_opt(*i, 0)
116            .single()
117            .ok_or_else(|| dsq_shared::error::operation_error("Invalid timestamp")),
118        Value::Float(f) => {
119            let secs = f.trunc() as i64;
120            let nanos = (f.fract() * 1_000_000_000.0) as u32;
121            Utc.timestamp_opt(secs, nanos)
122                .single()
123                .ok_or_else(|| dsq_shared::error::operation_error("Invalid timestamp"))
124        }
125        Value::String(s) => {
126            // Try parsing as RFC3339 first
127            if let Ok(dt) = DateTime::parse_from_rfc3339(s) {
128                Ok(dt.with_timezone(&Utc))
129            } else if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%d %H:%M:%S") {
130                Ok(dt.and_utc())
131            } else if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S") {
132                Ok(dt.and_utc())
133            } else if let Ok(date) = NaiveDate::parse_from_str(s, "%Y-%m-%d") {
134                let dt = date.and_hms_opt(0, 0, 0).unwrap();
135                Ok(dt.and_utc())
136            } else if let Ok(dt) = NaiveDateTime::parse_from_str(s, "%Y/%m/%d %H:%M:%S") {
137                Ok(dt.and_utc())
138            } else if let Ok(date) = NaiveDate::parse_from_str(s, "%Y/%m/%d") {
139                let dt = date.and_hms_opt(0, 0, 0).unwrap();
140                Ok(dt.and_utc())
141            } else {
142                Err(dsq_shared::error::operation_error(
143                    "Unable to parse date/time string",
144                ))
145            }
146        }
147        _ => Err(dsq_shared::error::operation_error(
148            "Unsupported type for date extraction",
149        )),
150    }
151}
152
153// Built-in function implementations
154
155inventory::submit! {
156    FunctionRegistration {
157        name: "lstrip",
158        func: builtin_lstrip,
159    }
160}
161
162fn builtin_lstrip(args: &[Value]) -> Result<Value> {
163    if args.len() != 1 {
164        return Err(dsq_shared::error::operation_error(
165            "lstrip() expects 1 argument",
166        ));
167    }
168
169    match &args[0] {
170        Value::String(s) => Ok(Value::String(s.trim_start().to_string())),
171        _ => Err(dsq_shared::error::operation_error(
172            "lstrip() requires string argument",
173        )),
174    }
175}
176
177inventory::submit! {
178    FunctionRegistration {
179        name: "sha256",
180        func: builtin_sha256,
181    }
182}
183
184fn builtin_sha256(args: &[Value]) -> Result<Value> {
185    if args.len() != 1 {
186        return Err(dsq_shared::error::operation_error(
187            "sha256() expects 1 argument",
188        ));
189    }
190
191    match &args[0] {
192        Value::String(s) => {
193            let mut hasher = Sha256::new();
194            hasher.update(s.as_bytes());
195            let result = hasher.finalize();
196            Ok(Value::String(format!("{:x}", result)))
197        }
198        _ => Err(dsq_shared::error::operation_error(
199            "sha256() requires string argument",
200        )),
201    }
202}
203
204pub fn compare_values_for_sorting(a: &Value, b: &Value) -> std::cmp::Ordering {
205    match (a, b) {
206        (Value::Null, Value::Null) => std::cmp::Ordering::Equal,
207        (Value::Null, _) => std::cmp::Ordering::Less,
208        (_, Value::Null) => std::cmp::Ordering::Greater,
209        (Value::Bool(a_val), Value::Bool(b_val)) => a_val.cmp(b_val),
210        (Value::Int(a_val), Value::Int(b_val)) => a_val.cmp(b_val),
211        (Value::Float(a_val), Value::Float(b_val)) => a_val
212            .partial_cmp(b_val)
213            .unwrap_or(std::cmp::Ordering::Equal),
214        (Value::String(a_val), Value::String(b_val)) => a_val.cmp(b_val),
215        (Value::Int(a_val), Value::Float(b_val)) => (*a_val as f64)
216            .partial_cmp(b_val)
217            .unwrap_or(std::cmp::Ordering::Equal),
218        (Value::Float(a_val), Value::Int(b_val)) => a_val
219            .partial_cmp(&(*b_val as f64))
220            .unwrap_or(std::cmp::Ordering::Equal),
221        _ => std::cmp::Ordering::Equal,
222    }
223}
224
225inventory::submit! {
226    FunctionRegistration {
227        name: "dtypes",
228        func: builtin_dtypes,
229    }
230}
231
232fn builtin_dtypes(args: &[Value]) -> Result<Value> {
233    if args.len() != 1 {
234        return Err(dsq_shared::error::operation_error(
235            "dtypes() expects 1 argument",
236        ));
237    }
238
239    match &args[0] {
240        Value::DataFrame(df) => {
241            let mut dtypes_obj = HashMap::new();
242            for col_name in df.get_column_names() {
243                if let Ok(series) = df.column(col_name) {
244                    dtypes_obj.insert(
245                        col_name.to_string(),
246                        Value::String(series.dtype().to_string()),
247                    );
248                }
249            }
250            Ok(Value::Object(dtypes_obj))
251        }
252        _ => Err(dsq_shared::error::operation_error(
253            "dtypes() requires DataFrame argument",
254        )),
255    }
256}
257
258inventory::submit! {
259    FunctionRegistration {
260        name: "round",
261        func: builtin_round,
262    }
263}
264
265fn builtin_round(args: &[Value]) -> Result<Value> {
266    if args.is_empty() || args.len() > 2 {
267        return Err(dsq_shared::error::operation_error(
268            "round() expects 1 or 2 arguments",
269        ));
270    }
271
272    let precision = if args.len() == 2 {
273        match &args[1] {
274            Value::Int(i) => *i as usize,
275            _ => {
276                return Err(dsq_shared::error::operation_error(
277                    "round() precision must be an integer",
278                ))
279            }
280        }
281    } else {
282        0
283    };
284
285    match &args[0] {
286        Value::Int(i) => {
287            if precision == 0 {
288                Ok(Value::Int(*i))
289            } else {
290                Ok(Value::Float(*i as f64))
291            }
292        }
293        Value::Float(f) => {
294            let multiplier = 10f64.powi(precision as i32);
295            let rounded = (f * multiplier).round() / multiplier;
296            Ok(Value::Float(rounded))
297        }
298        Value::Array(arr) => {
299            let rounded_arr: Result<Vec<Value>> = arr
300                .iter()
301                .map(|v| match v {
302                    Value::Int(i) => {
303                        if precision == 0 {
304                            Ok(Value::Int(*i))
305                        } else {
306                            Ok(Value::Float(*i as f64))
307                        }
308                    }
309                    Value::Float(f) => {
310                        let multiplier = 10f64.powi(precision as i32);
311                        let rounded = (f * multiplier).round() / multiplier;
312                        Ok(Value::Float(rounded))
313                    }
314                    _ => Ok(v.clone()),
315                })
316                .collect();
317            Ok(Value::Array(rounded_arr?))
318        }
319        Value::DataFrame(df) => {
320            // For simplicity, return unchanged for now
321            Ok(Value::DataFrame(df.clone()))
322        }
323        Value::Series(series) => {
324            // For simplicity, return unchanged for now
325            Ok(Value::Series(series.clone()))
326        }
327        _ => Ok(args[0].clone()),
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    use super::*;
334    use crate::builtin::filter::builtin_filter;
335    use crate::builtin::histogram::builtin_histogram;
336    use crate::builtin::select::builtin_select;
337    use crate::builtin::transliterate::builtin_transliterate;
338    use chrono::Datelike;
339    use dsq_shared::value::Value;
340    use polars::datatypes::PlSmallStr;
341    use polars::prelude::*;
342    use std::collections::HashMap;
343
344    fn create_test_dataframe() -> DataFrame {
345        let names = Series::new(PlSmallStr::from("name"), &["Alice", "Bob", "Charlie"]);
346        let ages = Series::new(PlSmallStr::from("age"), &[25, 30, 35]);
347        let scores = Series::new(PlSmallStr::from("score"), &[85.5, 92.0, 78.3]);
348        DataFrame::new(vec![names.into(), ages.into(), scores.into()]).unwrap()
349    }
350
351    #[test]
352    fn test_extract_timestamp() {
353        // Test with integer timestamp
354        let ts = Value::Int(1609459200); // 2021-01-01 00:00:00 UTC
355        let dt = extract_timestamp(&ts).unwrap();
356        assert_eq!(dt.year(), 2021);
357        assert_eq!(dt.month(), 1);
358        assert_eq!(dt.day(), 1);
359
360        // Test with RFC3339 string
361        let rfc3339 = Value::String("2021-01-01T00:00:00Z".to_string());
362        let dt = extract_timestamp(&rfc3339).unwrap();
363        assert_eq!(dt.year(), 2021);
364
365        // Test with date string
366        let date_str = Value::String("2021-01-01".to_string());
367        let dt = extract_timestamp(&date_str).unwrap();
368        assert_eq!(dt.year(), 2021);
369        assert_eq!(dt.month(), 1);
370        assert_eq!(dt.day(), 1);
371    }
372
373    #[test]
374    fn test_start_of_month() {
375        let registry = BuiltinRegistry::new();
376
377        // Test with timestamp in the middle of the month
378        let ts = Value::Int(1612137600); // 2021-02-01 00:00:00 UTC (already start of month)
379        let result = registry.call_function("start_of_month", &[ts]).unwrap();
380        assert_eq!(result, Value::String("2021-02-01".to_string()));
381
382        // Test with timestamp later in the month
383        let ts = Value::Int(1614556800); // 2021-03-01 00:00:00 UTC
384        let result = registry.call_function("start_of_month", &[ts]).unwrap();
385        assert_eq!(result, Value::String("2021-03-01".to_string()));
386
387        // Test with timestamp in the middle of February
388        let ts = Value::Int(1613347200); // 2021-02-15 00:00:00 UTC
389        let result = registry.call_function("start_of_month", &[ts]).unwrap();
390        assert_eq!(result, Value::String("2021-02-01".to_string()));
391
392        // Test with string date
393        let date_str = Value::String("2021-06-15".to_string());
394        let result = registry
395            .call_function("start_of_month", &[date_str])
396            .unwrap();
397        assert_eq!(result, Value::String("2021-06-01".to_string()));
398    }
399
400    #[test]
401    fn test_truncate_time_function() {
402        let registry = BuiltinRegistry::new();
403
404        // Test truncate to day
405        let result = registry
406            .call_function(
407                "truncate_time",
408                &[
409                    Value::String("2021-06-15T14:30:45Z".to_string()),
410                    Value::String("day".to_string()),
411                ],
412            )
413            .unwrap();
414        assert_eq!(
415            result,
416            Value::String("2021-06-15T00:00:00+00:00".to_string())
417        );
418
419        // Test truncate to hour
420        let result = registry
421            .call_function(
422                "truncate_time",
423                &[
424                    Value::String("2021-06-15T14:30:45Z".to_string()),
425                    Value::String("hour".to_string()),
426                ],
427            )
428            .unwrap();
429        assert_eq!(
430            result,
431            Value::String("2021-06-15T14:00:00+00:00".to_string())
432        );
433
434        // Test truncate to minute
435        let result = registry
436            .call_function(
437                "truncate_time",
438                &[
439                    Value::String("2021-06-15T14:30:45Z".to_string()),
440                    Value::String("minute".to_string()),
441                ],
442            )
443            .unwrap();
444        assert_eq!(
445            result,
446            Value::String("2021-06-15T14:30:00+00:00".to_string())
447        );
448
449        // Test truncate to month
450        let result = registry
451            .call_function(
452                "truncate_time",
453                &[
454                    Value::String("2021-06-15T14:30:45Z".to_string()),
455                    Value::String("month".to_string()),
456                ],
457            )
458            .unwrap();
459        assert_eq!(
460            result,
461            Value::String("2021-06-01T00:00:00+00:00".to_string())
462        );
463
464        // Test truncate to year
465        let result = registry
466            .call_function(
467                "truncate_time",
468                &[
469                    Value::String("2021-06-15T14:30:45Z".to_string()),
470                    Value::String("year".to_string()),
471                ],
472            )
473            .unwrap();
474        assert_eq!(
475            result,
476            Value::String("2021-01-01T00:00:00+00:00".to_string())
477        );
478    }
479
480    #[test]
481    fn test_year_function() {
482        let registry = BuiltinRegistry::new();
483
484        // Test with timestamp
485        let result = registry
486            .call_function("year", &[Value::Int(1609459200)])
487            .unwrap();
488        assert_eq!(result, Value::Int(2021));
489
490        // Test with date string
491        let result = registry
492            .call_function("year", &[Value::String("2021-06-15".to_string())])
493            .unwrap();
494        assert_eq!(result, Value::Int(2021));
495
496        // Test with RFC3339 string
497        let result = registry
498            .call_function("year", &[Value::String("2021-06-15T12:30:45Z".to_string())])
499            .unwrap();
500        assert_eq!(result, Value::Int(2021));
501    }
502
503    #[test]
504    fn test_date_diff_function() {
505        let registry = BuiltinRegistry::new();
506
507        // Test with date strings
508        let result = registry
509            .call_function(
510                "date_diff",
511                &[
512                    Value::String("2023-01-01".to_string()),
513                    Value::String("2023-01-05".to_string()),
514                ],
515            )
516            .unwrap();
517        assert_eq!(result, Value::Int(4));
518
519        // Test with same date
520        let result = registry
521            .call_function(
522                "date_diff",
523                &[
524                    Value::String("2023-01-01".to_string()),
525                    Value::String("2023-01-01".to_string()),
526                ],
527            )
528            .unwrap();
529        assert_eq!(result, Value::Int(0));
530
531        // Test with RFC3339 strings
532        let result = registry
533            .call_function(
534                "date_diff",
535                &[
536                    Value::String("2023-01-01T00:00:00Z".to_string()),
537                    Value::String("2023-01-05T00:00:00Z".to_string()),
538                ],
539            )
540            .unwrap();
541        assert_eq!(result, Value::Int(4));
542
543        // Test with arrays
544        let arr1 = Value::Array(vec![
545            Value::String("2023-01-01".to_string()),
546            Value::String("2023-02-01".to_string()),
547        ]);
548        let arr2 = Value::Array(vec![
549            Value::String("2023-01-05".to_string()),
550            Value::String("2023-02-05".to_string()),
551        ]);
552        let result = registry.call_function("date_diff", &[arr1, arr2]).unwrap();
553        assert_eq!(result, Value::Array(vec![Value::Int(4), Value::Int(4)]));
554    }
555
556    #[test]
557    fn test_gmtime_function() {
558        let registry = BuiltinRegistry::new();
559
560        // Test with timestamp 1609459200 (2021-01-01 00:00:00 UTC)
561        let result = registry
562            .call_function("gmtime", &[Value::Int(1609459200)])
563            .unwrap();
564        if let Value::Object(obj) = result {
565            assert_eq!(obj.get("year"), Some(&Value::Int(2021)));
566            assert_eq!(obj.get("month"), Some(&Value::Int(1)));
567            assert_eq!(obj.get("day"), Some(&Value::Int(1)));
568            assert_eq!(obj.get("hour"), Some(&Value::Int(0)));
569            assert_eq!(obj.get("minute"), Some(&Value::Int(0)));
570            assert_eq!(obj.get("second"), Some(&Value::Int(0)));
571            assert_eq!(obj.get("weekday"), Some(&Value::Int(5))); // Friday
572            assert_eq!(obj.get("yearday"), Some(&Value::Int(1)));
573        } else {
574            panic!("Expected object result");
575        }
576
577        // Test with date string
578        let result = registry
579            .call_function("gmtime", &[Value::String("2021-06-15".to_string())])
580            .unwrap();
581        if let Value::Object(obj) = result {
582            assert_eq!(obj.get("year"), Some(&Value::Int(2021)));
583            assert_eq!(obj.get("month"), Some(&Value::Int(6)));
584            assert_eq!(obj.get("day"), Some(&Value::Int(15)));
585            assert_eq!(obj.get("hour"), Some(&Value::Int(0)));
586            assert_eq!(obj.get("minute"), Some(&Value::Int(0)));
587            assert_eq!(obj.get("second"), Some(&Value::Int(0)));
588        } else {
589            panic!("Expected object result");
590        }
591    }
592
593    #[test]
594    fn test_month_function() {
595        let registry = BuiltinRegistry::new();
596
597        let result = registry
598            .call_function("month", &[Value::String("2021-06-15".to_string())])
599            .unwrap();
600        assert_eq!(result, Value::Int(6));
601    }
602
603    #[test]
604    fn test_day_function() {
605        let registry = BuiltinRegistry::new();
606
607        let result = registry
608            .call_function("day", &[Value::String("2021-06-15".to_string())])
609            .unwrap();
610        assert_eq!(result, Value::Int(15));
611    }
612
613    #[test]
614    fn test_hour_function() {
615        let registry = BuiltinRegistry::new();
616
617        let result = registry
618            .call_function("hour", &[Value::String("2021-06-15T14:30:45Z".to_string())])
619            .unwrap();
620        assert_eq!(result, Value::Int(14));
621    }
622
623    #[test]
624    fn test_minute_function() {
625        let registry = BuiltinRegistry::new();
626
627        let result = registry
628            .call_function(
629                "minute",
630                &[Value::String("2021-06-15T14:30:45Z".to_string())],
631            )
632            .unwrap();
633        assert_eq!(result, Value::Int(30));
634    }
635
636    #[test]
637    fn test_second_function() {
638        let registry = BuiltinRegistry::new();
639
640        let result = registry
641            .call_function(
642                "second",
643                &[Value::String("2021-06-15T14:30:45Z".to_string())],
644            )
645            .unwrap();
646        assert_eq!(result, Value::Int(45));
647    }
648
649    #[test]
650    fn test_tostring_function() {
651        let registry = BuiltinRegistry::new();
652
653        let result = registry
654            .call_function("tostring", &[Value::Int(42)])
655            .unwrap();
656        assert_eq!(result, Value::String("42".to_string()));
657
658        let result = registry
659            .call_function("tostring", &[Value::Float(3.14)])
660            .unwrap();
661        assert_eq!(result, Value::String("3.14".to_string()));
662
663        let result = registry
664            .call_function("tostring", &[Value::Bool(true)])
665            .unwrap();
666        assert_eq!(result, Value::String("true".to_string()));
667    }
668
669    #[test]
670    fn test_snake_case_function() {
671        let registry = BuiltinRegistry::new();
672
673        let result = registry
674            .call_function("snake_case", &[Value::String("CamelCase".to_string())])
675            .unwrap();
676        assert_eq!(result, Value::String("camel_case".to_string()));
677
678        let result = registry
679            .call_function("snake_case", &[Value::String("XMLHttpRequest".to_string())])
680            .unwrap();
681        assert_eq!(result, Value::String("xml_http_request".to_string()));
682    }
683
684    #[test]
685    fn test_lowercase_function() {
686        let registry = BuiltinRegistry::new();
687
688        let result = registry
689            .call_function("lowercase", &[Value::String("HELLO WORLD".to_string())])
690            .unwrap();
691        assert_eq!(result, Value::String("hello world".to_string()));
692
693        let result = registry
694            .call_function("lowercase", &[Value::String("HeLLo WoRlD".to_string())])
695            .unwrap();
696        assert_eq!(result, Value::String("hello world".to_string()));
697    }
698
699    #[test]
700    fn test_camel_case_function() {
701        let registry = BuiltinRegistry::new();
702
703        let result = registry
704            .call_function("camel_case", &[Value::String("snake_case".to_string())])
705            .unwrap();
706        assert_eq!(result, Value::String("snakeCase".to_string()));
707
708        let result = registry
709            .call_function(
710                "camel_case",
711                &[Value::String("xml_http_request".to_string())],
712            )
713            .unwrap();
714        assert_eq!(result, Value::String("xmlHttpRequest".to_string()));
715    }
716
717    #[test]
718    fn test_is_valid_utf8_function() {
719        let registry = BuiltinRegistry::new();
720
721        let result = registry
722            .call_function("is_valid_utf8", &[Value::String("hello world".to_string())])
723            .unwrap();
724        assert_eq!(result, Value::Bool(true));
725
726        let result = registry
727            .call_function("is_valid_utf8", &[Value::String("hello".to_string())])
728            .unwrap();
729        assert_eq!(result, Value::Bool(true));
730    }
731
732    #[test]
733    fn test_to_valid_utf8_function() {
734        let registry = BuiltinRegistry::new();
735
736        let result = registry
737            .call_function("to_valid_utf8", &[Value::String("hello world".to_string())])
738            .unwrap();
739        assert_eq!(result, Value::String("hello world".to_string()));
740
741        let result = registry
742            .call_function("to_valid_utf8", &[Value::String("café".to_string())])
743            .unwrap();
744        assert_eq!(result, Value::String("café".to_string()));
745
746        // Test with array
747        let arr = vec![
748            Value::String("café".to_string()),
749            Value::String("naïve".to_string()),
750        ];
751        let result = registry
752            .call_function("to_valid_utf8", &[Value::Array(arr)])
753            .unwrap();
754        let expected = vec![
755            Value::String("café".to_string()),
756            Value::String("naïve".to_string()),
757        ];
758        assert_eq!(result, Value::Array(expected));
759    }
760
761    #[test]
762    fn test_tabs_to_spaces_function() {
763        let registry = BuiltinRegistry::new();
764
765        // Test with default 4 spaces
766        let result = registry
767            .call_function(
768                "tabs_to_spaces",
769                &[Value::String("hello\tworld".to_string())],
770            )
771            .unwrap();
772        assert_eq!(result, Value::String("hello    world".to_string()));
773
774        // Test with custom 2 spaces
775        let result = registry
776            .call_function(
777                "tabs_to_spaces",
778                &[Value::String("a\tb\tc".to_string()), Value::Int(2)],
779            )
780            .unwrap();
781        assert_eq!(result, Value::String("a  b  c".to_string()));
782
783        // Test with leading tab
784        let result = registry
785            .call_function(
786                "tabs_to_spaces",
787                &[Value::String("\tindented text".to_string())],
788            )
789            .unwrap();
790        assert_eq!(result, Value::String("    indented text".to_string()));
791
792        // Test with multiple tabs
793        let result = registry
794            .call_function(
795                "tabs_to_spaces",
796                &[Value::String("Multiple\ttabs\tin\tone\tline".to_string())],
797            )
798            .unwrap();
799        assert_eq!(
800            result,
801            Value::String("Multiple    tabs    in    one    line".to_string())
802        );
803    }
804
805    #[test]
806    fn test_abs_function() {
807        let registry = BuiltinRegistry::new();
808
809        let result = registry.call_function("abs", &[Value::Int(-42)]).unwrap();
810        assert_eq!(result, Value::Int(42));
811
812        let result = registry
813            .call_function("abs", &[Value::Float(-3.14)])
814            .unwrap();
815        assert_eq!(result, Value::Float(3.14));
816    }
817
818    #[test]
819    fn test_floor_function() {
820        let registry = BuiltinRegistry::new();
821
822        let result = registry
823            .call_function("floor", &[Value::Float(3.7)])
824            .unwrap();
825        assert_eq!(result, Value::Float(3.0));
826
827        let result = registry
828            .call_function("floor", &[Value::Float(-3.7)])
829            .unwrap();
830        assert_eq!(result, Value::Float(-4.0));
831    }
832
833    #[test]
834    fn test_ceil_function() {
835        let registry = BuiltinRegistry::new();
836
837        let result = registry
838            .call_function("ceil", &[Value::Float(3.1)])
839            .unwrap();
840        assert_eq!(result, Value::Float(4.0));
841
842        let result = registry
843            .call_function("ceil", &[Value::Float(-3.1)])
844            .unwrap();
845        assert_eq!(result, Value::Float(-3.0));
846    }
847
848    #[test]
849    fn test_pow_function() {
850        let registry = BuiltinRegistry::new();
851
852        let result = registry
853            .call_function("pow", &[Value::Int(2), Value::Int(3)])
854            .unwrap();
855        assert_eq!(result, Value::Float(8.0));
856
857        let result = registry
858            .call_function("pow", &[Value::Float(2.0), Value::Float(0.5)])
859            .unwrap();
860        assert_eq!(result, Value::Float(std::f64::consts::SQRT_2));
861    }
862
863    #[test]
864    fn test_log10_function() {
865        let registry = BuiltinRegistry::new();
866
867        let result = registry.call_function("log10", &[Value::Int(10)]).unwrap();
868        assert_eq!(result, Value::Float(1.0));
869
870        let result = registry.call_function("log10", &[Value::Int(100)]).unwrap();
871        assert_eq!(result, Value::Float(2.0));
872
873        let result = registry
874            .call_function("log10", &[Value::Float(1000.0)])
875            .unwrap();
876        assert_eq!(result, Value::Float(3.0));
877
878        // Test domain error
879        let result = registry.call_function("log10", &[Value::Int(0)]);
880        assert!(result.is_err());
881
882        let result = registry.call_function("log10", &[Value::Float(-1.0)]);
883        assert!(result.is_err());
884    }
885
886    #[test]
887    fn test_pi_function() {
888        let registry = BuiltinRegistry::new();
889
890        let result = registry.call_function("pi", &[]).unwrap();
891        assert_eq!(result, Value::Float(std::f64::consts::PI));
892    }
893
894    #[test]
895    fn test_sha512_function() {
896        let registry = BuiltinRegistry::new();
897
898        let result = registry
899            .call_function("sha512", &[Value::String("hello".to_string())])
900            .unwrap();
901        if let Value::String(hash) = result {
902            assert_eq!(hash.len(), 128); // SHA512 produces 128 hex characters
903            assert!(hash.chars().all(|c| c.is_ascii_hexdigit()));
904        } else {
905            panic!("Expected string result");
906        }
907    }
908
909    #[test]
910    fn test_sha256_function() {
911        let registry = BuiltinRegistry::new();
912
913        let result = registry
914            .call_function("sha256", &[Value::String("hello".to_string())])
915            .unwrap();
916        if let Value::String(hash) = result {
917            assert_eq!(hash.len(), 64); // SHA256 produces 64 hex characters
918            assert!(hash.chars().all(|c| c.is_ascii_hexdigit()));
919        } else {
920            panic!("Expected string result");
921        }
922    }
923
924    #[test]
925    fn test_sha1_function() {
926        let registry = BuiltinRegistry::new();
927
928        let result = registry
929            .call_function("sha1", &[Value::String("hello".to_string())])
930            .unwrap();
931        if let Value::String(hash) = result {
932            assert_eq!(hash.len(), 40); // SHA1 produces 40 hex characters
933            assert!(hash.chars().all(|c| c.is_ascii_hexdigit()));
934        } else {
935            panic!("Expected string result");
936        }
937    }
938
939    #[test]
940    fn test_md5_function() {
941        let registry = BuiltinRegistry::new();
942
943        let result = registry
944            .call_function("md5", &[Value::String("hello".to_string())])
945            .unwrap();
946        if let Value::String(hash) = result {
947            assert_eq!(hash.len(), 32); // MD5 produces 32 hex characters
948            assert!(hash.chars().all(|c| c.is_ascii_hexdigit()));
949        } else {
950            panic!("Expected string result");
951        }
952    }
953
954    #[test]
955    fn test_base64_encode_decode() {
956        let registry = BuiltinRegistry::new();
957
958        let original = "hello world";
959        let encoded = registry
960            .call_function("base64_encode", &[Value::String(original.to_string())])
961            .unwrap();
962        if let Value::String(enc_str) = encoded {
963            let decoded = registry
964                .call_function("base64_decode", &[Value::String(enc_str)])
965                .unwrap();
966            assert_eq!(decoded, Value::String(original.to_string()));
967        } else {
968            panic!("Expected string result from base64_encode");
969        }
970    }
971
972    #[test]
973    fn test_base32_encode_decode() {
974        let registry = BuiltinRegistry::new();
975
976        let original = "hello world";
977        let encoded = registry
978            .call_function("base32_encode", &[Value::String(original.to_string())])
979            .unwrap();
980        if let Value::String(enc_str) = encoded {
981            let decoded = registry
982                .call_function("base32_decode", &[Value::String(enc_str)])
983                .unwrap();
984            assert_eq!(decoded, Value::String(original.to_string()));
985        } else {
986            panic!("Expected string result from base32_encode");
987        }
988    }
989
990    #[test]
991    fn test_base58_encode_decode() {
992        let registry = BuiltinRegistry::new();
993
994        let original = "hello world";
995        let encoded = registry
996            .call_function("base58_encode", &[Value::String(original.to_string())])
997            .unwrap();
998        if let Value::String(enc_str) = encoded {
999            let decoded = registry
1000                .call_function("base58_decode", &[Value::String(enc_str)])
1001                .unwrap();
1002            assert_eq!(decoded, Value::String(original.to_string()));
1003        } else {
1004            panic!("Expected string result from base58_encode");
1005        }
1006    }
1007
1008    #[test]
1009    fn test_columns_function() {
1010        let registry = BuiltinRegistry::new();
1011        let df = create_test_dataframe();
1012
1013        let result = registry
1014            .call_function("columns", &[Value::DataFrame(df)])
1015            .unwrap();
1016        if let Value::Array(cols) = result {
1017            assert_eq!(cols.len(), 3);
1018            assert!(cols.contains(&Value::String("name".to_string())));
1019            assert!(cols.contains(&Value::String("age".to_string())));
1020            assert!(cols.contains(&Value::String("score".to_string())));
1021        } else {
1022            panic!("Expected array result");
1023        }
1024    }
1025
1026    #[test]
1027    fn test_shape_function() {
1028        let registry = BuiltinRegistry::new();
1029        let df = create_test_dataframe();
1030
1031        let result = registry
1032            .call_function("shape", &[Value::DataFrame(df)])
1033            .unwrap();
1034        if let Value::Array(shape) = result {
1035            assert_eq!(shape.len(), 2);
1036            assert_eq!(shape[0], Value::Int(3)); // 3 rows
1037            assert_eq!(shape[1], Value::Int(3)); // 3 columns
1038        } else {
1039            panic!("Expected array result");
1040        }
1041    }
1042
1043    #[test]
1044    fn test_mean_function() {
1045        let registry = BuiltinRegistry::new();
1046        let df = create_test_dataframe();
1047
1048        let result = registry
1049            .call_function("mean", &[Value::DataFrame(df)])
1050            .unwrap();
1051        if let Value::Object(means) = result {
1052            assert!(means.contains_key("age"));
1053            assert!(means.contains_key("score"));
1054            // age mean should be (25+30+35)/3 = 30
1055            if let Some(Value::Float(age_mean)) = means.get("age") {
1056                assert!((age_mean - 30.0).abs() < 0.001);
1057            } else {
1058                panic!("Expected float for age mean");
1059            }
1060        } else {
1061            panic!("Expected object result");
1062        }
1063    }
1064
1065    #[test]
1066    fn test_median_function() {
1067        let registry = BuiltinRegistry::new();
1068        let df = create_test_dataframe();
1069
1070        let result = registry
1071            .call_function("median", &[Value::DataFrame(df)])
1072            .unwrap();
1073        if let Value::Object(medians) = result {
1074            assert!(medians.contains_key("age"));
1075            assert!(medians.contains_key("score"));
1076            // age median should be 30 (sorted: 25, 30, 35)
1077            if let Some(Value::Float(age_median)) = medians.get("age") {
1078                assert!((age_median - 30.0).abs() < 0.001);
1079            } else {
1080                panic!("Expected float for age median");
1081            }
1082        } else {
1083            panic!("Expected object result");
1084        }
1085    }
1086
1087    #[test]
1088    fn test_count_function() {
1089        let registry = BuiltinRegistry::new();
1090        let df = create_test_dataframe();
1091
1092        // Test DataFrame
1093        let result = registry
1094            .call_function("count", &[Value::DataFrame(df.clone())])
1095            .unwrap();
1096        assert_eq!(result, Value::Int(3));
1097
1098        // Test Array
1099        let arr = Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
1100        let result = registry.call_function("count", &[arr]).unwrap();
1101        assert_eq!(result, Value::Int(3));
1102
1103        // Test empty Array
1104        let empty_arr = Value::Array(vec![]);
1105        let result = registry.call_function("count", &[empty_arr]).unwrap();
1106        assert_eq!(result, Value::Int(0));
1107
1108        // Test Object
1109        let mut obj = std::collections::HashMap::new();
1110        obj.insert("a".to_string(), Value::Int(1));
1111        obj.insert("b".to_string(), Value::Int(2));
1112        let obj_val = Value::Object(obj);
1113        let result = registry.call_function("count", &[obj_val]).unwrap();
1114        assert_eq!(result, Value::Int(2));
1115
1116        // Test Series
1117        let series = Series::new(PlSmallStr::from("test"), vec![1i64, 2, 3]);
1118        let result = registry
1119            .call_function("count", &[Value::Series(series)])
1120            .unwrap();
1121        assert_eq!(result, Value::Int(3));
1122
1123        // Test String
1124        let string_val = Value::String("hello".to_string());
1125        let result = registry.call_function("count", &[string_val]).unwrap();
1126        assert_eq!(result, Value::Int(5));
1127
1128        // Test String with unicode
1129        let unicode_string = Value::String("héllo".to_string());
1130        let result = registry.call_function("count", &[unicode_string]).unwrap();
1131        assert_eq!(result, Value::Int(5)); // "héllo" has 5 characters
1132
1133        // Test other values (should return 1)
1134        let int_val = Value::Int(42);
1135        let result = registry.call_function("count", &[int_val]).unwrap();
1136        assert_eq!(result, Value::Int(1));
1137
1138        let float_val = Value::Float(3.14);
1139        let result = registry.call_function("count", &[float_val]).unwrap();
1140        assert_eq!(result, Value::Int(1));
1141
1142        let bool_val = Value::Bool(true);
1143        let result = registry.call_function("count", &[bool_val]).unwrap();
1144        assert_eq!(result, Value::Int(1));
1145
1146        let null_val = Value::Null;
1147        let result = registry.call_function("count", &[null_val]).unwrap();
1148        assert_eq!(result, Value::Int(1));
1149    }
1150
1151    #[test]
1152    fn test_url_parse_function() {
1153        let registry = BuiltinRegistry::new();
1154
1155        let result = registry
1156            .call_function(
1157                "url_parse",
1158                &[Value::String(
1159                    "https://www.example.com:8080/path?query=value#fragment".to_string(),
1160                )],
1161            )
1162            .unwrap();
1163        if let Value::Object(parsed) = result {
1164            assert_eq!(
1165                parsed.get("scheme"),
1166                Some(&Value::String("https".to_string()))
1167            );
1168            assert_eq!(
1169                parsed.get("host"),
1170                Some(&Value::String("www.example.com".to_string()))
1171            );
1172            assert_eq!(parsed.get("port"), Some(&Value::Int(8080)));
1173            assert_eq!(
1174                parsed.get("path"),
1175                Some(&Value::String("/path".to_string()))
1176            );
1177            assert_eq!(
1178                parsed.get("query"),
1179                Some(&Value::String("query=value".to_string()))
1180            );
1181            assert_eq!(
1182                parsed.get("fragment"),
1183                Some(&Value::String("fragment".to_string()))
1184            );
1185        } else {
1186            panic!("Expected object result");
1187        }
1188    }
1189
1190    #[test]
1191    fn test_url_extract_domain_function() {
1192        let registry = BuiltinRegistry::new();
1193
1194        let result = registry
1195            .call_function(
1196                "url_extract_domain",
1197                &[Value::String("https://www.example.com/path".to_string())],
1198            )
1199            .unwrap();
1200        assert_eq!(result, Value::String("www.example.com".to_string()));
1201    }
1202
1203    #[test]
1204    fn test_range_function() {
1205        let registry = BuiltinRegistry::new();
1206
1207        let result = registry.call_function("range", &[Value::Int(5)]).unwrap();
1208        if let Value::Array(arr) = result {
1209            assert_eq!(arr.len(), 5);
1210            assert_eq!(arr[0], Value::Int(0));
1211            assert_eq!(arr[4], Value::Int(4));
1212        } else {
1213            panic!("Expected array result");
1214        }
1215
1216        let result = registry
1217            .call_function("range", &[Value::Int(1), Value::Int(5)])
1218            .unwrap();
1219        if let Value::Array(arr) = result {
1220            assert_eq!(arr.len(), 4);
1221            assert_eq!(arr[0], Value::Int(1));
1222            assert_eq!(arr[3], Value::Int(4));
1223        } else {
1224            panic!("Expected array result");
1225        }
1226    }
1227
1228    #[test]
1229    fn test_select_function() {
1230        let registry = BuiltinRegistry::new();
1231
1232        let arr = Value::Array(vec![
1233            Value::String("a".to_string()),
1234            Value::String("b".to_string()),
1235            Value::String("c".to_string()),
1236        ]);
1237        let result = registry
1238            .call_function("select", &[arr, Value::Int(0), Value::Int(2)])
1239            .unwrap();
1240        if let Value::Array(selected) = result {
1241            assert_eq!(selected.len(), 2);
1242            assert_eq!(selected[0], Value::String("a".to_string()));
1243            assert_eq!(selected[1], Value::String("c".to_string()));
1244        } else {
1245            panic!("Expected array result");
1246        }
1247    }
1248
1249    #[test]
1250    fn test_del_function() {
1251        let registry = BuiltinRegistry::new();
1252
1253        let mut obj = HashMap::new();
1254        obj.insert("a".to_string(), Value::Int(1));
1255        obj.insert("b".to_string(), Value::Int(2));
1256        obj.insert("c".to_string(), Value::Int(3));
1257
1258        let result = registry
1259            .call_function("del", &[Value::Object(obj), Value::String("b".to_string())])
1260            .unwrap();
1261        if let Value::Object(del_obj) = result {
1262            assert!(!del_obj.contains_key("b"));
1263            assert_eq!(del_obj.get("a"), Some(&Value::Int(1)));
1264            assert_eq!(del_obj.get("c"), Some(&Value::Int(3)));
1265        } else {
1266            panic!("Expected object result");
1267        }
1268    }
1269
1270    #[test]
1271    fn test_fromjson_function() {
1272        let registry = BuiltinRegistry::new();
1273
1274        let json_str = r#"{"name": "Alice", "age": 30, "active": true}"#;
1275        let result = registry
1276            .call_function("fromjson", &[Value::String(json_str.to_string())])
1277            .unwrap();
1278        if let Value::Object(obj) = result {
1279            assert_eq!(obj.get("name"), Some(&Value::String("Alice".to_string())));
1280            assert_eq!(obj.get("age"), Some(&Value::Int(30)));
1281            assert_eq!(obj.get("active"), Some(&Value::Bool(true)));
1282        } else {
1283            panic!("Expected object result");
1284        }
1285    }
1286
1287    #[test]
1288    fn test_group_concat_function() {
1289        let registry = BuiltinRegistry::new();
1290
1291        let arr = Value::Array(vec![
1292            Value::String("a".to_string()),
1293            Value::String("b".to_string()),
1294            Value::String("c".to_string()),
1295        ]);
1296        let result = registry
1297            .call_function("group_concat", std::slice::from_ref(&arr))
1298            .unwrap();
1299        assert_eq!(result, Value::String("a,b,c".to_string()));
1300
1301        let result = registry
1302            .call_function("group_concat", &[arr, Value::String(";".to_string())])
1303            .unwrap();
1304        assert_eq!(result, Value::String("a;b;c".to_string()));
1305    }
1306
1307    #[test]
1308    fn test_registry_has_function() {
1309        let registry = BuiltinRegistry::new();
1310
1311        assert!(registry.has_function("year"));
1312        assert!(registry.has_function("month"));
1313        assert!(registry.has_function("tostring"));
1314        assert!(registry.has_function("abs"));
1315        assert!(registry.has_function("sha512"));
1316        assert!(registry.has_function("has"));
1317        assert!(registry.has_function("rstrip"));
1318        assert!(registry.has_function("url_extract_path"));
1319        assert!(registry.has_function("uppercase"));
1320        assert!(registry.has_function("toupper"));
1321        assert!(registry.has_function("iif"));
1322        assert!(registry.has_function("time_series_range"));
1323        assert!(!registry.has_function("nonexistent"));
1324    }
1325
1326    #[test]
1327    fn test_builtin_has() {
1328        let registry = BuiltinRegistry::new();
1329
1330        let obj = Value::Object(
1331            vec![
1332                ("name".to_string(), Value::String("Alice".to_string())),
1333                ("age".to_string(), Value::Int(30)),
1334                ("city".to_string(), Value::String("New York".to_string())),
1335            ]
1336            .into_iter()
1337            .collect(),
1338        );
1339
1340        // Test has with existing key
1341        let result = registry
1342            .call_function("has", &[obj.clone(), Value::String("city".to_string())])
1343            .unwrap();
1344        assert_eq!(result, Value::Bool(true));
1345
1346        // Test has with non-existing key
1347        let result = registry
1348            .call_function("has", &[obj.clone(), Value::String("country".to_string())])
1349            .unwrap();
1350        assert_eq!(result, Value::Bool(false));
1351
1352        // Test has with non-object
1353        let result = registry
1354            .call_function(
1355                "has",
1356                &[
1357                    Value::String("test".to_string()),
1358                    Value::String("key".to_string()),
1359                ],
1360            )
1361            .unwrap();
1362        assert_eq!(result, Value::Bool(false));
1363    }
1364
1365    #[test]
1366    fn test_registry_function_count() {
1367        let registry = BuiltinRegistry::new();
1368
1369        // Should have many functions registered
1370        assert!(registry.function_count() > 50);
1371    }
1372
1373    #[test]
1374    fn test_registry_function_names() {
1375        let registry = BuiltinRegistry::new();
1376
1377        let names = registry.function_names();
1378        assert!(names.contains(&"year".to_string()));
1379        assert!(names.contains(&"month".to_string()));
1380        assert!(names.contains(&"tostring".to_string()));
1381        assert!(names.contains(&"strptime".to_string()));
1382        assert!(names.contains(&"now".to_string()));
1383        assert!(names.contains(&"url_extract_port".to_string()));
1384    }
1385
1386    #[test]
1387    fn test_registry_strptime() {
1388        let registry = BuiltinRegistry::new();
1389
1390        // Test strptime via registry
1391        let result = registry
1392            .call_function(
1393                "strptime",
1394                &[
1395                    Value::String("2021-01-01 00:00:00".to_string()),
1396                    Value::String("%Y-%m-%d %H:%M:%S".to_string()),
1397                ],
1398            )
1399            .unwrap();
1400        assert!(matches!(result, Value::Int(_)));
1401    }
1402
1403    #[test]
1404    fn test_builtin_start_of_week() {
1405        let registry = BuiltinRegistry::new();
1406
1407        // Test with string date
1408        let result = registry
1409            .call_function("start_of_week", &[Value::String("2023-10-02".to_string())])
1410            .unwrap();
1411        // 2023-10-02 is Monday, so start of week is 2023-10-02 00:00:00 UTC
1412        assert_eq!(result, Value::Int(1696204800));
1413
1414        // Test with Tuesday
1415        let result = registry
1416            .call_function("start_of_week", &[Value::String("2023-10-03".to_string())])
1417            .unwrap();
1418        assert_eq!(result, Value::Int(1696204800)); // Still Monday
1419
1420        // Test with Sunday
1421        let result = registry
1422            .call_function("start_of_week", &[Value::String("2023-10-08".to_string())])
1423            .unwrap();
1424        assert_eq!(result, Value::Int(1696204800)); // Monday
1425
1426        // Test with sunday start day
1427        let result = registry
1428            .call_function(
1429                "start_of_week",
1430                &[
1431                    Value::String("2023-10-02".to_string()),
1432                    Value::String("sunday".to_string()),
1433                ],
1434            )
1435            .unwrap();
1436        // 2023-10-02 is Monday, start of week sunday is 2023-09-24? No.
1437        // 2023-10-02 Monday, previous Sunday is 2023-10-01
1438        // Timestamp for 2023-10-01 00:00:00 UTC
1439        assert_eq!(result, Value::Int(1696118400));
1440
1441        // Test with array
1442        let arr = Value::Array(vec![
1443            Value::String("2023-10-02".to_string()),
1444            Value::String("2023-10-03".to_string()),
1445        ]);
1446        let result = registry.call_function("start_of_week", &[arr]).unwrap();
1447        match result {
1448            Value::Array(res) => {
1449                assert_eq!(res.len(), 2);
1450                assert_eq!(res[0], Value::Int(1696204800));
1451                assert_eq!(res[1], Value::Int(1696204800));
1452            }
1453            _ => panic!("Expected array"),
1454        }
1455    }
1456
1457    #[test]
1458    fn test_builtin_length_with_dataframe() {
1459        let registry = BuiltinRegistry::new();
1460        let df = DataFrame::new(vec![
1461            Series::new(PlSmallStr::from("name"), &["Alice", "Bob", "Charlie"]).into(),
1462            Series::new(PlSmallStr::from("age"), &[25, 30, 35]).into(),
1463        ])
1464        .unwrap();
1465        let df_value = Value::DataFrame(df);
1466
1467        let result = registry.call_function("length", &[df_value]).unwrap();
1468        assert_eq!(result, Value::Int(3));
1469    }
1470
1471    #[test]
1472    fn test_builtin_select_single_arg() {
1473        // Test with truthy value
1474        let result = builtin_select(&[Value::Bool(true)]).unwrap();
1475        assert_eq!(result, Value::Bool(true));
1476
1477        // Test with falsy value
1478        let result = builtin_select(&[Value::Bool(false)]).unwrap();
1479        assert_eq!(result, Value::Null);
1480
1481        // Test with null
1482        let result = builtin_select(&[Value::Null]).unwrap();
1483        assert_eq!(result, Value::Null);
1484
1485        // Test with string
1486        let result = builtin_select(&[Value::String("test".to_string())]).unwrap();
1487        assert_eq!(result, Value::String("test".to_string()));
1488
1489        // Test with empty string
1490        let result = builtin_select(&[Value::String("".to_string())]).unwrap();
1491        assert_eq!(result, Value::Null);
1492
1493        // Test with int
1494        let result = builtin_select(&[Value::Int(42)]).unwrap();
1495        assert_eq!(result, Value::Int(42));
1496
1497        // Test with zero
1498        let result = builtin_select(&[Value::Int(0)]).unwrap();
1499        assert_eq!(result, Value::Null);
1500    }
1501
1502    #[test]
1503    fn test_builtin_select_two_args() {
1504        // Test with truthy condition
1505        let result =
1506            builtin_select(&[Value::String("test".to_string()), Value::Bool(true)]).unwrap();
1507        assert_eq!(result, Value::String("test".to_string()));
1508
1509        // Test with falsy condition
1510        let result =
1511            builtin_select(&[Value::String("test".to_string()), Value::Bool(false)]).unwrap();
1512        assert_eq!(result, Value::Null);
1513
1514        // Test with null condition
1515        let result = builtin_select(&[Value::String("test".to_string()), Value::Null]).unwrap();
1516        assert_eq!(result, Value::Null);
1517
1518        // Test with array input
1519        let arr = Value::Array(vec![Value::Int(1), Value::Int(2)]);
1520        let result = builtin_select(&[arr, Value::Bool(true)]).unwrap();
1521        assert_eq!(result, Value::Array(vec![Value::Int(1), Value::Int(2)]));
1522    }
1523
1524    #[test]
1525    fn test_builtin_select_array_with_mask() {
1526        let arr = vec![Value::Int(1), Value::Int(2), Value::Int(3)];
1527        let mask = vec![Value::Bool(true), Value::Bool(false), Value::Bool(true)];
1528        let result = builtin_select(&[Value::Array(arr), Value::Array(mask)]).unwrap();
1529        match result {
1530            Value::Array(filtered) => {
1531                assert_eq!(filtered.len(), 2);
1532                assert_eq!(filtered[0], Value::Int(1));
1533                assert_eq!(filtered[1], Value::Int(3));
1534            }
1535            _ => panic!("Expected Array"),
1536        }
1537    }
1538
1539    #[test]
1540    fn test_builtin_select_dataframe_with_series() {
1541        let name = PlSmallStr::from("name");
1542        let age: PlSmallStr = "age".into();
1543        let mask: PlSmallStr = "mask".into();
1544        let df = DataFrame::new(vec![
1545            Series::new(
1546                name,
1547                vec![
1548                    "Alice".to_string(),
1549                    "Bob".to_string(),
1550                    "Charlie".to_string(),
1551                ],
1552            )
1553            .into(),
1554            Series::new(age, vec![25, 30, 35]).into(),
1555        ])
1556        .unwrap();
1557        let mask_series = Series::new(mask, vec![true, false, true]);
1558        let result =
1559            builtin_select(&[Value::DataFrame(df.clone()), Value::Series(mask_series)]).unwrap();
1560        match result {
1561            Value::DataFrame(filtered_df) => {
1562                assert_eq!(filtered_df.height(), 2);
1563                let names = filtered_df.column("name").unwrap();
1564                assert_eq!(names.len(), 2);
1565                if let Ok(AnyValue::String(name1)) = names.get(0) {
1566                    assert_eq!(name1, "Alice");
1567                }
1568                if let Ok(AnyValue::String(name2)) = names.get(1) {
1569                    assert_eq!(name2, "Charlie");
1570                }
1571            }
1572            _ => panic!("Expected DataFrame"),
1573        }
1574    }
1575
1576    #[test]
1577    fn test_builtin_select_series_with_series() {
1578        let series = Series::new(PlSmallStr::from("values"), vec![1, 2, 3, 4]);
1579        let mask_series = Series::new(PlSmallStr::from("mask"), vec![true, false, true, false]);
1580        let result = builtin_select(&[Value::Series(series), Value::Series(mask_series)]).unwrap();
1581        match result {
1582            Value::Series(filtered_series) => {
1583                assert_eq!(filtered_series.len(), 2);
1584                if let Ok(AnyValue::Int64(val1)) = filtered_series.get(0) {
1585                    assert_eq!(val1, 1);
1586                }
1587                if let Ok(AnyValue::Int64(val2)) = filtered_series.get(1) {
1588                    assert_eq!(val2, 3);
1589                }
1590            }
1591            _ => panic!("Expected Series"),
1592        }
1593    }
1594
1595    #[test]
1596    fn test_builtin_select_single_arg_extended() {
1597        // Test with object
1598        let mut obj = std::collections::HashMap::new();
1599        obj.insert("key".to_string(), Value::String("value".to_string()));
1600        let result = builtin_select(&[Value::Object(obj.clone())]).unwrap();
1601        assert_eq!(result, Value::Object(obj));
1602
1603        // Test with array
1604        let arr = Value::Array(vec![Value::Int(1), Value::Int(2)]);
1605        let result = builtin_select(std::slice::from_ref(&arr)).unwrap();
1606        assert_eq!(result, arr);
1607
1608        // Test with float
1609        let result = builtin_select(&[Value::Float(std::f64::consts::PI)]).unwrap();
1610        assert_eq!(result, Value::Float(std::f64::consts::PI));
1611
1612        // Test with negative int
1613        let result = builtin_select(&[Value::Int(-1)]).unwrap();
1614        assert_eq!(result, Value::Int(-1));
1615    }
1616
1617    #[test]
1618    fn test_builtin_select_two_args_extended() {
1619        use polars::prelude::*;
1620        // Test with object input
1621        let mut obj = std::collections::HashMap::new();
1622        obj.insert("key".to_string(), Value::String("value".to_string()));
1623        let result = builtin_select(&[Value::Object(obj.clone()), Value::Bool(true)]).unwrap();
1624        assert_eq!(result, Value::Object(obj.clone()));
1625
1626        let result = builtin_select(&[Value::Object(obj), Value::Bool(false)]).unwrap();
1627        assert_eq!(result, Value::Null);
1628
1629        // Test with DataFrame input
1630        let df = DataFrame::new(vec![
1631            Series::new(PlSmallStr::from("name"), &["Alice"]).into(),
1632            Series::new(PlSmallStr::from("age"), &[25]).into(),
1633        ])
1634        .unwrap();
1635        let result = builtin_select(&[Value::DataFrame(df.clone()), Value::Bool(true)]).unwrap();
1636        if let Value::DataFrame(result_df) = result {
1637            assert_eq!(result_df.height(), df.height());
1638            assert_eq!(result_df.width(), df.width());
1639        } else {
1640            panic!("Expected DataFrame result");
1641        }
1642
1643        let result = builtin_select(&[Value::DataFrame(df), Value::Bool(false)]).unwrap();
1644        assert_eq!(result, Value::Null);
1645
1646        // Test with Series input
1647        let series = Series::new(PlSmallStr::from("values"), vec![1, 2, 3]);
1648        let result = builtin_select(&[Value::Series(series.clone()), Value::Bool(true)]).unwrap();
1649        if let Value::Series(result_series) = result {
1650            assert_eq!(result_series.name(), series.name());
1651            assert_eq!(result_series.len(), series.len());
1652        } else {
1653            panic!("Expected Series result");
1654        }
1655
1656        let result = builtin_select(&[Value::Series(series), Value::Bool(false)]).unwrap();
1657        assert_eq!(result, Value::Null);
1658    }
1659
1660    #[test]
1661    fn test_builtin_select_mask_errors() {
1662        // Mismatched lengths for array mask
1663        let arr = Value::Array(vec![Value::Int(1)]);
1664        let mask = Value::Array(vec![Value::Bool(true), Value::Bool(false)]);
1665        let result = builtin_select(&[arr, mask]);
1666        assert!(result.is_err());
1667        assert!(result.unwrap_err().to_string().contains("same length"));
1668
1669        // Mismatched lengths for DataFrame mask series
1670        let df = DataFrame::new(vec![Column::new(PlSmallStr::from("a"), vec![1])]).unwrap();
1671        let mask_series = Series::new(PlSmallStr::from("mask"), vec![true, false]);
1672        let result = builtin_select(&[Value::DataFrame(df), Value::Series(mask_series)]);
1673        assert!(result.is_err());
1674        assert!(result.unwrap_err().to_string().contains("same length"));
1675
1676        // Mismatched lengths for Series mask series
1677        let series = Series::new(PlSmallStr::from("values"), vec![1]);
1678        let mask_series = Series::new(PlSmallStr::from("mask"), vec![true, false]);
1679        let result = builtin_select(&[Value::Series(series), Value::Series(mask_series)]);
1680        assert!(result.is_err());
1681        assert!(result.unwrap_err().to_string().contains("same length"));
1682
1683        // Mask series with non-booleans
1684        let series = Series::new(PlSmallStr::from("values"), vec![1, 2]);
1685        let mask_series = Series::new(
1686            PlSmallStr::from("mask"),
1687            vec![AnyValue::Int64(1), AnyValue::Int64(0)],
1688        ); // Non-booleans
1689        let result = builtin_select(&[Value::Series(series), Value::Series(mask_series)]);
1690        assert!(result.is_err());
1691        assert!(result
1692            .unwrap_err()
1693            .to_string()
1694            .contains("must contain booleans"));
1695    }
1696
1697    #[test]
1698    fn test_builtin_select_invalid_args() {
1699        // Empty args
1700        let result = builtin_select(&[]);
1701        assert!(result.is_err());
1702
1703        // Too many args
1704        let result = builtin_select(&[Value::Int(1), Value::Int(2), Value::Int(3)]);
1705        assert!(result.is_err());
1706    }
1707
1708    #[test]
1709    fn test_builtin_filter_array() {
1710        let arr = Value::Array(vec![
1711            Value::Int(1),
1712            Value::Int(2),
1713            Value::Int(1),
1714            Value::Int(3),
1715        ]);
1716        let filter_value = Value::Int(1);
1717        let result = builtin_filter(&[arr, filter_value]).unwrap();
1718        match result {
1719            Value::Array(filtered) => {
1720                assert_eq!(filtered.len(), 2);
1721                assert_eq!(filtered[0], Value::Int(1));
1722                assert_eq!(filtered[1], Value::Int(1));
1723            }
1724            _ => panic!("Expected Array"),
1725        }
1726    }
1727
1728    #[test]
1729    fn test_builtin_filter_dataframe() {
1730        let df = DataFrame::new(vec![
1731            Series::new(PlSmallStr::from("col1"), vec![1, 2, 1, 3]).into(),
1732            Series::new(PlSmallStr::from("col2"), vec!["a", "b", "c", "d"]).into(),
1733        ])
1734        .unwrap();
1735        let df_value = Value::DataFrame(df);
1736        let filter_value = Value::Int(1);
1737        let result = builtin_filter(&[df_value, filter_value]).unwrap();
1738        match result {
1739            Value::DataFrame(filtered_df) => {
1740                assert_eq!(filtered_df.height(), 2);
1741                let col1 = filtered_df.column("col1").unwrap();
1742                if let Ok(AnyValue::Int64(val1)) = col1.get(0) {
1743                    assert_eq!(val1, 1);
1744                }
1745                if let Ok(AnyValue::Int64(val2)) = col1.get(1) {
1746                    assert_eq!(val2, 1);
1747                }
1748            }
1749            _ => panic!("Expected DataFrame"),
1750        }
1751    }
1752
1753    #[test]
1754    fn test_builtin_filter_series() {
1755        let series = Series::new(
1756            <&str as Into<PlSmallStr>>::into("values"),
1757            vec![1i32, 2, 1, 3],
1758        );
1759        let series_value = Value::Series(series);
1760        let filter_value = Value::Int(1);
1761        let result = builtin_filter(&[series_value, filter_value]).unwrap();
1762        match result {
1763            Value::Series(filtered_series) => {
1764                assert_eq!(filtered_series.len(), 2);
1765                if let Ok(AnyValue::Int64(val1)) = filtered_series.get(0) {
1766                    assert_eq!(val1, 1);
1767                }
1768                if let Ok(AnyValue::Int64(val2)) = filtered_series.get(1) {
1769                    assert_eq!(val2, 1);
1770                }
1771            }
1772            _ => panic!("Expected Series"),
1773        }
1774    }
1775
1776    #[test]
1777    fn test_builtin_filter_no_matches() {
1778        let arr = Value::Array(vec![Value::Int(1), Value::Int(2)]);
1779        let filter_value = Value::Int(3);
1780        let result = builtin_filter(&[arr, filter_value]).unwrap();
1781        match result {
1782            Value::Array(filtered) => {
1783                assert_eq!(filtered.len(), 0);
1784            }
1785            _ => panic!("Expected Array"),
1786        }
1787    }
1788
1789    #[test]
1790    fn test_builtin_filter_invalid_args() {
1791        // Too few args
1792        let result = builtin_filter(&[Value::Array(vec![])]);
1793        assert!(result.is_err());
1794
1795        // Too many args
1796        let result = builtin_filter(&[Value::Array(vec![]), Value::Int(1), Value::Int(2)]);
1797        assert!(result.is_err());
1798    }
1799
1800    #[test]
1801    fn test_builtin_humanize_number() {
1802        let registry = BuiltinRegistry::new();
1803
1804        // Test number formatting
1805        let result = registry
1806            .call_function(
1807                "humanize",
1808                &[Value::Int(1234567), Value::String("number".to_string())],
1809            )
1810            .unwrap();
1811        assert_eq!(result, Value::String("1,234,567".to_string()));
1812
1813        // Test currency formatting
1814        let result = registry
1815            .call_function(
1816                "humanize",
1817                &[Value::Int(123456), Value::String("currency".to_string())],
1818            )
1819            .unwrap();
1820        assert_eq!(result, Value::String("$1,234.56".to_string()));
1821
1822        // Test bytes formatting
1823        let result = registry
1824            .call_function(
1825                "humanize",
1826                &[Value::Int(1048576), Value::String("bytes".to_string())],
1827            )
1828            .unwrap();
1829        assert_eq!(result, Value::String("1.0 MB".to_string()));
1830
1831        // Test percentage formatting
1832        let result = registry
1833            .call_function(
1834                "humanize",
1835                &[Value::Float(0.85), Value::String("percentage".to_string())],
1836            )
1837            .unwrap();
1838        assert_eq!(result, Value::String("85.0%".to_string()));
1839
1840        // Test date formatting
1841        let result = registry
1842            .call_function(
1843                "humanize",
1844                &[
1845                    Value::String("2023-12-25".to_string()),
1846                    Value::String("date".to_string()),
1847                ],
1848            )
1849            .unwrap();
1850        assert_eq!(result, Value::String("December 25, 2023".to_string()));
1851    }
1852
1853    #[test]
1854    fn test_builtin_humanize_auto() {
1855        let registry = BuiltinRegistry::new();
1856
1857        // Test auto number formatting
1858        let result = registry
1859            .call_function("humanize", &[Value::Int(1234567)])
1860            .unwrap();
1861        assert_eq!(result, Value::String("1,234,567".to_string()));
1862
1863        // Test auto bytes detection
1864        let result = registry
1865            .call_function("humanize", &[Value::Int(1048576)])
1866            .unwrap();
1867        assert_eq!(result, Value::String("1.0 MB".to_string()));
1868
1869        // Test auto date detection
1870        let result = registry
1871            .call_function("humanize", &[Value::String("2023-12-25".to_string())])
1872            .unwrap();
1873        assert_eq!(result, Value::String("December 25, 2023".to_string()));
1874    }
1875
1876    #[test]
1877    fn test_builtin_humanize_invalid_args() {
1878        let registry = BuiltinRegistry::new();
1879
1880        // Test wrong number of arguments
1881        let result = registry.call_function("humanize", &[]);
1882        assert!(result.is_err());
1883
1884        let result = registry.call_function(
1885            "humanize",
1886            &[
1887                Value::Int(1),
1888                Value::String("number".to_string()),
1889                Value::Int(2),
1890            ],
1891        );
1892        assert!(result.is_err());
1893
1894        // Test invalid format
1895        let result = registry.call_function(
1896            "humanize",
1897            &[Value::Int(1), Value::String("invalid".to_string())],
1898        );
1899        assert!(result.is_err());
1900        assert!(result.unwrap_err().to_string().contains("unknown format"));
1901    }
1902
1903    #[test]
1904    fn test_builtin_tan() {
1905        let registry = BuiltinRegistry::new();
1906
1907        // Test tan(0) = 0
1908        let result = registry.call_function("tan", &[Value::Int(0)]).unwrap();
1909        if let Value::Float(val) = result {
1910            assert!((val - 0.0).abs() < 1e-10);
1911        } else {
1912            panic!("Expected float result");
1913        }
1914
1915        // Test tan(π/4) ≈ 1.0 (45 degrees in radians)
1916        let pi_over_4 = std::f64::consts::PI / 4.0;
1917        let result = registry
1918            .call_function("tan", &[Value::Float(pi_over_4)])
1919            .unwrap();
1920        if let Value::Float(val) = result {
1921            assert!((val - 1.0).abs() < 1e-10);
1922        } else {
1923            panic!("Expected float result");
1924        }
1925
1926        // Test tan(π/2) should be very large (approaches infinity)
1927        let pi_over_2 = std::f64::consts::PI / 2.0;
1928        let result = registry
1929            .call_function("tan", &[Value::Float(pi_over_2)])
1930            .unwrap();
1931        if let Value::Float(val) = result {
1932            assert!(val.abs() > 1e10); // Very large value (positive or negative)
1933        } else {
1934            panic!("Expected float result");
1935        }
1936
1937        // Test tan with float (30 degrees in radians)
1938        let angle_radians = 30.0_f64.to_radians();
1939        let result = registry
1940            .call_function("tan", &[Value::Float(angle_radians)])
1941            .unwrap();
1942        let expected = angle_radians.tan();
1943        if let Value::Float(val) = result {
1944            assert!((val - expected).abs() < 1e-10);
1945        } else {
1946            panic!("Expected float result");
1947        }
1948
1949        // Test tan with array
1950        let angle_radians = (45.0_f64).to_radians();
1951        let result = registry
1952            .call_function(
1953                "tan",
1954                &[Value::Array(vec![
1955                    Value::Int(0),
1956                    Value::Float(angle_radians),
1957                ])],
1958            )
1959            .unwrap();
1960        if let Value::Array(arr) = result {
1961            assert_eq!(arr.len(), 2);
1962            if let Value::Float(val0) = &arr[0] {
1963                assert!((val0 - 0.0).abs() < 1e-10);
1964            } else {
1965                panic!("Expected float in array");
1966            }
1967            if let Value::Float(val1) = &arr[1] {
1968                assert!((val1 - 1.0).abs() < 1e-10);
1969            } else {
1970                panic!("Expected float in array");
1971            }
1972        } else {
1973            panic!("Expected array result");
1974        }
1975    }
1976
1977    #[test]
1978    fn test_builtin_tan_invalid_args() {
1979        let registry = BuiltinRegistry::new();
1980
1981        // Test wrong number of arguments
1982        let result = registry.call_function("tan", &[]);
1983        assert!(result.is_err());
1984        assert!(result
1985            .unwrap_err()
1986            .to_string()
1987            .contains("expects 1 argument"));
1988
1989        let result = registry.call_function("tan", &[Value::Int(1), Value::Int(2)]);
1990        assert!(result.is_err());
1991        assert!(result
1992            .unwrap_err()
1993            .to_string()
1994            .contains("expects 1 argument"));
1995
1996        // Test with non-numeric value
1997        let result = registry.call_function("tan", &[Value::String("not a number".to_string())]);
1998        assert!(result.is_err());
1999        assert!(result.unwrap_err().to_string().contains("requires numeric"));
2000
2001        // Test with array containing non-numeric value
2002        let result = registry.call_function(
2003            "tan",
2004            &[Value::Array(vec![
2005                Value::Int(1),
2006                Value::String("invalid".to_string()),
2007            ])],
2008        );
2009        assert!(result.is_err());
2010        assert!(result
2011            .unwrap_err()
2012            .to_string()
2013            .contains("requires numeric values in array"));
2014    }
2015
2016    #[test]
2017    fn test_builtin_quartile_invalid_args() {
2018        let registry = BuiltinRegistry::new();
2019
2020        // Test wrong number of arguments
2021        let result = registry.call_function("quartile", &[]);
2022        assert!(result.is_err());
2023        assert!(result
2024            .unwrap_err()
2025            .to_string()
2026            .contains("expects 1 or 2 arguments"));
2027
2028        let result =
2029            registry.call_function("quartile", &[Value::Int(1), Value::Int(2), Value::Int(3)]);
2030        assert!(result.is_err());
2031        assert!(result
2032            .unwrap_err()
2033            .to_string()
2034            .contains("expects 1 or 2 arguments"));
2035
2036        // Test invalid quartile number
2037        let result = registry.call_function(
2038            "quartile",
2039            &[Value::Array(vec![Value::Int(1)]), Value::Int(0)],
2040        );
2041        assert!(result.is_err());
2042        assert!(result
2043            .unwrap_err()
2044            .to_string()
2045            .contains("second argument must be 1, 2, or 3"));
2046
2047        let result = registry.call_function(
2048            "quartile",
2049            &[Value::Array(vec![Value::Int(1)]), Value::Int(4)],
2050        );
2051        assert!(result.is_err());
2052        assert!(result
2053            .unwrap_err()
2054            .to_string()
2055            .contains("second argument must be 1, 2, or 3"));
2056
2057        let result = registry.call_function(
2058            "quartile",
2059            &[
2060                Value::Array(vec![Value::Int(1)]),
2061                Value::String("invalid".to_string()),
2062            ],
2063        );
2064        assert!(result.is_err());
2065        assert!(result
2066            .unwrap_err()
2067            .to_string()
2068            .contains("second argument must be 1, 2, or 3"));
2069
2070        // Test invalid input type
2071        let result = registry.call_function("quartile", &[Value::String("not array".to_string())]);
2072        assert!(result.is_err());
2073        assert!(result
2074            .unwrap_err()
2075            .to_string()
2076            .contains("requires array, DataFrame, or Series"));
2077    }
2078
2079    #[test]
2080    fn test_builtin_array_pop_array() {
2081        let registry = BuiltinRegistry::new();
2082
2083        // Test popping from array with elements
2084        let arr = Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
2085        let result = registry.call_function("array_pop", &[arr]).unwrap();
2086        assert_eq!(result, Value::Int(3));
2087
2088        // Test popping from single-element array
2089        let arr = Value::Array(vec![Value::String("hello".to_string())]);
2090        let result = registry.call_function("array_pop", &[arr]).unwrap();
2091        assert_eq!(result, Value::String("hello".to_string()));
2092
2093        // Test popping from empty array
2094        let arr = Value::Array(vec![]);
2095        let result = registry.call_function("array_pop", &[arr]).unwrap();
2096        assert_eq!(result, Value::Null);
2097    }
2098
2099    #[test]
2100    fn test_builtin_array_pop_dataframe() {
2101        let registry = BuiltinRegistry::new();
2102
2103        // Test popping from DataFrame with rows
2104        let df = DataFrame::new(vec![
2105            Series::new(
2106                PlSmallStr::from("name"),
2107                vec![
2108                    "Alice".to_string(),
2109                    "Bob".to_string(),
2110                    "Charlie".to_string(),
2111                ],
2112            )
2113            .into(),
2114            Series::new(PlSmallStr::from("age"), vec![25i64, 30i64, 35i64]).into(),
2115        ])
2116        .unwrap();
2117        let df_value = Value::DataFrame(df);
2118        let result = registry.call_function("array_pop", &[df_value]).unwrap();
2119        match result {
2120            Value::Object(obj) => {
2121                assert_eq!(obj.get("name"), Some(&Value::String("Charlie".to_string())));
2122                assert_eq!(obj.get("age"), Some(&Value::Int(35)));
2123            }
2124            _ => panic!("Expected object result"),
2125        }
2126
2127        // Test popping from single-row DataFrame
2128        let df = DataFrame::new(vec![
2129            Series::new(PlSmallStr::from("id"), vec![42i64]).into(),
2130            Series::new(PlSmallStr::from("value"), vec!["test".to_string()]).into(),
2131        ])
2132        .unwrap();
2133        let df_value = Value::DataFrame(df);
2134        let result = registry.call_function("array_pop", &[df_value]).unwrap();
2135        match result {
2136            Value::Object(obj) => {
2137                assert_eq!(obj.get("id"), Some(&Value::Int(42)));
2138                assert_eq!(obj.get("value"), Some(&Value::String("test".to_string())));
2139            }
2140            _ => panic!("Expected object result"),
2141        }
2142
2143        // Test popping from empty DataFrame
2144        let df = DataFrame::new(vec![Column::new(
2145            PlSmallStr::from("empty"),
2146            Vec::<String>::new(),
2147        )])
2148        .unwrap();
2149        let df_value = Value::DataFrame(df);
2150        let result = registry.call_function("array_pop", &[df_value]).unwrap();
2151        assert_eq!(result, Value::Null);
2152    }
2153
2154    #[test]
2155    fn test_builtin_array_pop_series() {
2156        let registry = BuiltinRegistry::new();
2157
2158        // Test popping from list series
2159        let list_series = Series::new(
2160            PlSmallStr::from("lists"),
2161            vec![Series::new(PlSmallStr::from(""), vec![1, 2, 3])],
2162        );
2163        let series_value = Value::Series(list_series);
2164        let result = registry
2165            .call_function("array_pop", &[series_value])
2166            .unwrap();
2167        assert_eq!(result, Value::Int(3));
2168
2169        // Test popping from empty list series
2170        let list_series = Series::new(
2171            "empty_lists".into(),
2172            vec![Series::new(PlSmallStr::from(""), Vec::<i32>::new())],
2173        );
2174        let series_value = Value::Series(list_series);
2175        let result = registry
2176            .call_function("array_pop", &[series_value])
2177            .unwrap();
2178        assert_eq!(result, Value::Null);
2179
2180        // Test with non-list series (should error)
2181        let int_series = Series::new(PlSmallStr::from("ints"), vec![1, 2, 3]);
2182        let series_value = Value::Series(int_series);
2183        let result = registry.call_function("array_pop", &[series_value]);
2184        assert!(result.is_err());
2185        assert!(result
2186            .unwrap_err()
2187            .to_string()
2188            .contains("requires an array, DataFrame, or list series"));
2189    }
2190
2191    #[test]
2192    fn test_builtin_array_pop_invalid_args() {
2193        let registry = BuiltinRegistry::new();
2194
2195        // Test wrong number of arguments
2196        let result = registry.call_function("array_pop", &[]);
2197        assert!(result.is_err());
2198        assert!(result
2199            .unwrap_err()
2200            .to_string()
2201            .contains("expects 1 argument"));
2202
2203        let arr = Value::Array(vec![Value::Int(1)]);
2204        let result = registry.call_function("array_pop", &[arr, Value::Int(2)]);
2205        assert!(result.is_err());
2206        assert!(result
2207            .unwrap_err()
2208            .to_string()
2209            .contains("expects 1 argument"));
2210
2211        // Test invalid input type
2212        let result = registry.call_function("array_pop", &[Value::String("not array".to_string())]);
2213        assert!(result.is_err());
2214        assert!(result
2215            .unwrap_err()
2216            .to_string()
2217            .contains("requires an array, DataFrame, or list series"));
2218    }
2219
2220    #[test]
2221    fn test_builtin_mround() {
2222        // Test basic rounding to nearest multiple
2223        let result = builtin::mround::builtin_mround(&[Value::Int(17), Value::Int(5)]).unwrap();
2224        assert_eq!(result, Value::Float(15.0));
2225
2226        let result = builtin::mround::builtin_mround(&[Value::Int(18), Value::Int(5)]).unwrap();
2227        assert_eq!(result, Value::Float(20.0));
2228
2229        let result = builtin::mround::builtin_mround(&[Value::Int(15), Value::Int(5)]).unwrap();
2230        assert_eq!(result, Value::Float(15.0));
2231
2232        // Test with floats
2233        let result = builtin::mround::builtin_mround(&[Value::Float(17.3), Value::Int(5)]).unwrap();
2234        assert_eq!(result, Value::Float(15.0));
2235
2236        let result = builtin::mround::builtin_mround(&[Value::Float(18.7), Value::Int(5)]).unwrap();
2237        assert_eq!(result, Value::Float(20.0));
2238
2239        // Test with string numbers
2240        let result =
2241            builtin::mround::builtin_mround(&[Value::String("17".to_string()), Value::Int(5)])
2242                .unwrap();
2243        assert_eq!(result, Value::Float(15.0));
2244
2245        let result =
2246            builtin::mround::builtin_mround(&[Value::String("18.5".to_string()), Value::Int(5)])
2247                .unwrap();
2248        assert_eq!(result, Value::Float(20.0));
2249
2250        // Test with string multiple
2251        let result =
2252            builtin::mround::builtin_mround(&[Value::Int(17), Value::String("5".to_string())])
2253                .unwrap();
2254        assert_eq!(result, Value::Float(15.0));
2255
2256        // Test multiple 0
2257        let result = builtin::mround::builtin_mround(&[Value::Int(17), Value::Int(0)]).unwrap();
2258        assert_eq!(result, Value::Float(17.0));
2259
2260        // Test invalid number
2261        let result = builtin::mround::builtin_mround(&[
2262            Value::String("not a number".to_string()),
2263            Value::Int(5),
2264        ]);
2265        assert!(result.is_err());
2266
2267        // Test invalid multiple
2268        let result = builtin::mround::builtin_mround(&[
2269            Value::Int(17),
2270            Value::String("not a number".to_string()),
2271        ]);
2272        assert!(result.is_err());
2273
2274        // Test wrong number of arguments
2275        let result = builtin::mround::builtin_mround(&[Value::Int(17)]);
2276        assert!(result.is_err());
2277
2278        let result =
2279            builtin::mround::builtin_mround(&[Value::Int(17), Value::Int(5), Value::Int(1)]);
2280        assert!(result.is_err());
2281    }
2282
2283    #[test]
2284    #[cfg(feature = "rand")]
2285    fn test_builtin_rand() {
2286        // Test rand with no arguments
2287        let result = builtin::rand::builtin_rand(&[]).unwrap();
2288        match result {
2289            Value::Float(f) => {
2290                assert!((0.0..1.0).contains(&f));
2291            }
2292            _ => panic!("Expected Float"),
2293        }
2294
2295        // Test rand with one argument (should be ignored)
2296        let result = builtin::rand::builtin_rand(&[Value::Int(42)]).unwrap();
2297        match result {
2298            Value::Float(f) => {
2299                assert!((0.0..1.0).contains(&f));
2300            }
2301            _ => panic!("Expected Float"),
2302        }
2303
2304        // Test rand with too many arguments
2305        let result = builtin::rand::builtin_rand(&[Value::Int(1), Value::Int(2)]);
2306        assert!(result.is_err());
2307    }
2308
2309    #[test]
2310    fn test_builtin_histogram_array() {
2311        let arr = Value::Array(vec![
2312            Value::Int(1),
2313            Value::Int(2),
2314            Value::Int(3),
2315            Value::Int(4),
2316            Value::Int(5),
2317            Value::Int(6),
2318            Value::Int(7),
2319            Value::Int(8),
2320            Value::Int(9),
2321            Value::Int(10),
2322        ]);
2323        let result = builtin_histogram(&[arr]).unwrap();
2324        match result {
2325            Value::Object(obj) => {
2326                assert!(obj.contains_key("counts"));
2327                assert!(obj.contains_key("bins"));
2328                if let Value::Array(counts) = &obj["counts"] {
2329                    assert_eq!(counts.len(), 10);
2330                    // Check some counts
2331                    assert_eq!(counts[0], Value::Int(1)); // bin for 1
2332                    assert_eq!(counts[9], Value::Int(1)); // bin for 10
2333                } else {
2334                    panic!("counts should be array");
2335                }
2336                if let Value::Array(bins) = &obj["bins"] {
2337                    assert_eq!(bins.len(), 11); // 10 bins + 1
2338                    assert_eq!(bins[0], Value::Float(1.0));
2339                    assert_eq!(bins[10], Value::Float(10.0));
2340                } else {
2341                    panic!("bins should be array");
2342                }
2343            }
2344            _ => panic!("Expected Object"),
2345        }
2346    }
2347
2348    #[test]
2349    fn test_builtin_histogram_series() {
2350        let series = Series::new(
2351            PlSmallStr::from("values"),
2352            vec![1i64, 2, 3, 4, 5, 6, 7, 8, 9, 10],
2353        );
2354        let series_value = Value::Series(series);
2355        let result = builtin_histogram(&[series_value]).unwrap();
2356        match result {
2357            Value::Object(obj) => {
2358                assert!(obj.contains_key("counts"));
2359                assert!(obj.contains_key("bins"));
2360                if let Value::Array(counts) = &obj["counts"] {
2361                    assert_eq!(counts.len(), 10);
2362                }
2363            }
2364            _ => panic!("Expected Object"),
2365        }
2366    }
2367
2368    #[test]
2369    fn test_builtin_histogram_empty() {
2370        let arr = Value::Array(vec![]);
2371        let result = builtin_histogram(&[arr]).unwrap();
2372        match result {
2373            Value::Object(obj) => {
2374                if let Value::Array(counts) = &obj["counts"] {
2375                    assert_eq!(counts.len(), 0);
2376                }
2377                if let Value::Array(bins) = &obj["bins"] {
2378                    assert_eq!(bins.len(), 0);
2379                }
2380            }
2381            _ => panic!("Expected Object"),
2382        }
2383    }
2384
2385    #[test]
2386    fn test_builtin_histogram_custom_bins() {
2387        let arr = Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
2388        let result = builtin_histogram(&[arr, Value::Int(5)]).unwrap();
2389        match result {
2390            Value::Object(obj) => {
2391                if let Value::Array(counts) = &obj["counts"] {
2392                    assert_eq!(counts.len(), 5);
2393                }
2394                if let Value::Array(bins) = &obj["bins"] {
2395                    assert_eq!(bins.len(), 6); // 5 + 1
2396                }
2397            }
2398            _ => panic!("Expected Object"),
2399        }
2400    }
2401
2402    #[test]
2403    fn test_builtin_histogram_invalid_args() {
2404        // No args
2405        let result = builtin_histogram(&[]);
2406        assert!(result.is_err());
2407
2408        // Too many args
2409        let arr = Value::Array(vec![Value::Int(1)]);
2410        let result = builtin_histogram(&[arr.clone(), Value::Int(5), Value::Int(6)]);
2411        assert!(result.is_err());
2412
2413        // Invalid bins
2414        let result = builtin_histogram(&[arr.clone(), Value::Int(0)]);
2415        assert!(result.is_err());
2416
2417        // Non-numeric bins
2418        let result = builtin_histogram(&[arr.clone(), Value::String("5".to_string())]);
2419        assert!(result.is_err());
2420
2421        // Invalid input type
2422        let result = builtin_histogram(&[Value::Int(1)]);
2423        assert!(result.is_err());
2424    }
2425
2426    #[test]
2427    fn test_builtin_transliterate() {
2428        // Test basic cyrillic to latin transliteration
2429        let result = builtin_transliterate(&[
2430            Value::String("Привет".to_string()),
2431            Value::String("cyrillic".to_string()),
2432            Value::String("latin".to_string()),
2433        ])
2434        .unwrap();
2435        assert_eq!(result, Value::String("Privet".to_string()));
2436
2437        // Test another word
2438        let result = builtin_transliterate(&[
2439            Value::String("Москва".to_string()),
2440            Value::String("cyrillic".to_string()),
2441            Value::String("latin".to_string()),
2442        ])
2443        .unwrap();
2444        assert_eq!(result, Value::String("Moskva".to_string()));
2445
2446        // Test with mixed case
2447        let result = builtin_transliterate(&[
2448            Value::String("Александр".to_string()),
2449            Value::String("cyrillic".to_string()),
2450            Value::String("latin".to_string()),
2451        ])
2452        .unwrap();
2453        assert_eq!(result, Value::String("Aleksandr".to_string()));
2454
2455        // Test array input
2456        let result = builtin_transliterate(&[
2457            Value::Array(vec![
2458                Value::String("Привет".to_string()),
2459                Value::String("Москва".to_string()),
2460                Value::Int(123), // Non-string should be unchanged
2461            ]),
2462            Value::String("cyrillic".to_string()),
2463            Value::String("latin".to_string()),
2464        ])
2465        .unwrap();
2466        assert_eq!(
2467            result,
2468            Value::Array(vec![
2469                Value::String("Privet".to_string()),
2470                Value::String("Moskva".to_string()),
2471                Value::Int(123)
2472            ])
2473        );
2474
2475        // Test DataFrame input
2476        let text_series = Series::new(PlSmallStr::from("text"), &["Привет", "Москва", "тест"]);
2477        let df = DataFrame::new(vec![text_series.into()]).unwrap();
2478        let result = builtin_transliterate(&[
2479            Value::DataFrame(df),
2480            Value::String("cyrillic".to_string()),
2481            Value::String("latin".to_string()),
2482        ])
2483        .unwrap();
2484        if let Value::DataFrame(result_df) = result {
2485            let text_col = result_df.column("text").unwrap();
2486            let values: Vec<String> = text_col
2487                .str()
2488                .unwrap()
2489                .into_iter()
2490                .map(|s| s.unwrap_or("").to_string())
2491                .collect();
2492            assert_eq!(values, vec!["Privet", "Moskva", "test"]);
2493        } else {
2494            panic!("Expected DataFrame");
2495        }
2496
2497        // Test unsupported script conversion
2498        let result = builtin_transliterate(&[
2499            Value::String("hello".to_string()),
2500            Value::String("latin".to_string()),
2501            Value::String("cyrillic".to_string()),
2502        ]);
2503        assert!(result.is_err());
2504        assert!(result.unwrap_err().to_string().contains("not supported"));
2505
2506        // Test invalid arguments
2507        let result = builtin_transliterate(&[Value::String("test".to_string())]);
2508        assert!(result.is_err());
2509
2510        let result = builtin_transliterate(&[
2511            Value::Int(123),
2512            Value::String("cyrillic".to_string()),
2513            Value::String("latin".to_string()),
2514        ]);
2515        assert!(result.is_err());
2516    }
2517
2518    #[test]
2519    fn test_transliterate_registry() {
2520        let registry = BuiltinRegistry::new();
2521        assert!(registry.has_function("transliterate"));
2522        let result = registry
2523            .call_function(
2524                "transliterate",
2525                &[
2526                    Value::String("Привет".to_string()),
2527                    Value::String("cyrillic".to_string()),
2528                    Value::String("latin".to_string()),
2529                ],
2530            )
2531            .unwrap();
2532        assert_eq!(result, Value::String("Privet".to_string()));
2533    }
2534
2535    #[test]
2536    fn test_builtin_length() {
2537        let registry = BuiltinRegistry::new();
2538
2539        // Test with arrays
2540        let arr = Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
2541        let result = registry.call_function("length", &[arr]).unwrap();
2542        assert_eq!(result, Value::Int(3));
2543
2544        // Test with mixed type array
2545        let mixed_arr = Value::Array(vec![
2546            Value::Int(1),
2547            Value::String("hello".to_string()),
2548            Value::Bool(true),
2549        ]);
2550        let result = registry.call_function("length", &[mixed_arr]).unwrap();
2551        assert_eq!(result, Value::Int(3));
2552
2553        // Test with empty array
2554        let empty_arr = Value::Array(vec![]);
2555        let result = registry.call_function("length", &[empty_arr]).unwrap();
2556        assert_eq!(result, Value::Int(0));
2557
2558        // Test with strings
2559        let str_val = Value::String("hello".to_string());
2560        let result = registry.call_function("length", &[str_val]).unwrap();
2561        assert_eq!(result, Value::Int(5));
2562
2563        // Test with Unicode string
2564        let unicode_str = Value::String("héllo wörld".to_string());
2565        let result = registry.call_function("length", &[unicode_str]).unwrap();
2566        assert_eq!(result, Value::Int(11)); // Character count, not byte count
2567
2568        // Test with objects
2569        let mut obj = HashMap::new();
2570        obj.insert("a".to_string(), Value::Int(1));
2571        obj.insert("b".to_string(), Value::Int(2));
2572        let obj_val = Value::Object(obj);
2573        let result = registry.call_function("length", &[obj_val]).unwrap();
2574        assert_eq!(result, Value::Int(2));
2575
2576        // Test with DataFrame
2577        let df = create_test_dataframe();
2578        let df_val = Value::DataFrame(df);
2579        let result = registry.call_function("length", &[df_val]).unwrap();
2580        assert_eq!(result, Value::Int(3)); // 3 rows
2581
2582        // Test with Series
2583        let series = Series::new(PlSmallStr::from("test"), &[1, 2, 3, 4]);
2584        let series_val = Value::Series(series);
2585        let result = registry.call_function("length", &[series_val]).unwrap();
2586        assert_eq!(result, Value::Int(4));
2587
2588        // Test with null
2589        let result = registry.call_function("length", &[Value::Null]).unwrap();
2590        assert_eq!(result, Value::Int(0));
2591
2592        // Test with scalar values
2593        let result = registry.call_function("length", &[Value::Int(42)]).unwrap();
2594        assert_eq!(result, Value::Int(1));
2595
2596        let result = registry
2597            .call_function("length", &[Value::Bool(true)])
2598            .unwrap();
2599        assert_eq!(result, Value::Int(1));
2600    }
2601
2602    #[test]
2603    fn test_builtin_keys() {
2604        let registry = BuiltinRegistry::new();
2605
2606        // Test object keys
2607        let mut obj = HashMap::new();
2608        obj.insert("b".to_string(), Value::Int(2));
2609        obj.insert("a".to_string(), Value::Int(1));
2610        obj.insert("c".to_string(), Value::Int(3));
2611        let obj_val = Value::Object(obj);
2612        let result = registry.call_function("keys", &[obj_val]).unwrap();
2613        if let Value::Array(keys) = result {
2614            assert_eq!(keys.len(), 3);
2615            // Keys should be sorted
2616            assert_eq!(keys[0], Value::String("a".to_string()));
2617            assert_eq!(keys[1], Value::String("b".to_string()));
2618            assert_eq!(keys[2], Value::String("c".to_string()));
2619        } else {
2620            panic!("Expected array of keys");
2621        }
2622
2623        // Test DataFrame keys (column names)
2624        let df = create_test_dataframe();
2625        let df_val = Value::DataFrame(df);
2626        let result = registry.call_function("keys", &[df_val]).unwrap();
2627        if let Value::Array(keys) = result {
2628            assert_eq!(keys.len(), 3);
2629            assert!(keys.contains(&Value::String("name".to_string())));
2630            assert!(keys.contains(&Value::String("age".to_string())));
2631            assert!(keys.contains(&Value::String("score".to_string())));
2632        } else {
2633            panic!("Expected array of column names");
2634        }
2635    }
2636
2637    #[test]
2638    fn test_builtin_values() {
2639        let registry = BuiltinRegistry::new();
2640
2641        // Test object values
2642        let mut obj = HashMap::new();
2643        obj.insert("a".to_string(), Value::Int(1));
2644        obj.insert("b".to_string(), Value::Int(2));
2645        let obj_val = Value::Object(obj);
2646        let result = registry.call_function("values", &[obj_val]).unwrap();
2647        if let Value::Array(values) = result {
2648            assert_eq!(values.len(), 2);
2649            assert!(values.contains(&Value::Int(1)));
2650            assert!(values.contains(&Value::Int(2)));
2651        } else {
2652            panic!("Expected array of values");
2653        }
2654    }
2655
2656    #[test]
2657    fn test_builtin_type() {
2658        let registry = BuiltinRegistry::new();
2659
2660        // Test different types
2661        let result = registry.call_function("type", &[Value::Int(42)]).unwrap();
2662        assert_eq!(result, Value::String("integer".to_string()));
2663
2664        let result = registry
2665            .call_function("type", &[Value::Float(3.14)])
2666            .unwrap();
2667        assert_eq!(result, Value::String("float".to_string()));
2668
2669        let result = registry
2670            .call_function("type", &[Value::String("hello".to_string())])
2671            .unwrap();
2672        assert_eq!(result, Value::String("string".to_string()));
2673
2674        let result = registry
2675            .call_function("type", &[Value::Bool(true)])
2676            .unwrap();
2677        assert_eq!(result, Value::String("boolean".to_string()));
2678
2679        let result = registry.call_function("type", &[Value::Null]).unwrap();
2680        assert_eq!(result, Value::String("null".to_string()));
2681
2682        let arr = Value::Array(vec![Value::Int(1)]);
2683        let result = registry.call_function("type", &[arr]).unwrap();
2684        assert_eq!(result, Value::String("array".to_string()));
2685
2686        let mut obj = HashMap::new();
2687        obj.insert("key".to_string(), Value::Int(1));
2688        let obj_val = Value::Object(obj);
2689        let result = registry.call_function("type", &[obj_val]).unwrap();
2690        assert_eq!(result, Value::String("object".to_string()));
2691    }
2692
2693    #[test]
2694    fn test_builtin_empty() {
2695        let registry = BuiltinRegistry::new();
2696
2697        // Test empty array
2698        let empty_arr = Value::Array(vec![]);
2699        let result = registry.call_function("empty", &[empty_arr]).unwrap();
2700        assert_eq!(result, Value::Bool(true));
2701
2702        // Test non-empty array
2703        let arr = Value::Array(vec![Value::Int(1)]);
2704        let result = registry.call_function("empty", &[arr]).unwrap();
2705        assert_eq!(result, Value::Bool(false));
2706
2707        // Test empty object
2708        let empty_obj = Value::Object(HashMap::new());
2709        let result = registry.call_function("empty", &[empty_obj]).unwrap();
2710        assert_eq!(result, Value::Bool(true));
2711
2712        // Test non-empty object
2713        let mut obj = HashMap::new();
2714        obj.insert("key".to_string(), Value::Int(1));
2715        let obj_val = Value::Object(obj);
2716        let result = registry.call_function("empty", &[obj_val]).unwrap();
2717        assert_eq!(result, Value::Bool(false));
2718
2719        // Test empty string
2720        let empty_str = Value::String("".to_string());
2721        let result = registry.call_function("empty", &[empty_str]).unwrap();
2722        assert_eq!(result, Value::Bool(true));
2723
2724        // Test non-empty string
2725        let str_val = Value::String("hello".to_string());
2726        let result = registry.call_function("empty", &[str_val]).unwrap();
2727        assert_eq!(result, Value::Bool(false));
2728    }
2729
2730    #[test]
2731    fn test_builtin_generate_uuidv4() {
2732        let registry = BuiltinRegistry::new();
2733
2734        // Test no arguments
2735        let result1 = registry.call_function("generate_uuidv4", &[]).unwrap();
2736        let result2 = registry.call_function("generate_uuidv4", &[]).unwrap();
2737
2738        match (&result1, &result2) {
2739            (Value::String(uuid1), Value::String(uuid2)) => {
2740                // Check they are different
2741                assert_ne!(uuid1, uuid2);
2742
2743                // Check they are valid UUID v4 format
2744                assert_eq!(uuid1.len(), 36); // UUID v4 string length
2745                assert_eq!(uuid2.len(), 36);
2746
2747                // Check version (4) and variant bits
2748                let uuid1_parsed = uuid::Uuid::parse_str(uuid1).unwrap();
2749                let uuid2_parsed = uuid::Uuid::parse_str(uuid2).unwrap();
2750
2751                assert_eq!(uuid1_parsed.get_version_num(), 4);
2752                assert_eq!(uuid2_parsed.get_version_num(), 4);
2753            }
2754            _ => panic!("Expected strings"),
2755        }
2756
2757        // Test with arguments should fail
2758        let result = registry.call_function("generate_uuidv4", &[Value::Int(1)]);
2759        assert!(result.is_err());
2760    }
2761
2762    #[test]
2763    fn test_builtin_avg_ifs_array() {
2764        let registry = BuiltinRegistry::new();
2765
2766        // Test basic avg_ifs with arrays
2767        let values = Value::Array(vec![
2768            Value::Int(10),
2769            Value::Int(20),
2770            Value::Int(30),
2771            Value::Int(40),
2772        ]);
2773        let mask1 = Value::Array(vec![
2774            Value::Bool(true),
2775            Value::Bool(false),
2776            Value::Bool(true),
2777            Value::Bool(false),
2778        ]);
2779        let mask2 = Value::Array(vec![
2780            Value::Bool(true),
2781            Value::Bool(true),
2782            Value::Bool(true),
2783            Value::Bool(false),
2784        ]);
2785        let result = registry
2786            .call_function("avg_ifs", &[values, mask1, mask2])
2787            .unwrap();
2788        assert_eq!(result, Value::Float(20.0)); // (10 + 30) / 2 = 20.0
2789
2790        // Test with floats
2791        let values = Value::Array(vec![
2792            Value::Float(1.5),
2793            Value::Float(2.5),
2794            Value::Float(3.5),
2795        ]);
2796        let mask1 = Value::Array(vec![
2797            Value::Bool(true),
2798            Value::Bool(true),
2799            Value::Bool(true),
2800        ]);
2801        let mask2 = Value::Array(vec![
2802            Value::Bool(true),
2803            Value::Bool(false),
2804            Value::Bool(true),
2805        ]);
2806        let result = registry
2807            .call_function("avg_ifs", &[values, mask1, mask2])
2808            .unwrap();
2809        assert_eq!(result, Value::Float(2.5)); // (1.5 + 3.5) / 2
2810
2811        // Test with no matches
2812        let values = Value::Array(vec![Value::Int(10), Value::Int(20)]);
2813        let mask1 = Value::Array(vec![Value::Bool(false), Value::Bool(false)]);
2814        let mask2 = Value::Array(vec![Value::Bool(true), Value::Bool(true)]);
2815        let result = registry
2816            .call_function("avg_ifs", &[values, mask1, mask2])
2817            .unwrap();
2818        assert_eq!(result, Value::Null);
2819
2820        // Test with two masks (same as first test but different values)
2821        let values = Value::Array(vec![Value::Int(5), Value::Int(15), Value::Int(25)]);
2822        let mask1 = Value::Array(vec![
2823            Value::Bool(true),
2824            Value::Bool(true),
2825            Value::Bool(false),
2826        ]);
2827        let mask2 = Value::Array(vec![
2828            Value::Bool(true),
2829            Value::Bool(false),
2830            Value::Bool(true),
2831        ]);
2832        let result = registry
2833            .call_function("avg_ifs", &[values, mask1, mask2])
2834            .unwrap();
2835        assert_eq!(result, Value::Float(5.0)); // Only first value (5) satisfies both masks
2836    }
2837
2838    #[test]
2839    fn test_builtin_avg_ifs_dataframe() {
2840        let registry = BuiltinRegistry::new();
2841
2842        // Create test DataFrame
2843        let df = DataFrame::new(vec![
2844            Series::new(PlSmallStr::from("a"), vec![10i64, 20, 30]).into(),
2845            Series::new(PlSmallStr::from("b"), vec![1i64, 2, 3]).into(),
2846            Series::new(PlSmallStr::from("c"), vec![100.0f64, 200.0, 300.0]).into(),
2847        ])
2848        .unwrap();
2849        let df_value = Value::DataFrame(df);
2850
2851        // Create masks
2852        let mask1 = Series::new(PlSmallStr::from("mask1"), vec![true, false, true]);
2853        let mask1_value = Value::Series(mask1);
2854        let mask2 = Series::new(PlSmallStr::from("mask2"), vec![true, true, true]);
2855        let mask2_value = Value::Series(mask2);
2856
2857        let result = registry
2858            .call_function("avg_ifs", &[df_value, mask1_value, mask2_value])
2859            .unwrap();
2860        match result {
2861            Value::Object(obj) => {
2862                // Should average columns where masks are true
2863                assert_eq!(obj.get("a"), Some(&Value::Float(20.0))); // (10 + 30) / 2
2864                assert_eq!(obj.get("b"), Some(&Value::Float(2.0))); // (1 + 3) / 2
2865                assert_eq!(obj.get("c"), Some(&Value::Float(200.0))); // (100 + 300) / 2
2866            }
2867            _ => panic!("Expected object"),
2868        }
2869    }
2870
2871    #[test]
2872    fn test_builtin_avg_ifs_series() {
2873        let registry = BuiltinRegistry::new();
2874
2875        // Test with numeric series
2876        let series = Series::new(PlSmallStr::from("values"), vec![10.0f64, 20.0, 30.0, 40.0]);
2877        let series_value = Value::Series(series);
2878
2879        let mask1 = Series::new(PlSmallStr::from("mask1"), vec![true, false, true, false]);
2880        let mask1_value = Value::Series(mask1);
2881        let mask2 = Series::new(PlSmallStr::from("mask2"), vec![true, true, true, false]);
2882        let mask2_value = Value::Series(mask2);
2883
2884        let result = registry
2885            .call_function("avg_ifs", &[series_value, mask1_value, mask2_value])
2886            .unwrap();
2887        assert_eq!(result, Value::Float(20.0)); // (10.0 + 30.0) / 2 = 20.0
2888
2889        // Test with no matches
2890        let series = Series::new(PlSmallStr::from("values"), vec![10.0f64, 20.0]);
2891        let series_value = Value::Series(series);
2892        let mask1 = Series::new(PlSmallStr::from("mask1"), vec![false, false]);
2893        let mask1_value = Value::Series(mask1);
2894        let mask2 = Series::new(PlSmallStr::from("mask2"), vec![true, true]);
2895        let mask2_value = Value::Series(mask2);
2896
2897        let result = registry
2898            .call_function("avg_ifs", &[series_value, mask1_value, mask2_value])
2899            .unwrap();
2900        assert_eq!(result, Value::Null);
2901    }
2902
2903    #[test]
2904    fn test_builtin_avg_ifs_invalid_args() {
2905        let registry = BuiltinRegistry::new();
2906
2907        // Test wrong number of arguments
2908        let result = registry.call_function("avg_ifs", &[Value::Array(vec![Value::Int(1)])]);
2909        assert!(result.is_err());
2910        assert!(result
2911            .unwrap_err()
2912            .to_string()
2913            .contains("expects at least 3 arguments"));
2914
2915        // Test mismatched array lengths
2916        let values = Value::Array(vec![Value::Int(1), Value::Int(2)]);
2917        let mask1 = Value::Array(vec![Value::Bool(true)]); // Different length
2918        let mask2 = Value::Array(vec![Value::Bool(true), Value::Bool(false)]);
2919        let result = registry.call_function("avg_ifs", &[values, mask1, mask2]);
2920        assert!(result.is_err());
2921        assert!(result
2922            .unwrap_err()
2923            .to_string()
2924            .contains("all masks must have same length as values"));
2925
2926        // Test non-array mask for arrays
2927        let values = Value::Array(vec![Value::Int(1), Value::Int(2)]);
2928        let mask1 = Value::String("not array".to_string());
2929        let mask2 = Value::Array(vec![Value::Bool(true), Value::Bool(false)]);
2930        let result = registry.call_function("avg_ifs", &[values, mask1, mask2]);
2931        assert!(result.is_err());
2932        assert!(result
2933            .unwrap_err()
2934            .to_string()
2935            .contains("all masks must be arrays"));
2936
2937        // Test invalid input type
2938        let result = registry.call_function(
2939            "avg_ifs",
2940            &[
2941                Value::String("not array".to_string()),
2942                Value::Array(vec![Value::Bool(true)]),
2943                Value::Array(vec![Value::Bool(true)]),
2944            ],
2945        );
2946        assert!(result.is_err());
2947        assert!(result
2948            .unwrap_err()
2949            .to_string()
2950            .contains("first argument must be array, DataFrame, or Series"));
2951    }
2952
2953    #[test]
2954    fn test_builtin_count_if_array() {
2955        let registry = BuiltinRegistry::new();
2956
2957        // Test basic count_if with arrays
2958        let values = Value::Array(vec![
2959            Value::Int(10),
2960            Value::Int(20),
2961            Value::Int(30),
2962            Value::Int(40),
2963        ]);
2964        let mask = Value::Array(vec![
2965            Value::Bool(true),
2966            Value::Bool(false),
2967            Value::Bool(true),
2968            Value::Bool(false),
2969        ]);
2970        let result = registry.call_function("count_if", &[values, mask]).unwrap();
2971        assert_eq!(result, Value::Int(2)); // Two true values
2972
2973        // Test with all true
2974        let values = Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
2975        let mask = Value::Array(vec![
2976            Value::Bool(true),
2977            Value::Bool(true),
2978            Value::Bool(true),
2979        ]);
2980        let result = registry.call_function("count_if", &[values, mask]).unwrap();
2981        assert_eq!(result, Value::Int(3));
2982
2983        // Test with all false
2984        let values = Value::Array(vec![Value::Int(1), Value::Int(2)]);
2985        let mask = Value::Array(vec![Value::Bool(false), Value::Bool(false)]);
2986        let result = registry.call_function("count_if", &[values, mask]).unwrap();
2987        assert_eq!(result, Value::Int(0));
2988
2989        // Test with truthy values
2990        let values = Value::Array(vec![
2991            Value::Int(1),
2992            Value::Int(0),
2993            Value::String("hello".to_string()),
2994            Value::String("".to_string()),
2995        ]);
2996        let mask = Value::Array(vec![
2997            Value::Int(1),
2998            Value::Int(0),
2999            Value::String("hello".to_string()),
3000            Value::String("".to_string()),
3001        ]);
3002        let result = registry.call_function("count_if", &[values, mask]).unwrap();
3003        assert_eq!(result, Value::Int(2)); // 1 and "hello" are truthy
3004
3005        // Test with empty arrays
3006        let values = Value::Array(vec![]);
3007        let mask = Value::Array(vec![]);
3008        let result = registry.call_function("count_if", &[values, mask]).unwrap();
3009        assert_eq!(result, Value::Int(0));
3010
3011        // Test with null values in mask
3012        let values = Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
3013        let mask = Value::Array(vec![Value::Bool(true), Value::Null, Value::Bool(false)]);
3014        let result = registry.call_function("count_if", &[values, mask]).unwrap();
3015        assert_eq!(result, Value::Int(1)); // Only first is true, null is falsy
3016    }
3017
3018    #[test]
3019    fn test_builtin_count_if_dataframe() {
3020        let registry = BuiltinRegistry::new();
3021
3022        // Create test DataFrame
3023        let df = DataFrame::new(vec![
3024            Series::new(PlSmallStr::from("a"), vec![10i64, 20, 30]).into(),
3025            Series::new(PlSmallStr::from("b"), vec![1i64, 2, 3]).into(),
3026        ])
3027        .unwrap();
3028        let df_value = Value::DataFrame(df);
3029
3030        // Create mask
3031        let mask = Series::new(PlSmallStr::from("mask"), vec![true, false, true]);
3032        let mask_value = Value::Series(mask);
3033
3034        let result = registry
3035            .call_function("count_if", &[df_value, mask_value])
3036            .unwrap();
3037        assert_eq!(result, Value::Int(2)); // Two true values in mask
3038    }
3039
3040    #[test]
3041    fn test_builtin_count_if_series() {
3042        let registry = BuiltinRegistry::new();
3043
3044        // Test with numeric series
3045        let series = Series::new(PlSmallStr::from("values"), vec![10.0f64, 20.0, 30.0, 40.0]);
3046        let series_value = Value::Series(series);
3047
3048        let mask = Series::new(PlSmallStr::from("mask"), vec![true, false, true, false]);
3049        let mask_value = Value::Series(mask);
3050
3051        let result = registry
3052            .call_function("count_if", &[series_value, mask_value])
3053            .unwrap();
3054        assert_eq!(result, Value::Int(2)); // Two true values
3055    }
3056
3057    #[test]
3058    fn test_builtin_count_if_invalid_args() {
3059        let registry = BuiltinRegistry::new();
3060
3061        // Test wrong number of arguments
3062        let result = registry.call_function("count_if", &[Value::Array(vec![Value::Int(1)])]);
3063        assert!(result.is_err());
3064        assert!(result
3065            .unwrap_err()
3066            .to_string()
3067            .contains("expects 2 arguments"));
3068
3069        // Test mismatched array lengths
3070        let values = Value::Array(vec![Value::Int(1), Value::Int(2)]);
3071        let mask = Value::Array(vec![Value::Bool(true)]); // Different length
3072        let result = registry.call_function("count_if", &[values, mask]);
3073        assert!(result.is_err());
3074        assert!(result
3075            .unwrap_err()
3076            .to_string()
3077            .contains("collection and mask arrays must have same length"));
3078
3079        // Test invalid types
3080        let values = Value::String("not array".to_string());
3081        let mask = Value::Array(vec![Value::Bool(true)]);
3082        let result = registry.call_function("count_if", &[values, mask]);
3083        assert!(result.is_err());
3084        assert!(result
3085            .unwrap_err()
3086            .to_string()
3087            .contains("requires (array, array) or (dataframe/series, series)"));
3088    }
3089
3090    #[test]
3091    fn test_builtin_sort() {
3092        let registry = BuiltinRegistry::new();
3093
3094        // Test sorting an array
3095        let arr = Value::Array(vec![Value::Int(3), Value::Int(1), Value::Int(2)]);
3096        let result = registry.call_function("sort", &[arr]).unwrap();
3097        if let Value::Array(sorted) = result {
3098            assert_eq!(sorted, vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
3099        } else {
3100            panic!("Expected sorted array");
3101        }
3102
3103        // Test sorting strings
3104        let arr = Value::Array(vec![
3105            Value::String("c".to_string()),
3106            Value::String("a".to_string()),
3107            Value::String("b".to_string()),
3108        ]);
3109        let result = registry.call_function("sort", &[arr]).unwrap();
3110        if let Value::Array(sorted) = result {
3111            assert_eq!(
3112                sorted,
3113                vec![
3114                    Value::String("a".to_string()),
3115                    Value::String("b".to_string()),
3116                    Value::String("c".to_string())
3117                ]
3118            );
3119        } else {
3120            panic!("Expected sorted array");
3121        }
3122
3123        // Test sorting mixed types (should not change order for unsupported comparisons)
3124        let arr = Value::Array(vec![Value::Int(1), Value::String("a".to_string())]);
3125        let result = registry
3126            .call_function("sort", std::slice::from_ref(&arr))
3127            .unwrap();
3128        assert_eq!(result, arr); // Should remain unchanged
3129
3130        // Test invalid arguments
3131        let result = registry.call_function("sort", &[]);
3132        assert!(result.is_err());
3133        assert!(result
3134            .unwrap_err()
3135            .to_string()
3136            .contains("expects 1 argument"));
3137
3138        let result = registry.call_function("sort", &[Value::Int(1), Value::Int(2)]);
3139        assert!(result.is_err());
3140        assert!(result
3141            .unwrap_err()
3142            .to_string()
3143            .contains("expects 1 argument"));
3144    }
3145
3146    #[test]
3147    fn test_builtin_stdev_s_array() {
3148        let registry = BuiltinRegistry::new();
3149
3150        // Test with array of integers
3151        let arr = Value::Array(vec![
3152            Value::Int(1),
3153            Value::Int(2),
3154            Value::Int(3),
3155            Value::Int(4),
3156            Value::Int(5),
3157        ]);
3158        let result = registry.call_function("stdev_s", &[arr]).unwrap();
3159        match result {
3160            Value::Float(stdev) => {
3161                // Sample standard deviation of [1,2,3,4,5] should be approximately 1.581
3162                assert!((stdev - 1.58113883).abs() < 0.0001);
3163            }
3164            _ => panic!("Expected float result"),
3165        }
3166
3167        // Test with array of floats
3168        let arr = Value::Array(vec![
3169            Value::Float(1.0),
3170            Value::Float(2.0),
3171            Value::Float(3.0),
3172            Value::Float(4.0),
3173            Value::Float(5.0),
3174        ]);
3175        let result = registry.call_function("stdev_s", &[arr]).unwrap();
3176        match result {
3177            Value::Float(stdev) => {
3178                assert!((stdev - 1.58113883).abs() < 0.0001);
3179            }
3180            _ => panic!("Expected float result"),
3181        }
3182
3183        // Test with mixed int/float
3184        let arr = Value::Array(vec![Value::Int(1), Value::Float(2.0), Value::Int(3)]);
3185        let result = registry.call_function("stdev_s", &[arr]).unwrap();
3186        match result {
3187            Value::Float(stdev) => {
3188                // Sample std of [1,2,3] is 1.0
3189                assert!((stdev - 1.0).abs() < 0.0001);
3190            }
3191            _ => panic!("Expected float result"),
3192        }
3193
3194        // Test with less than 2 values (should return null)
3195        let arr = Value::Array(vec![Value::Int(1)]);
3196        let result = registry.call_function("stdev_s", &[arr]).unwrap();
3197        assert_eq!(result, Value::Null);
3198
3199        // Test with empty array
3200        let arr = Value::Array(vec![]);
3201        let result = registry.call_function("stdev_s", &[arr]).unwrap();
3202        assert_eq!(result, Value::Null);
3203
3204        // Test with non-numeric values (should be ignored)
3205        let arr = Value::Array(vec![
3206            Value::Int(1),
3207            Value::String("test".to_string()),
3208            Value::Int(3),
3209        ]);
3210        let result = registry.call_function("stdev_s", &[arr]).unwrap();
3211        match result {
3212            Value::Float(stdev) => {
3213                // Only [1,3] should be considered, std is sqrt(2)
3214                assert!((stdev - std::f64::consts::SQRT_2).abs() < 0.0001);
3215            }
3216            _ => panic!("Expected float result"),
3217        }
3218    }
3219
3220    #[test]
3221    fn test_builtin_stdev_s_invalid_args() {
3222        let registry = BuiltinRegistry::new();
3223
3224        // Test with no arguments
3225        let result = registry.call_function("stdev_s", &[]);
3226        assert!(result.is_err());
3227        assert!(result
3228            .unwrap_err()
3229            .to_string()
3230            .contains("expects 1 argument"));
3231
3232        // Test with too many arguments
3233        let result = registry.call_function("stdev_s", &[Value::Int(1), Value::Int(2)]);
3234        assert!(result.is_err());
3235        assert!(result
3236            .unwrap_err()
3237            .to_string()
3238            .contains("expects 1 argument"));
3239
3240        // Test with invalid type
3241        let result = registry.call_function("stdev_s", &[Value::String("test".to_string())]);
3242        assert!(result.is_err());
3243        assert!(result
3244            .unwrap_err()
3245            .to_string()
3246            .contains("requires array, DataFrame, or Series"));
3247    }
3248
3249    #[test]
3250    fn test_builtin_stdev_s_dataframe() {
3251        let registry = BuiltinRegistry::new();
3252
3253        // Create a simple DataFrame
3254        let df = DataFrame::new(vec![
3255            Series::new(PlSmallStr::from("col1"), &[1.0, 2.0, 3.0]).into(),
3256            Series::new(PlSmallStr::from("col2"), &[4.0, 5.0, 6.0]).into(),
3257        ])
3258        .unwrap();
3259        let df_value = Value::DataFrame(df);
3260
3261        let result = registry.call_function("stdev_s", &[df_value]).unwrap();
3262        match result {
3263            Value::Object(obj) => {
3264                // Sample standard deviation of [1.0, 2.0, 3.0] is 1.0
3265                assert_eq!(obj.get("col1"), Some(&Value::Float(1.0)));
3266                assert_eq!(obj.get("col2"), Some(&Value::Float(1.0)));
3267            }
3268            _ => panic!("Expected object result"),
3269        }
3270    }
3271
3272    #[test]
3273    fn test_builtin_stdev_s_series() {
3274        let registry = BuiltinRegistry::new();
3275
3276        // Create a numeric series
3277        let series = Series::new(PlSmallStr::from("test"), &[1.0, 2.0, 3.0, 4.0, 5.0]);
3278        let series_value = Value::Series(series);
3279
3280        let result = registry.call_function("stdev_s", &[series_value]).unwrap();
3281        // Sample standard deviation of [1, 2, 3, 4, 5] is sqrt(2.5) ≈ 1.5811388
3282        match result {
3283            Value::Float(val) => assert!((val - 1.5811388).abs() < 1e-6),
3284            _ => panic!("Expected Float"),
3285        }
3286    }
3287
3288    #[test]
3289    fn test_builtin_std_array() {
3290        let registry = BuiltinRegistry::new();
3291
3292        // Test with array of integers
3293        let arr = Value::Array(vec![
3294            Value::Int(1),
3295            Value::Int(2),
3296            Value::Int(3),
3297            Value::Int(4),
3298            Value::Int(5),
3299        ]);
3300        let result = registry.call_function("std", &[arr]).unwrap();
3301        match result {
3302            Value::Float(stdev) => {
3303                // Sample standard deviation of [1,2,3,4,5] should be approximately 1.581
3304                assert!((stdev - 1.58113883).abs() < 0.0001);
3305            }
3306            _ => panic!("Expected float result"),
3307        }
3308
3309        // Test with array of floats
3310        let arr = Value::Array(vec![
3311            Value::Float(1.0),
3312            Value::Float(2.0),
3313            Value::Float(3.0),
3314            Value::Float(4.0),
3315            Value::Float(5.0),
3316        ]);
3317        let result = registry.call_function("std", &[arr]).unwrap();
3318        match result {
3319            Value::Float(stdev) => {
3320                assert!((stdev - 1.58113883).abs() < 0.0001);
3321            }
3322            _ => panic!("Expected float result"),
3323        }
3324
3325        // Test with mixed int/float
3326        let arr = Value::Array(vec![Value::Int(1), Value::Float(2.0), Value::Int(3)]);
3327        let result = registry.call_function("std", &[arr]).unwrap();
3328        match result {
3329            Value::Float(stdev) => {
3330                // Sample std of [1,2,3] is 1.0
3331                assert!((stdev - 1.0).abs() < 0.0001);
3332            }
3333            _ => panic!("Expected float result"),
3334        }
3335
3336        // Test with less than 2 values (should return null)
3337        let arr = Value::Array(vec![Value::Int(1)]);
3338        let result = registry.call_function("std", &[arr]).unwrap();
3339        assert_eq!(result, Value::Null);
3340
3341        // Test with empty array
3342        let arr = Value::Array(vec![]);
3343        let result = registry.call_function("std", &[arr]).unwrap();
3344        assert_eq!(result, Value::Null);
3345
3346        // Test with non-numeric values (should be ignored)
3347        let arr = Value::Array(vec![
3348            Value::Int(1),
3349            Value::String("test".to_string()),
3350            Value::Int(3),
3351        ]);
3352        let result = registry.call_function("std", &[arr]).unwrap();
3353        match result {
3354            Value::Float(stdev) => {
3355                // Only [1,3] should be considered, std is sqrt(2)
3356                assert!((stdev - std::f64::consts::SQRT_2).abs() < 0.0001);
3357            }
3358            _ => panic!("Expected float result"),
3359        }
3360    }
3361
3362    #[test]
3363    fn test_builtin_std_invalid_args() {
3364        let registry = BuiltinRegistry::new();
3365
3366        // Test with no arguments
3367        let result = registry.call_function("std", &[]);
3368        assert!(result.is_err());
3369        assert!(result
3370            .unwrap_err()
3371            .to_string()
3372            .contains("expects 1 argument"));
3373
3374        // Test with too many arguments
3375        let result = registry.call_function("std", &[Value::Int(1), Value::Int(2)]);
3376        assert!(result.is_err());
3377        assert!(result
3378            .unwrap_err()
3379            .to_string()
3380            .contains("expects 1 argument"));
3381
3382        // Test with invalid type
3383        let result = registry.call_function("std", &[Value::String("test".to_string())]);
3384        assert!(result.is_err());
3385        assert!(result
3386            .unwrap_err()
3387            .to_string()
3388            .contains("requires array, DataFrame, or Series"));
3389    }
3390
3391    #[test]
3392    fn test_builtin_std_dataframe() {
3393        let registry = BuiltinRegistry::new();
3394
3395        // Create a simple DataFrame
3396        let df = DataFrame::new(vec![
3397            Series::new("col1".into(), &[1.0, 2.0, 3.0]).into(),
3398            Series::new("col2".into(), &[4.0, 5.0, 6.0]).into(),
3399        ])
3400        .unwrap();
3401        let df_value = Value::DataFrame(df);
3402
3403        let result = registry.call_function("std", &[df_value]).unwrap();
3404        match result {
3405            Value::Object(obj) => {
3406                // Should return object with null values (placeholder implementation)
3407                assert_eq!(obj.get("col1"), Some(&Value::Null));
3408                assert_eq!(obj.get("col2"), Some(&Value::Null));
3409            }
3410            _ => panic!("Expected object result"),
3411        }
3412    }
3413
3414    #[test]
3415    fn test_builtin_std_series() {
3416        let registry = BuiltinRegistry::new();
3417
3418        // Create a numeric series
3419        let series = Series::new(PlSmallStr::from("test"), &[1.0, 2.0, 3.0, 4.0, 5.0]);
3420        let series_value = Value::Series(series);
3421
3422        let result = registry.call_function("std", &[series_value]).unwrap();
3423        // Should return null (placeholder implementation)
3424        assert_eq!(result, Value::Null);
3425    }
3426
3427    #[test]
3428    fn test_builtin_today() {
3429        let registry = BuiltinRegistry::new();
3430
3431        // Test that today() returns a string in YYYY-MM-DD format
3432        let result = registry.call_function("today", &[]).unwrap();
3433        match result {
3434            Value::String(date_str) => {
3435                // Should be in YYYY-MM-DD format
3436                assert_eq!(date_str.len(), 10);
3437                assert!(date_str.chars().nth(4) == Some('-'));
3438                assert!(date_str.chars().nth(7) == Some('-'));
3439                // Should be today's date
3440                let today = chrono::Utc::now().date_naive();
3441                let expected = today.format("%Y-%m-%d").to_string();
3442                assert_eq!(date_str, expected);
3443            }
3444            _ => panic!("Expected string result"),
3445        }
3446
3447        // Test that today() with arguments fails
3448        let result = registry.call_function("today", &[Value::Int(1)]);
3449        assert!(result.is_err());
3450    }
3451
3452    #[test]
3453    fn test_builtin_repeat() {
3454        let registry = BuiltinRegistry::new();
3455
3456        // Test basic repeat with integer count
3457        let value = Value::String("hello".to_string());
3458        let count = Value::Int(3);
3459        let result = registry.call_function("repeat", &[value, count]).unwrap();
3460        match result {
3461            Value::Array(arr) => {
3462                assert_eq!(arr.len(), 3);
3463                assert_eq!(arr[0], Value::String("hello".to_string()));
3464                assert_eq!(arr[1], Value::String("hello".to_string()));
3465                assert_eq!(arr[2], Value::String("hello".to_string()));
3466            }
3467            _ => panic!("Expected array"),
3468        }
3469
3470        // Test repeat with count 0
3471        let value = Value::Int(42);
3472        let count = Value::Int(0);
3473        let result = registry.call_function("repeat", &[value, count]).unwrap();
3474        match result {
3475            Value::Array(arr) => {
3476                assert_eq!(arr.len(), 0);
3477            }
3478            _ => panic!("Expected array"),
3479        }
3480
3481        // Test repeat with count 1
3482        let value = Value::Bool(true);
3483        let count = Value::Int(1);
3484        let result = registry.call_function("repeat", &[value, count]).unwrap();
3485        match result {
3486            Value::Array(arr) => {
3487                assert_eq!(arr.len(), 1);
3488                assert_eq!(arr[0], Value::Bool(true));
3489            }
3490            _ => panic!("Expected array"),
3491        }
3492
3493        // Test repeat with negative count (should error)
3494        let value = Value::String("test".to_string());
3495        let count = Value::Int(-1);
3496        let result = registry.call_function("repeat", &[value, count]);
3497        assert!(result.is_err());
3498        assert!(result
3499            .unwrap_err()
3500            .to_string()
3501            .contains("non-negative integer"));
3502
3503        // Test repeat with wrong number of arguments
3504        let result = registry.call_function("repeat", &[Value::String("test".to_string())]);
3505        assert!(result.is_err());
3506        assert!(result
3507            .unwrap_err()
3508            .to_string()
3509            .contains("expects 2 arguments"));
3510
3511        let result = registry.call_function(
3512            "repeat",
3513            &[
3514                Value::String("test".to_string()),
3515                Value::Int(1),
3516                Value::Int(2),
3517            ],
3518        );
3519        assert!(result.is_err());
3520        assert!(result
3521            .unwrap_err()
3522            .to_string()
3523            .contains("expects 2 arguments"));
3524
3525        // Test repeat with invalid count type
3526        let value = Value::String("test".to_string());
3527        let count = Value::String("not a number".to_string());
3528        let result = registry.call_function("repeat", &[value, count]);
3529        assert!(result.is_err());
3530        assert!(result
3531            .unwrap_err()
3532            .to_string()
3533            .contains("non-negative integer"));
3534    }
3535
3536    #[test]
3537    fn test_builtin_avg() {
3538        let registry = BuiltinRegistry::new();
3539
3540        // Test avg with array of integers
3541        let values = Value::Array(vec![Value::Int(10), Value::Int(20), Value::Int(30)]);
3542        let result = registry.call_function("avg", &[values]).unwrap();
3543        assert_eq!(result, Value::Float(20.0));
3544
3545        // Test avg with array of floats
3546        let values = Value::Array(vec![
3547            Value::Float(1.5),
3548            Value::Float(2.5),
3549            Value::Float(3.5),
3550        ]);
3551        let result = registry.call_function("avg", &[values]).unwrap();
3552        assert_eq!(result, Value::Float(2.5));
3553
3554        // Test avg with mixed int and float
3555        let values = Value::Array(vec![Value::Int(10), Value::Float(20.0), Value::Int(30)]);
3556        let result = registry.call_function("avg", &[values]).unwrap();
3557        assert_eq!(result, Value::Float(20.0));
3558
3559        // Test avg with empty array
3560        let values = Value::Array(vec![]);
3561        let result = registry.call_function("avg", &[values]).unwrap();
3562        assert_eq!(result, Value::Null);
3563
3564        // Test avg with array containing non-numeric values (should ignore them)
3565        let values = Value::Array(vec![
3566            Value::Int(10),
3567            Value::String("ignore".to_string()),
3568            Value::Int(30),
3569        ]);
3570        let result = registry.call_function("avg", &[values]).unwrap();
3571        assert_eq!(result, Value::Float(20.0));
3572
3573        // Test avg with no arguments (should error)
3574        let result = registry.call_function("avg", &[]);
3575        assert!(result.is_err());
3576        assert!(result
3577            .unwrap_err()
3578            .to_string()
3579            .contains("expects 1 argument"));
3580
3581        // Test avg with too many arguments (should error)
3582        let result = registry.call_function("avg", &[Value::Array(vec![]), Value::Array(vec![])]);
3583        assert!(result.is_err());
3584        assert!(result
3585            .unwrap_err()
3586            .to_string()
3587            .contains("expects 1 argument"));
3588    }
3589
3590    #[test]
3591    fn test_builtin_correl_arrays() {
3592        let registry = BuiltinRegistry::new();
3593
3594        // Test perfect positive correlation
3595        let arr1 = Value::Array(vec![
3596            Value::Int(1),
3597            Value::Int(2),
3598            Value::Int(3),
3599            Value::Int(4),
3600        ]);
3601        let arr2 = Value::Array(vec![
3602            Value::Int(2),
3603            Value::Int(4),
3604            Value::Int(6),
3605            Value::Int(8),
3606        ]);
3607        let result = registry.call_function("correl", &[arr1, arr2]).unwrap();
3608        assert_eq!(result, Value::Float(1.0));
3609
3610        // Test perfect negative correlation
3611        let arr1 = Value::Array(vec![
3612            Value::Int(1),
3613            Value::Int(2),
3614            Value::Int(3),
3615            Value::Int(4),
3616        ]);
3617        let arr2 = Value::Array(vec![
3618            Value::Int(8),
3619            Value::Int(6),
3620            Value::Int(4),
3621            Value::Int(2),
3622        ]);
3623        let result = registry.call_function("correl", &[arr1, arr2]).unwrap();
3624        assert_eq!(result, Value::Float(-1.0));
3625
3626        // Test no correlation
3627        let arr1 = Value::Array(vec![
3628            Value::Int(1),
3629            Value::Int(1),
3630            Value::Int(1),
3631            Value::Int(1),
3632        ]);
3633        let arr2 = Value::Array(vec![
3634            Value::Int(1),
3635            Value::Int(2),
3636            Value::Int(3),
3637            Value::Int(4),
3638        ]);
3639        let result = registry.call_function("correl", &[arr1, arr2]).unwrap();
3640        assert_eq!(result, Value::Float(0.0));
3641
3642        // Test with floats
3643        let arr1 = Value::Array(vec![
3644            Value::Float(1.0),
3645            Value::Float(2.0),
3646            Value::Float(3.0),
3647        ]);
3648        let arr2 = Value::Array(vec![
3649            Value::Float(1.0),
3650            Value::Float(2.0),
3651            Value::Float(3.0),
3652        ]);
3653        let result = registry.call_function("correl", &[arr1, arr2]).unwrap();
3654        assert_eq!(result, Value::Float(1.0));
3655    }
3656
3657    #[test]
3658    fn test_builtin_correl_series() {
3659        let registry = BuiltinRegistry::new();
3660
3661        // Test with Series (should return Null for now as placeholder)
3662        let series1 = Value::Series(Series::new(PlSmallStr::from("a"), &[1, 2, 3, 4]));
3663        let series2 = Value::Series(Series::new(PlSmallStr::from("b"), &[2, 4, 6, 8]));
3664        let result = registry
3665            .call_function("correl", &[series1, series2])
3666            .unwrap();
3667        assert_eq!(result, Value::Null);
3668    }
3669
3670    #[test]
3671    fn test_builtin_correl_errors() {
3672        let registry = BuiltinRegistry::new();
3673
3674        // Test with wrong number of arguments (0 args)
3675        let result = registry.call_function("correl", &[]);
3676        assert!(result.is_err());
3677        assert!(result
3678            .unwrap_err()
3679            .to_string()
3680            .contains("expects 2 arguments"));
3681
3682        // Test with wrong number of arguments (1 arg)
3683        let arr = Value::Array(vec![Value::Int(1), Value::Int(2)]);
3684        let result = registry.call_function("correl", &[arr]);
3685        assert!(result.is_err());
3686        assert!(result
3687            .unwrap_err()
3688            .to_string()
3689            .contains("expects 2 arguments"));
3690
3691        // Test with wrong number of arguments (3 args)
3692        let arr = Value::Array(vec![Value::Int(1), Value::Int(2)]);
3693        let result = registry.call_function("correl", &[arr.clone(), arr.clone(), arr]);
3694        assert!(result.is_err());
3695        assert!(result
3696            .unwrap_err()
3697            .to_string()
3698            .contains("expects 2 arguments"));
3699
3700        // Test with mismatched array lengths
3701        let arr1 = Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
3702        let arr2 = Value::Array(vec![Value::Int(1), Value::Int(2)]);
3703        let result = registry.call_function("correl", &[arr1, arr2]);
3704        assert!(result.is_err());
3705        assert!(result
3706            .unwrap_err()
3707            .to_string()
3708            .contains("requires two arrays or two series"));
3709
3710        // Test with non-array/non-series arguments
3711        let str1 = Value::String("hello".to_string());
3712        let str2 = Value::String("world".to_string());
3713        let result = registry.call_function("correl", &[str1, str2]);
3714        assert!(result.is_err());
3715        assert!(result
3716            .unwrap_err()
3717            .to_string()
3718            .contains("requires two arrays or two series"));
3719    }
3720
3721    #[test]
3722    fn test_builtin_correl_edge_cases() {
3723        let registry = BuiltinRegistry::new();
3724
3725        // Test with empty arrays
3726        let arr1 = Value::Array(vec![]);
3727        let arr2 = Value::Array(vec![]);
3728        let result = registry.call_function("correl", &[arr1, arr2]).unwrap();
3729        assert_eq!(result, Value::Null);
3730
3731        // Test with single element arrays
3732        let arr1 = Value::Array(vec![Value::Int(1)]);
3733        let arr2 = Value::Array(vec![Value::Int(2)]);
3734        let result = registry.call_function("correl", &[arr1, arr2]).unwrap();
3735        assert_eq!(result, Value::Null);
3736
3737        // Test with arrays containing nulls (should be filtered out)
3738        let arr1 = Value::Array(vec![Value::Int(1), Value::Null, Value::Int(3)]);
3739        let arr2 = Value::Array(vec![Value::Int(2), Value::Null, Value::Int(6)]);
3740        let result = registry.call_function("correl", &[arr1, arr2]).unwrap();
3741        assert_eq!(result, Value::Float(1.0)); // 1,3 and 2,6 should correlate perfectly
3742
3743        // Test with arrays containing non-numeric values (should be filtered out)
3744        let arr1 = Value::Array(vec![
3745            Value::Int(1),
3746            Value::String("ignore".to_string()),
3747            Value::Int(3),
3748        ]);
3749        let arr2 = Value::Array(vec![
3750            Value::Int(2),
3751            Value::String("ignore".to_string()),
3752            Value::Int(6),
3753        ]);
3754        let result = registry.call_function("correl", &[arr1, arr2]).unwrap();
3755        assert_eq!(result, Value::Float(1.0));
3756
3757        // Test with constant arrays (zero variance)
3758        let arr1 = Value::Array(vec![Value::Int(5), Value::Int(5), Value::Int(5)]);
3759        let arr2 = Value::Array(vec![Value::Int(1), Value::Int(2), Value::Int(3)]);
3760        let result = registry.call_function("correl", &[arr1, arr2]).unwrap();
3761        assert_eq!(result, Value::Float(0.0));
3762    }
3763
3764    #[test]
3765    fn test_url_set_domain_without_www() {
3766        let registry = BuiltinRegistry::new();
3767
3768        // Test with URL that has www
3769        let url_with_www = Value::String("https://www.example.com/path".to_string());
3770        let result = registry
3771            .call_function("url_set_domain_without_www", &[url_with_www])
3772            .unwrap();
3773        assert_eq!(
3774            result,
3775            Value::String("https://example.com/path".to_string())
3776        );
3777
3778        // Test with URL that doesn't have www
3779        let url_without_www = Value::String("https://example.com/path".to_string());
3780        let result = registry
3781            .call_function("url_set_domain_without_www", &[url_without_www])
3782            .unwrap();
3783        assert_eq!(
3784            result,
3785            Value::String("https://example.com/path".to_string())
3786        );
3787
3788        // Test with subdomain that starts with www
3789        let subdomain = Value::String("https://www.sub.example.com/path".to_string());
3790        let result = registry
3791            .call_function("url_set_domain_without_www", &[subdomain])
3792            .unwrap();
3793        assert_eq!(
3794            result,
3795            Value::String("https://sub.example.com/path".to_string())
3796        );
3797
3798        // Test with URL without path
3799        let no_path = Value::String("https://www.example.com".to_string());
3800        let result = registry
3801            .call_function("url_set_domain_without_www", &[no_path])
3802            .unwrap();
3803        assert_eq!(result, Value::String("https://example.com/".to_string()));
3804
3805        // Test with invalid URL
3806        let invalid_url = Value::String("not-a-url".to_string());
3807        let result = registry
3808            .call_function("url_set_domain_without_www", &[invalid_url])
3809            .unwrap();
3810        assert_eq!(result, Value::String("not-a-url".to_string()));
3811
3812        // Test with array
3813        let urls = Value::Array(vec![
3814            Value::String("https://www.example.com".to_string()),
3815            Value::String("https://test.com".to_string()),
3816        ]);
3817        let result = registry
3818            .call_function("url_set_domain_without_www", &[urls])
3819            .unwrap();
3820        assert_eq!(
3821            result,
3822            Value::Array(vec![
3823                Value::String("https://example.com/".to_string()),
3824                Value::String("https://test.com/".to_string()),
3825            ])
3826        );
3827    }
3828
3829    #[test]
3830    fn test_transform_values_registered() {
3831        let registry = BuiltinRegistry::new();
3832        assert!(registry.has_function("transform_values"));
3833        let names = registry.function_names();
3834        assert!(names.contains(&"transform_values".to_string()));
3835    }
3836
3837    #[test]
3838    fn test_least_frequent() {
3839        let registry = BuiltinRegistry::new();
3840
3841        // Test with array of strings
3842        let arr = Value::Array(vec![
3843            Value::String("a".to_string()),
3844            Value::String("b".to_string()),
3845            Value::String("a".to_string()),
3846            Value::String("c".to_string()),
3847            Value::String("b".to_string()),
3848            Value::String("a".to_string()),
3849        ]);
3850        let result = registry.call_function("least_frequent", &[arr]).unwrap();
3851        assert_eq!(result, Value::String("c".to_string())); // "c" appears once, others more
3852
3853        // Test with array of numbers
3854        let arr = Value::Array(vec![
3855            Value::Int(1),
3856            Value::Int(2),
3857            Value::Int(1),
3858            Value::Int(3),
3859            Value::Int(2),
3860            Value::Int(1),
3861        ]);
3862        let result = registry.call_function("least_frequent", &[arr]).unwrap();
3863        assert_eq!(result, Value::Int(3)); // 3 appears once
3864
3865        // Test with empty array
3866        let arr = Value::Array(vec![]);
3867        let result = registry.call_function("least_frequent", &[arr]).unwrap();
3868        assert_eq!(result, Value::Null);
3869
3870        // Test with single element
3871        let arr = Value::Array(vec![Value::String("single".to_string())]);
3872        let result = registry.call_function("least_frequent", &[arr]).unwrap();
3873        assert_eq!(result, Value::String("single".to_string()));
3874
3875        // Test with all same frequency
3876        let arr = Value::Array(vec![
3877            Value::String("x".to_string()),
3878            Value::String("y".to_string()),
3879            Value::String("z".to_string()),
3880        ]);
3881        let result = registry.call_function("least_frequent", &[arr]).unwrap();
3882        // Should return one of them, arbitrary choice
3883        match result {
3884            Value::String(s) if s == "x" || s == "y" || s == "z" => {}
3885            _ => panic!("Expected one of x, y, z"),
3886        }
3887
3888        // Test with DataFrame
3889        let names = Series::new(
3890            PlSmallStr::from("name"),
3891            &["Alice", "Bob", "Alice", "Charlie"],
3892        );
3893        let ages = Series::new(PlSmallStr::from("age"), &[25, 30, 25, 35]);
3894        let df = DataFrame::new(vec![names.into(), ages.into()]).unwrap();
3895        let df_val = Value::DataFrame(df);
3896        let result = registry.call_function("least_frequent", &[df_val]).unwrap();
3897        // Charlie appears once, others more
3898        assert_eq!(result, Value::String("Charlie".to_string()));
3899
3900        // Test with Series
3901        let series = Series::new(PlSmallStr::from("values"), &[1, 2, 1, 3, 2, 1]);
3902        let series_val = Value::Series(series);
3903        let result = registry
3904            .call_function("least_frequent", &[series_val])
3905            .unwrap();
3906        assert_eq!(result, Value::Int(3));
3907
3908        // Test error cases
3909        let result = registry.call_function("least_frequent", &[]);
3910        assert!(result.is_err());
3911
3912        let result =
3913            registry.call_function("least_frequent", &[Value::String("not array".to_string())]);
3914        assert!(result.is_err());
3915    }
3916
3917    #[test]
3918    fn test_transpose_registered() {
3919        let registry = BuiltinRegistry::new();
3920        assert!(registry.has_function("transpose"));
3921    }
3922
3923    #[test]
3924    fn test_iferror_via_registry() {
3925        let registry = BuiltinRegistry::new();
3926
3927        // Test iferror is registered
3928        assert!(registry.has_function("iferror"));
3929
3930        // Test with null first argument
3931        let result = registry
3932            .call_function(
3933                "iferror",
3934                &[Value::Null, Value::String("default".to_string())],
3935            )
3936            .unwrap();
3937        assert_eq!(result, Value::String("default".to_string()));
3938
3939        // Test with non-null first argument
3940        let result = registry
3941            .call_function(
3942                "iferror",
3943                &[Value::Int(42), Value::String("default".to_string())],
3944            )
3945            .unwrap();
3946        assert_eq!(result, Value::Int(42));
3947
3948        // Test error with wrong number of args
3949        let result = registry.call_function("iferror", &[Value::Null]);
3950        assert!(result.is_err());
3951        assert!(result
3952            .unwrap_err()
3953            .to_string()
3954            .contains("iferror() expects 2 arguments"));
3955    }
3956
3957    #[test]
3958    fn test_truncate_date_function() {
3959        let registry = BuiltinRegistry::new();
3960
3961        // Test truncate to year
3962        let result = registry
3963            .call_function(
3964                "truncate_date",
3965                &[
3966                    Value::String("2021-06-15T14:30:45Z".to_string()),
3967                    Value::String("year".to_string()),
3968                ],
3969            )
3970            .unwrap();
3971        // 2021-01-01 00:00:00 UTC timestamp
3972        assert_eq!(result, Value::Int(1609459200));
3973
3974        // Test truncate to month
3975        let result = registry
3976            .call_function(
3977                "truncate_date",
3978                &[
3979                    Value::String("2021-06-15T14:30:45Z".to_string()),
3980                    Value::String("month".to_string()),
3981                ],
3982            )
3983            .unwrap();
3984        // 2021-06-01 00:00:00 UTC timestamp
3985        assert_eq!(result, Value::Int(1622505600));
3986
3987        // Test truncate to day
3988        let result = registry
3989            .call_function(
3990                "truncate_date",
3991                &[
3992                    Value::String("2021-06-15T14:30:45Z".to_string()),
3993                    Value::String("day".to_string()),
3994                ],
3995            )
3996            .unwrap();
3997        // 2021-06-15 00:00:00 UTC timestamp
3998        assert_eq!(result, Value::Int(1623715200));
3999
4000        // Test truncate to hour
4001        let result = registry
4002            .call_function(
4003                "truncate_date",
4004                &[
4005                    Value::String("2021-06-15T14:30:45Z".to_string()),
4006                    Value::String("hour".to_string()),
4007                ],
4008            )
4009            .unwrap();
4010        // 2021-06-15 14:00:00 UTC timestamp
4011        assert_eq!(result, Value::Int(1623765600));
4012
4013        // Test truncate to minute
4014        let result = registry
4015            .call_function(
4016                "truncate_date",
4017                &[
4018                    Value::String("2021-06-15T14:30:45Z".to_string()),
4019                    Value::String("minute".to_string()),
4020                ],
4021            )
4022            .unwrap();
4023        // 2021-06-15 14:30:00 UTC timestamp
4024        assert_eq!(result, Value::Int(1623767400));
4025
4026        // Test with integer timestamp
4027        let result = registry
4028            .call_function(
4029                "truncate_date",
4030                &[Value::Int(1623767400), Value::String("day".to_string())],
4031            )
4032            .unwrap();
4033        assert_eq!(result, Value::Int(1623715200));
4034
4035        // Test invalid unit
4036        let result = registry.call_function(
4037            "truncate_date",
4038            &[
4039                Value::String("2021-06-15T14:30:45Z".to_string()),
4040                Value::String("invalid".to_string()),
4041            ],
4042        );
4043        assert!(result.is_err());
4044        assert!(result
4045            .unwrap_err()
4046            .to_string()
4047            .contains("truncate_date() invalid unit"));
4048
4049        // Test wrong number of arguments
4050        let result = registry.call_function(
4051            "truncate_date",
4052            &[Value::String("2021-06-15T14:30:45Z".to_string())],
4053        );
4054        assert!(result.is_err());
4055        assert!(result
4056            .unwrap_err()
4057            .to_string()
4058            .contains("truncate_date() expects 2 arguments"));
4059
4060        // Test invalid date
4061        let result = registry.call_function(
4062            "truncate_date",
4063            &[
4064                Value::String("invalid-date".to_string()),
4065                Value::String("day".to_string()),
4066            ],
4067        );
4068        assert!(result.is_err());
4069    }
4070
4071    #[test]
4072    fn test_url_strip_port_if_default() {
4073        let registry = BuiltinRegistry::new();
4074
4075        // Test HTTP default port (80) - should be stripped
4076        let result = registry
4077            .call_function(
4078                "url_strip_port_if_default",
4079                &[Value::String("http://example.com:80/path".to_string())],
4080            )
4081            .unwrap();
4082        assert_eq!(result, Value::String("http://example.com/path".to_string()));
4083
4084        // Test HTTPS default port (443) - should be stripped
4085        let result = registry
4086            .call_function(
4087                "url_strip_port_if_default",
4088                &[Value::String("https://example.com:443/path".to_string())],
4089            )
4090            .unwrap();
4091        assert_eq!(
4092            result,
4093            Value::String("https://example.com/path".to_string())
4094        );
4095
4096        // Test FTP default port (21) - should be stripped
4097        let result = registry
4098            .call_function(
4099                "url_strip_port_if_default",
4100                &[Value::String("ftp://example.com:21/path".to_string())],
4101            )
4102            .unwrap();
4103        assert_eq!(result, Value::String("ftp://example.com/path".to_string()));
4104
4105        // Test SSH default port (22) - should be stripped
4106        let result = registry
4107            .call_function(
4108                "url_strip_port_if_default",
4109                &[Value::String("ssh://example.com:22/path".to_string())],
4110            )
4111            .unwrap();
4112        assert_eq!(result, Value::String("ssh://example.com/path".to_string()));
4113
4114        // Test Telnet default port (23) - should be stripped
4115        let result = registry
4116            .call_function(
4117                "url_strip_port_if_default",
4118                &[Value::String("telnet://example.com:23/".to_string())],
4119            )
4120            .unwrap();
4121        assert_eq!(result, Value::String("telnet://example.com/".to_string()));
4122
4123        // Test non-default port - should NOT be stripped
4124        let result = registry
4125            .call_function(
4126                "url_strip_port_if_default",
4127                &[Value::String("http://example.com:8080/path".to_string())],
4128            )
4129            .unwrap();
4130        assert_eq!(
4131            result,
4132            Value::String("http://example.com:8080/path".to_string())
4133        );
4134
4135        // Test HTTPS with non-default port - should NOT be stripped
4136        let result = registry
4137            .call_function(
4138                "url_strip_port_if_default",
4139                &[Value::String("https://example.com:8443/path".to_string())],
4140            )
4141            .unwrap();
4142        assert_eq!(
4143            result,
4144            Value::String("https://example.com:8443/path".to_string())
4145        );
4146
4147        // Test URL without port - should remain unchanged
4148        let result = registry
4149            .call_function(
4150                "url_strip_port_if_default",
4151                &[Value::String("http://example.com/path".to_string())],
4152            )
4153            .unwrap();
4154        assert_eq!(result, Value::String("http://example.com/path".to_string()));
4155
4156        // Test invalid URL - should return original string
4157        let result = registry
4158            .call_function(
4159                "url_strip_port_if_default",
4160                &[Value::String("not-a-url".to_string())],
4161            )
4162            .unwrap();
4163        assert_eq!(result, Value::String("not-a-url".to_string()));
4164
4165        // Test with array of URLs
4166        let urls = vec![
4167            Value::String("http://example.com:80/".to_string()),
4168            Value::String("https://example.com:443/".to_string()),
4169            Value::String("http://example.com:8080/".to_string()),
4170        ];
4171        let result = registry
4172            .call_function("url_strip_port_if_default", &[Value::Array(urls)])
4173            .unwrap();
4174        match result {
4175            Value::Array(arr) => {
4176                assert_eq!(arr.len(), 3);
4177                assert_eq!(arr[0], Value::String("http://example.com/".to_string()));
4178                assert_eq!(arr[1], Value::String("https://example.com/".to_string()));
4179                assert_eq!(
4180                    arr[2],
4181                    Value::String("http://example.com:8080/".to_string())
4182                );
4183            }
4184            _ => panic!("Expected Array"),
4185        }
4186
4187        // Test with invalid input type
4188        let result = registry.call_function("url_strip_port_if_default", &[Value::Int(42)]);
4189        assert!(result.is_err());
4190        assert!(result
4191            .unwrap_err()
4192            .to_string()
4193            .contains("url_strip_port_if_default() requires string, array, DataFrame, or Series"));
4194
4195        // Test with wrong number of arguments
4196        let result = registry.call_function("url_strip_port_if_default", &[]);
4197        assert!(result.is_err());
4198        assert!(result
4199            .unwrap_err()
4200            .to_string()
4201            .contains("url_strip_port_if_default() expects 1 argument"));
4202
4203        let result = registry.call_function(
4204            "url_strip_port_if_default",
4205            &[
4206                Value::String("test".to_string()),
4207                Value::String("extra".to_string()),
4208            ],
4209        );
4210        assert!(result.is_err());
4211        assert!(result
4212            .unwrap_err()
4213            .to_string()
4214            .contains("url_strip_port_if_default() expects 1 argument"));
4215    }
4216
4217    #[test]
4218    fn test_url_strip_port() {
4219        let registry = BuiltinRegistry::new();
4220
4221        // Test stripping port from URL with port
4222        let result = registry
4223            .call_function(
4224                "url_strip_port",
4225                &[Value::String("http://example.com:8080/path".to_string())],
4226            )
4227            .unwrap();
4228        assert_eq!(result, Value::String("http://example.com/path".to_string()));
4229
4230        // Test stripping port from HTTPS URL
4231        let result = registry
4232            .call_function(
4233                "url_strip_port",
4234                &[Value::String("https://example.com:8443/path".to_string())],
4235            )
4236            .unwrap();
4237        assert_eq!(
4238            result,
4239            Value::String("https://example.com/path".to_string())
4240        );
4241
4242        // Test URL without port - should remain unchanged
4243        let result = registry
4244            .call_function(
4245                "url_strip_port",
4246                &[Value::String("http://example.com/path".to_string())],
4247            )
4248            .unwrap();
4249        assert_eq!(result, Value::String("http://example.com/path".to_string()));
4250
4251        // Test invalid URL - should return original string
4252        let result = registry
4253            .call_function("url_strip_port", &[Value::String("not-a-url".to_string())])
4254            .unwrap();
4255        assert_eq!(result, Value::String("not-a-url".to_string()));
4256
4257        // Test with array of URLs
4258        let urls = vec![
4259            Value::String("http://example.com:8080/".to_string()),
4260            Value::String("https://example.com:8443/".to_string()),
4261            Value::String("http://example.com/".to_string()),
4262        ];
4263        let result = registry
4264            .call_function("url_strip_port", &[Value::Array(urls)])
4265            .unwrap();
4266        match result {
4267            Value::Array(arr) => {
4268                assert_eq!(arr.len(), 3);
4269                assert_eq!(arr[0], Value::String("http://example.com/".to_string()));
4270                assert_eq!(arr[1], Value::String("https://example.com/".to_string()));
4271                assert_eq!(arr[2], Value::String("http://example.com/".to_string()));
4272            }
4273            _ => panic!("Expected Array"),
4274        }
4275
4276        // Test with invalid input type
4277        let result = registry.call_function("url_strip_port", &[Value::Int(42)]);
4278        assert!(result.is_err());
4279        assert!(result
4280            .unwrap_err()
4281            .to_string()
4282            .contains("url_strip_port() requires string, array, DataFrame, or Series"));
4283
4284        // Test with wrong number of arguments
4285        let result = registry.call_function("url_strip_port", &[]);
4286        assert!(result.is_err());
4287        assert!(result
4288            .unwrap_err()
4289            .to_string()
4290            .contains("url_strip_port() expects 1 argument"));
4291
4292        let result = registry.call_function(
4293            "url_strip_port",
4294            &[
4295                Value::String("test".to_string()),
4296                Value::String("extra".to_string()),
4297            ],
4298        );
4299        assert!(result.is_err());
4300        assert!(result
4301            .unwrap_err()
4302            .to_string()
4303            .contains("url_strip_port() expects 1 argument"));
4304    }
4305
4306    #[test]
4307    fn test_url_set_port() {
4308        let registry = BuiltinRegistry::new();
4309
4310        // Test setting port on URL without port
4311        let result = registry
4312            .call_function(
4313                "url_set_port",
4314                &[
4315                    Value::String("http://example.com/path".to_string()),
4316                    Value::Int(8080),
4317                ],
4318            )
4319            .unwrap();
4320        assert_eq!(
4321            result,
4322            Value::String("http://example.com:8080/path".to_string())
4323        );
4324
4325        // Test setting port on URL with existing port
4326        let result = registry
4327            .call_function(
4328                "url_set_port",
4329                &[
4330                    Value::String("http://example.com:80/path".to_string()),
4331                    Value::Int(8080),
4332                ],
4333            )
4334            .unwrap();
4335        assert_eq!(
4336            result,
4337            Value::String("http://example.com:8080/path".to_string())
4338        );
4339
4340        // Test setting port with string port number
4341        let result = registry
4342            .call_function(
4343                "url_set_port",
4344                &[
4345                    Value::String("https://example.com/path".to_string()),
4346                    Value::String("8443".to_string()),
4347                ],
4348            )
4349            .unwrap();
4350        assert_eq!(
4351            result,
4352            Value::String("https://example.com:8443/path".to_string())
4353        );
4354
4355        // Test setting port 0
4356        let result = registry
4357            .call_function(
4358                "url_set_port",
4359                &[
4360                    Value::String("http://example.com/path".to_string()),
4361                    Value::Int(0),
4362                ],
4363            )
4364            .unwrap();
4365        assert_eq!(
4366            result,
4367            Value::String("http://example.com:0/path".to_string())
4368        );
4369
4370        // Test setting maximum port
4371        let result = registry
4372            .call_function(
4373                "url_set_port",
4374                &[
4375                    Value::String("http://example.com/path".to_string()),
4376                    Value::Int(65535),
4377                ],
4378            )
4379            .unwrap();
4380        assert_eq!(
4381            result,
4382            Value::String("http://example.com:65535/path".to_string())
4383        );
4384
4385        // Test invalid URL - should return original
4386        let result = registry
4387            .call_function(
4388                "url_set_port",
4389                &[Value::String("not-a-url".to_string()), Value::Int(8080)],
4390            )
4391            .unwrap();
4392        assert_eq!(result, Value::String("not-a-url".to_string()));
4393
4394        // Test with array
4395        let urls = Value::Array(vec![
4396            Value::String("http://example.com/".to_string()),
4397            Value::String("https://test.com/path".to_string()),
4398        ]);
4399        let result = registry
4400            .call_function("url_set_port", &[urls, Value::Int(9000)])
4401            .unwrap();
4402        match result {
4403            Value::Array(arr) => {
4404                assert_eq!(arr.len(), 2);
4405                assert_eq!(
4406                    arr[0],
4407                    Value::String("http://example.com:9000/".to_string())
4408                );
4409                assert_eq!(
4410                    arr[1],
4411                    Value::String("https://test.com:9000/path".to_string())
4412                );
4413            }
4414            _ => panic!("Expected Array"),
4415        }
4416
4417        // Test invalid port number (string)
4418        let result = registry.call_function(
4419            "url_set_port",
4420            &[
4421                Value::String("http://example.com/".to_string()),
4422                Value::String("invalid".to_string()),
4423            ],
4424        );
4425        assert!(result.is_err());
4426        assert!(result
4427            .unwrap_err()
4428            .to_string()
4429            .contains("Invalid port number"));
4430
4431        // Test invalid second argument type
4432        let result = registry.call_function(
4433            "url_set_port",
4434            &[
4435                Value::String("http://example.com/".to_string()),
4436                Value::Float(8080.0),
4437            ],
4438        );
4439        assert!(result.is_err());
4440        assert!(result
4441            .unwrap_err()
4442            .to_string()
4443            .contains("url_set_port() second argument must be an integer or string"));
4444
4445        // Test wrong number of arguments (0 args)
4446        let result = registry.call_function("url_set_port", &[]);
4447        assert!(result.is_err());
4448        assert!(result
4449            .unwrap_err()
4450            .to_string()
4451            .contains("url_set_port() expects 2 arguments"));
4452
4453        // Test wrong number of arguments (1 arg)
4454        let result = registry.call_function(
4455            "url_set_port",
4456            &[Value::String("http://example.com/".to_string())],
4457        );
4458        assert!(result.is_err());
4459        assert!(result
4460            .unwrap_err()
4461            .to_string()
4462            .contains("url_set_port() expects 2 arguments"));
4463
4464        // Test wrong number of arguments (3 args)
4465        let result = registry.call_function(
4466            "url_set_port",
4467            &[
4468                Value::String("http://example.com/".to_string()),
4469                Value::Int(8080),
4470                Value::Int(1),
4471            ],
4472        );
4473        assert!(result.is_err());
4474        assert!(result
4475            .unwrap_err()
4476            .to_string()
4477            .contains("url_set_port() expects 2 arguments"));
4478
4479        // Test with DataFrame
4480        let df = DataFrame::new(vec![Column::new(
4481            PlSmallStr::from("urls"),
4482            &["http://example.com/", "https://test.com/path"],
4483        )])
4484        .unwrap();
4485        let result = registry
4486            .call_function("url_set_port", &[Value::DataFrame(df), Value::Int(9000)])
4487            .unwrap();
4488        match result {
4489            Value::DataFrame(result_df) => {
4490                let urls_col = result_df.column("urls").unwrap().str().unwrap();
4491                assert_eq!(urls_col.get(0).unwrap(), "http://example.com:9000/");
4492                assert_eq!(urls_col.get(1).unwrap(), "https://test.com:9000/path");
4493            }
4494            _ => panic!("Expected DataFrame"),
4495        }
4496
4497        // Test with Series
4498        let series = Series::new(
4499            "urls".into(),
4500            &["http://example.com/", "https://test.com/path"],
4501        );
4502        let result = registry
4503            .call_function("url_set_port", &[Value::Series(series), Value::Int(9000)])
4504            .unwrap();
4505        match result {
4506            Value::Series(result_series) => {
4507                let urls_col = result_series.str().unwrap();
4508                assert_eq!(urls_col.get(0).unwrap(), "http://example.com:9000/");
4509                assert_eq!(urls_col.get(1).unwrap(), "https://test.com:9000/path");
4510            }
4511            _ => panic!("Expected Series"),
4512        }
4513
4514        // Test invalid input type
4515        let result = registry.call_function("url_set_port", &[Value::Int(42), Value::Int(8080)]);
4516        assert!(result.is_err());
4517        assert!(result
4518            .unwrap_err()
4519            .to_string()
4520            .contains("url_set_port() requires string, array, DataFrame, or Series"));
4521    }
4522
4523    #[test]
4524    fn test_time_series_range_function() {
4525        let registry = BuiltinRegistry::new();
4526
4527        // Test basic time series range with seconds
4528        let start = Value::Int(1609459200); // 2021-01-01 00:00:00 UTC
4529        let end = Value::Int(1609459260); // 2021-01-01 00:01:00 UTC
4530        let interval = Value::String("10s".to_string());
4531        let result = registry
4532            .call_function("time_series_range", &[start.clone(), end, interval])
4533            .unwrap();
4534        if let Value::Array(arr) = result {
4535            assert_eq!(arr.len(), 7); // 0, 10, 20, 30, 40, 50, 60 seconds
4536            assert_eq!(arr[0], Value::Int(1609459200));
4537            assert_eq!(arr[6], Value::Int(1609459260));
4538        } else {
4539            panic!("Expected array result");
4540        }
4541
4542        // Test with minutes
4543        let start = Value::Int(1609459200);
4544        let end = Value::Int(1609462800); // 1 hour later
4545        let interval = Value::String("15m".to_string());
4546        let result = registry
4547            .call_function("time_series_range", &[start, end, interval])
4548            .unwrap();
4549        if let Value::Array(arr) = result {
4550            assert_eq!(arr.len(), 5); // 0, 15, 30, 45, 60 minutes
4551        } else {
4552            panic!("Expected array result");
4553        }
4554
4555        // Test with start >= end (should return empty array)
4556        let start = Value::Int(1609459260);
4557        let end = Value::Int(1609459200);
4558        let interval = Value::String("10s".to_string());
4559        let result = registry
4560            .call_function("time_series_range", &[start, end, interval])
4561            .unwrap();
4562        if let Value::Array(arr) = result {
4563            assert_eq!(arr.len(), 0);
4564        } else {
4565            panic!("Expected array result");
4566        }
4567
4568        // Test with string timestamps
4569        let start = Value::String("2021-01-01T00:00:00Z".to_string());
4570        let end = Value::String("2021-01-01T00:00:30Z".to_string());
4571        let interval = Value::String("10s".to_string());
4572        let result = registry
4573            .call_function("time_series_range", &[start, end, interval])
4574            .unwrap();
4575        if let Value::Array(arr) = result {
4576            assert_eq!(arr.len(), 4); // 0, 10, 20, 30
4577        } else {
4578            panic!("Expected array result");
4579        }
4580
4581        // Test invalid interval
4582        let start = Value::Int(1609459200);
4583        let end = Value::Int(1609459260);
4584        let interval = Value::String("10x".to_string());
4585        let result = registry.call_function("time_series_range", &[start.clone(), end, interval]);
4586        assert!(result.is_err());
4587
4588        // Test wrong number of arguments
4589        let result = registry.call_function("time_series_range", &[start]);
4590        assert!(result.is_err());
4591    }
4592
4593    #[test]
4594    fn test_map_registered_via_inventory() {
4595        let registry = BuiltinRegistry::new();
4596        assert!(registry.has_function("map"));
4597        let function_names = registry.function_names();
4598        assert!(function_names.contains(&"map".to_string()));
4599    }
4600}