use crate::shared::models::UserSession; use crate::shared::state::AppState; use log::debug; use rhai::{Array, Dynamic, Engine}; use std::sync::Arc; pub fn sort_keyword(_state: &Arc, _user: UserSession, engine: &mut Engine) { engine.register_fn("SORT", |arr: Array| -> Array { sort_array(arr, false) }); engine.register_fn("sort", |arr: Array| -> Array { sort_array(arr, false) }); engine.register_fn("SORT", |arr: Array, direction: &str| -> Array { let desc = direction.eq_ignore_ascii_case("DESC") || direction.eq_ignore_ascii_case("DESCENDING"); sort_array(arr, desc) }); engine.register_fn("sort", |arr: Array, direction: &str| -> Array { let desc = direction.eq_ignore_ascii_case("DESC") || direction.eq_ignore_ascii_case("DESCENDING"); sort_array(arr, desc) }); engine.register_fn("SORT_ASC", |arr: Array| -> Array { sort_array(arr, false) }); engine.register_fn("sort_asc", |arr: Array| -> Array { sort_array(arr, false) }); engine.register_fn("SORT_DESC", |arr: Array| -> Array { sort_array(arr, true) }); engine.register_fn("sort_desc", |arr: Array| -> Array { sort_array(arr, true) }); debug!("Registered SORT keyword"); } fn sort_array(mut arr: Array, descending: bool) -> Array { arr.sort_by(|a, b| { let cmp = compare_dynamic(a, b); if descending { cmp.reverse() } else { cmp } }); arr } fn compare_dynamic(a: &Dynamic, b: &Dynamic) -> std::cmp::Ordering { if let (Some(a_num), Some(b_num)) = (to_f64(a), to_f64(b)) { return a_num .partial_cmp(&b_num) .unwrap_or(std::cmp::Ordering::Equal); } a.to_string().cmp(&b.to_string()) } fn to_f64(value: &Dynamic) -> Option { if value.is_int() { value.as_int().ok().map(|i| i as f64) } else if value.is_float() { value.as_float().ok() } else if value.is_string() { value .clone() .into_string() .ok() .and_then(|s| s.parse().ok()) } else { None } } #[cfg(test)] mod tests { use super::*; use rhai::{Array, Dynamic}; #[test] fn test_sort_integers() { let arr: Array = vec![ Dynamic::from(3), Dynamic::from(1), Dynamic::from(4), Dynamic::from(1), Dynamic::from(5), ]; let sorted = sort_array(arr, false); assert_eq!(sorted[0].as_int().unwrap(), 1); assert_eq!(sorted[1].as_int().unwrap(), 1); assert_eq!(sorted[2].as_int().unwrap(), 3); assert_eq!(sorted[3].as_int().unwrap(), 4); assert_eq!(sorted[4].as_int().unwrap(), 5); } #[test] fn test_sort_strings() { let arr: Array = vec![ Dynamic::from("banana"), Dynamic::from("apple"), Dynamic::from("cherry"), ]; let sorted = sort_array(arr, false); assert_eq!(sorted[0].clone().into_string().unwrap(), "apple"); assert_eq!(sorted[1].clone().into_string().unwrap(), "banana"); assert_eq!(sorted[2].clone().into_string().unwrap(), "cherry"); } #[test] fn test_sort_descending() { let arr: Array = vec![Dynamic::from(1), Dynamic::from(3), Dynamic::from(2)]; let sorted = sort_array(arr, true); assert_eq!(sorted[0].as_int().unwrap(), 3); assert_eq!(sorted[1].as_int().unwrap(), 2); assert_eq!(sorted[2].as_int().unwrap(), 1); } #[test] fn test_compare_dynamic_numbers() { let a = Dynamic::from(5); let b = Dynamic::from(3); assert_eq!(compare_dynamic(&a, &b), std::cmp::Ordering::Greater); } #[test] fn test_compare_dynamic_strings() { let a = Dynamic::from("apple"); let b = Dynamic::from("banana"); assert_eq!(compare_dynamic(&a, &b), std::cmp::Ordering::Less); } }