1use std::collections::{HashMap, HashSet};
13use std::hash::Hash;
14
15pub const DATE_UNITS: &[&str] = &["day", "week", "month", "quarter", "year", "year_month"];
17
18pub fn seq_get<T>(seq: &[T], index: isize) -> Option<&T> {
33 let len = seq.len() as isize;
34 if len == 0 {
35 return None;
36 }
37
38 let actual_index = if index < 0 { len + index } else { index };
39
40 if actual_index < 0 || actual_index >= len {
41 None
42 } else {
43 seq.get(actual_index as usize)
44 }
45}
46
47pub trait EnsureList {
51 type Item;
52 fn ensure_list(self) -> Vec<Self::Item>;
53}
54
55impl<T> EnsureList for Vec<T> {
56 type Item = T;
57 fn ensure_list(self) -> Vec<Self::Item> {
58 self
59 }
60}
61
62impl<T> EnsureList for Option<T> {
63 type Item = T;
64 fn ensure_list(self) -> Vec<Self::Item> {
65 match self {
66 Some(v) => vec![v],
67 None => vec![],
68 }
69 }
70}
71
72pub fn ensure_list<T>(value: T) -> Vec<T> {
74 vec![value]
75}
76
77pub fn ensure_list_option<T>(value: Option<T>) -> Vec<T> {
79 match value {
80 Some(v) => vec![v],
81 None => vec![],
82 }
83}
84
85pub fn csv(args: &[&str], sep: &str) -> String {
96 args.iter()
97 .filter(|s| !s.is_empty())
98 .copied()
99 .collect::<Vec<_>>()
100 .join(sep)
101}
102
103pub fn csv_default(args: &[&str]) -> String {
105 csv(args, ", ")
106}
107
108pub fn while_changing<T, F>(mut value: T, func: F) -> T
127where
128 T: Clone + PartialEq,
129 F: Fn(T) -> T,
130{
131 loop {
132 let new_value = func(value.clone());
133 if new_value == value {
134 return new_value;
135 }
136 value = new_value;
137 }
138}
139
140pub fn while_changing_hash<T, F, H>(mut value: T, func: F, hasher: H) -> T
144where
145 F: Fn(T) -> T,
146 H: Fn(&T) -> u64,
147{
148 loop {
149 let start_hash = hasher(&value);
150 value = func(value);
151 let end_hash = hasher(&value);
152 if start_hash == end_hash {
153 return value;
154 }
155 }
156}
157
158pub fn tsort<T>(mut dag: HashMap<T, HashSet<T>>) -> Result<Vec<T>, TsortError>
183where
184 T: Clone + Eq + Hash + Ord,
185{
186 let mut result = Vec::new();
187
188 let all_deps: Vec<T> = dag
190 .values()
191 .flat_map(|deps| deps.iter().cloned())
192 .collect();
193
194 for dep in all_deps {
195 dag.entry(dep).or_insert_with(HashSet::new);
196 }
197
198 while !dag.is_empty() {
199 let mut current: Vec<T> = dag
201 .iter()
202 .filter(|(_, deps)| deps.is_empty())
203 .map(|(node, _)| node.clone())
204 .collect();
205
206 if current.is_empty() {
207 return Err(TsortError::CycleDetected);
208 }
209
210 current.sort();
212
213 for node in ¤t {
215 dag.remove(node);
216 }
217
218 let current_set: HashSet<_> = current.iter().cloned().collect();
220 for deps in dag.values_mut() {
221 *deps = deps.difference(¤t_set).cloned().collect();
222 }
223
224 result.extend(current);
225 }
226
227 Ok(result)
228}
229
230#[derive(Debug, Clone, PartialEq, Eq)]
232pub enum TsortError {
233 CycleDetected,
234}
235
236impl std::fmt::Display for TsortError {
237 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
238 match self {
239 TsortError::CycleDetected => write!(f, "Cycle detected in DAG"),
240 }
241 }
242}
243
244impl std::error::Error for TsortError {}
245
246pub fn find_new_name(taken: &HashSet<String>, base: &str) -> String {
266 if !taken.contains(base) {
267 return base.to_string();
268 }
269
270 let mut i = 2;
271 loop {
272 let new_name = format!("{}_{}", base, i);
273 if !taken.contains(&new_name) {
274 return new_name;
275 }
276 i += 1;
277 }
278}
279
280pub fn name_sequence(prefix: &str) -> impl FnMut() -> String {
295 let prefix = prefix.to_string();
296 let mut counter = 0usize;
297 move || {
298 let name = format!("{}{}", prefix, counter);
299 counter += 1;
300 name
301 }
302}
303
304pub fn is_int(text: &str) -> bool {
317 text.parse::<i64>().is_ok()
318}
319
320pub fn is_float(text: &str) -> bool {
333 text.parse::<f64>().is_ok()
334}
335
336pub fn is_iso_date(text: &str) -> bool {
348 if text.len() != 10 {
350 return false;
351 }
352 let parts: Vec<&str> = text.split('-').collect();
353 if parts.len() != 3 {
354 return false;
355 }
356 if parts[0].len() != 4 || parts[1].len() != 2 || parts[2].len() != 2 {
357 return false;
358 }
359
360 let year: u32 = match parts[0].parse() {
361 Ok(y) => y,
362 Err(_) => return false,
363 };
364 let month: u32 = match parts[1].parse() {
365 Ok(m) => m,
366 Err(_) => return false,
367 };
368 let day: u32 = match parts[2].parse() {
369 Ok(d) => d,
370 Err(_) => return false,
371 };
372
373 if month < 1 || month > 12 {
374 return false;
375 }
376 if day < 1 || day > 31 {
377 return false;
378 }
379
380 let days_in_month = match month {
382 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
383 4 | 6 | 9 | 11 => 30,
384 2 => {
385 if (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0) {
387 29
388 } else {
389 28
390 }
391 }
392 _ => return false,
393 };
394
395 day <= days_in_month
396}
397
398pub fn is_iso_datetime(text: &str) -> bool {
406 if text.len() < 10 {
408 return false;
409 }
410
411 if !is_iso_date(&text[..10]) {
413 return false;
414 }
415
416 if text.len() > 10 {
418 let sep = text.chars().nth(10).expect("length checked above");
420 if sep != 'T' && sep != ' ' {
421 return false;
422 }
423
424 let time_str = &text[11..];
426
427 let time_end = time_str
429 .find('+')
430 .or_else(|| time_str.rfind('-'))
431 .or_else(|| time_str.find('Z'))
432 .unwrap_or(time_str.len());
433
434 let time_without_tz = &time_str[..time_end];
435
436 let (time_part, _frac_part) = match time_without_tz.find('.') {
438 Some(idx) => (&time_without_tz[..idx], Some(&time_without_tz[idx + 1..])),
439 None => (time_without_tz, None),
440 };
441
442 if time_part.len() < 8 {
444 if time_part.len() != 5 {
446 return false;
447 }
448 }
449
450 let parts: Vec<&str> = time_part.split(':').collect();
451 if parts.len() < 2 || parts.len() > 3 {
452 return false;
453 }
454
455 let hour: u32 = match parts[0].parse() {
456 Ok(h) => h,
457 Err(_) => return false,
458 };
459 let minute: u32 = match parts[1].parse() {
460 Ok(m) => m,
461 Err(_) => return false,
462 };
463
464 if hour > 23 || minute > 59 {
465 return false;
466 }
467
468 if parts.len() == 3 {
469 let second: u32 = match parts[2].parse() {
470 Ok(s) => s,
471 Err(_) => return false,
472 };
473 if second > 59 {
474 return false;
475 }
476 }
477 }
478
479 true
480}
481
482pub fn camel_to_snake_case(name: &str) -> String {
493 let mut result = String::with_capacity(name.len() + 4);
494 for (i, ch) in name.chars().enumerate() {
495 if ch.is_uppercase() && i > 0 {
496 result.push('_');
497 }
498 result.push(ch.to_ascii_uppercase());
499 }
500 result
501}
502
503pub fn snake_to_camel_case(name: &str) -> String {
514 let mut result = String::with_capacity(name.len());
515 let mut capitalize_next = true;
516
517 for ch in name.chars() {
518 if ch == '_' {
519 capitalize_next = true;
520 } else if capitalize_next {
521 result.push(ch.to_ascii_uppercase());
522 capitalize_next = false;
523 } else {
524 result.push(ch.to_ascii_lowercase());
525 }
526 }
527 result
528}
529
530pub fn dict_depth<K, V>(d: &HashMap<K, V>) -> usize
546where
547 K: std::hash::Hash + Eq,
548{
549 if d.is_empty() {
552 1
553 } else {
554 1
555 }
556}
557
558pub fn first<I, T>(mut iter: I) -> Option<T>
571where
572 I: Iterator<Item = T>,
573{
574 iter.next()
575}
576
577pub fn split_num_words(
600 value: &str,
601 sep: &str,
602 min_num_words: usize,
603 fill_from_start: bool,
604) -> Vec<Option<String>> {
605 let words: Vec<String> = value.split(sep).map(|s| s.to_string()).collect();
606 let num_words = words.len();
607
608 if num_words >= min_num_words {
609 return words.into_iter().map(Some).collect();
610 }
611
612 let padding = min_num_words - num_words;
613 let mut result = Vec::with_capacity(min_num_words);
614
615 if fill_from_start {
616 result.extend(std::iter::repeat(None).take(padding));
617 result.extend(words.into_iter().map(Some));
618 } else {
619 result.extend(words.into_iter().map(Some));
620 result.extend(std::iter::repeat(None).take(padding));
621 }
622
623 result
624}
625
626pub fn flatten<T: Clone>(values: &[Vec<T>]) -> Vec<T> {
631 values.iter().flat_map(|v| v.iter().cloned()).collect()
632}
633
634pub fn merge_ranges<T: Ord + Copy>(mut ranges: Vec<(T, T)>) -> Vec<(T, T)> {
651 if ranges.is_empty() {
652 return vec![];
653 }
654
655 ranges.sort_by(|a, b| a.0.cmp(&b.0));
656
657 let mut merged = vec![ranges[0]];
658
659 for (start, end) in ranges.into_iter().skip(1) {
660 let last = merged.last_mut().expect("merged initialized with at least one element");
661 if start <= last.1 {
662 last.1 = std::cmp::max(last.1, end);
663 } else {
664 merged.push((start, end));
665 }
666 }
667
668 merged
669}
670
671pub fn is_date_unit(unit: &str) -> bool {
673 DATE_UNITS.contains(&unit.to_lowercase().as_str())
674}
675
676pub fn apply_index_offset(expression: &str, offset: i64) -> Option<String> {
702 if offset == 0 {
703 return Some(expression.to_string());
704 }
705
706 if let Ok(value) = expression.parse::<i64>() {
708 return Some((value + offset).to_string());
709 }
710
711 None
713}
714
715#[derive(Debug, Clone)]
736pub struct SingleValuedMapping<K, V>
737where
738 K: Eq + Hash,
739{
740 keys: HashSet<K>,
741 value: V,
742}
743
744impl<K, V> SingleValuedMapping<K, V>
745where
746 K: Eq + Hash,
747{
748 pub fn new(keys: HashSet<K>, value: V) -> Self {
750 Self { keys, value }
751 }
752
753 pub fn from_iter<I: IntoIterator<Item = K>>(keys: I, value: V) -> Self {
755 Self {
756 keys: keys.into_iter().collect(),
757 value,
758 }
759 }
760
761 pub fn get(&self, key: &K) -> Option<&V> {
763 if self.keys.contains(key) {
764 Some(&self.value)
765 } else {
766 None
767 }
768 }
769
770 pub fn contains_key(&self, key: &K) -> bool {
772 self.keys.contains(key)
773 }
774
775 pub fn len(&self) -> usize {
777 self.keys.len()
778 }
779
780 pub fn is_empty(&self) -> bool {
782 self.keys.is_empty()
783 }
784
785 pub fn keys(&self) -> impl Iterator<Item = &K> {
787 self.keys.iter()
788 }
789
790 pub fn value(&self) -> &V {
792 &self.value
793 }
794
795 pub fn iter(&self) -> impl Iterator<Item = (&K, &V)> {
797 self.keys.iter().map(move |k| (k, &self.value))
798 }
799}
800
801pub fn to_bool(value: &str) -> Option<bool> {
815 let lower = value.to_lowercase();
816 match lower.as_str() {
817 "true" | "1" => Some(true),
818 "false" | "0" => Some(false),
819 _ => None,
820 }
821}
822
823#[cfg(test)]
824mod tests {
825 use super::*;
826
827 #[test]
828 fn test_seq_get() {
829 let v = vec![1, 2, 3, 4, 5];
830 assert_eq!(seq_get(&v, 0), Some(&1));
831 assert_eq!(seq_get(&v, 4), Some(&5));
832 assert_eq!(seq_get(&v, 5), None);
833 assert_eq!(seq_get(&v, -1), Some(&5));
834 assert_eq!(seq_get(&v, -5), Some(&1));
835 assert_eq!(seq_get(&v, -6), None);
836
837 let empty: Vec<i32> = vec![];
838 assert_eq!(seq_get(&empty, 0), None);
839 assert_eq!(seq_get(&empty, -1), None);
840 }
841
842 #[test]
843 fn test_csv() {
844 assert_eq!(csv(&["a", "b", "c"], ", "), "a, b, c");
845 assert_eq!(csv(&["a", "", "c"], ", "), "a, c");
846 assert_eq!(csv(&["", "", ""], ", "), "");
847 assert_eq!(csv(&["a"], ", "), "a");
848 }
849
850 #[test]
851 fn test_while_changing() {
852 let result = while_changing(16, |n| if n % 2 == 0 { n / 2 } else { n });
854 assert_eq!(result, 1);
855
856 let result = while_changing(5, |n| if n % 2 == 0 { n / 2 } else { n });
858 assert_eq!(result, 5);
859 }
860
861 #[test]
862 fn test_tsort() {
863 let mut dag = HashMap::new();
864 dag.insert("a", HashSet::from(["b", "c"]));
865 dag.insert("b", HashSet::from(["c"]));
866 dag.insert("c", HashSet::new());
867
868 let sorted = tsort(dag).unwrap();
869 assert_eq!(sorted, vec!["c", "b", "a"]);
870 }
871
872 #[test]
873 fn test_tsort_cycle() {
874 let mut dag = HashMap::new();
875 dag.insert("a", HashSet::from(["b"]));
876 dag.insert("b", HashSet::from(["a"]));
877
878 let result = tsort(dag);
879 assert!(result.is_err());
880 }
881
882 #[test]
883 fn test_find_new_name() {
884 let taken = HashSet::from(["col".to_string(), "col_2".to_string()]);
885 assert_eq!(find_new_name(&taken, "col"), "col_3");
886 assert_eq!(find_new_name(&taken, "other"), "other");
887
888 let empty = HashSet::new();
889 assert_eq!(find_new_name(&empty, "col"), "col");
890 }
891
892 #[test]
893 fn test_name_sequence() {
894 let mut gen = name_sequence("a");
895 assert_eq!(gen(), "a0");
896 assert_eq!(gen(), "a1");
897 assert_eq!(gen(), "a2");
898 }
899
900 #[test]
901 fn test_is_int() {
902 assert!(is_int("123"));
903 assert!(is_int("-456"));
904 assert!(is_int("0"));
905 assert!(!is_int("12.34"));
906 assert!(!is_int("abc"));
907 assert!(!is_int(""));
908 }
909
910 #[test]
911 fn test_is_float() {
912 assert!(is_float("12.34"));
913 assert!(is_float("123"));
914 assert!(is_float("-1.5e10"));
915 assert!(is_float("0.0"));
916 assert!(!is_float("abc"));
917 assert!(!is_float(""));
918 }
919
920 #[test]
921 fn test_is_iso_date() {
922 assert!(is_iso_date("2023-01-15"));
923 assert!(is_iso_date("2024-02-29")); assert!(!is_iso_date("2023-02-29")); assert!(!is_iso_date("01-15-2023"));
926 assert!(!is_iso_date("2023-13-01")); assert!(!is_iso_date("2023-01-32")); assert!(!is_iso_date("not a date"));
929 }
930
931 #[test]
932 fn test_is_iso_datetime() {
933 assert!(is_iso_datetime("2023-01-15T10:30:00"));
934 assert!(is_iso_datetime("2023-01-15 10:30:00"));
935 assert!(is_iso_datetime("2023-01-15T10:30:00.123456"));
936 assert!(is_iso_datetime("2023-01-15T10:30:00+00:00"));
937 assert!(is_iso_datetime("2023-01-15"));
938 assert!(!is_iso_datetime("not a datetime"));
939 assert!(!is_iso_datetime("2023-01-15X10:30:00")); }
941
942 #[test]
943 fn test_camel_to_snake_case() {
944 assert_eq!(camel_to_snake_case("camelCase"), "CAMEL_CASE");
945 assert_eq!(camel_to_snake_case("PascalCase"), "PASCAL_CASE");
946 assert_eq!(camel_to_snake_case("simple"), "SIMPLE");
947 }
948
949 #[test]
950 fn test_snake_to_camel_case() {
951 assert_eq!(snake_to_camel_case("snake_case"), "SnakeCase");
952 assert_eq!(snake_to_camel_case("my_http_server"), "MyHttpServer");
953 assert_eq!(snake_to_camel_case("simple"), "Simple");
954 }
955
956 #[test]
957 fn test_split_num_words() {
958 assert_eq!(
959 split_num_words("db.table", ".", 3, true),
960 vec![None, Some("db".to_string()), Some("table".to_string())]
961 );
962 assert_eq!(
963 split_num_words("db.table", ".", 3, false),
964 vec![Some("db".to_string()), Some("table".to_string()), None]
965 );
966 assert_eq!(
967 split_num_words("catalog.db.table", ".", 3, true),
968 vec![Some("catalog".to_string()), Some("db".to_string()), Some("table".to_string())]
969 );
970 assert_eq!(
971 split_num_words("db.table", ".", 1, true),
972 vec![Some("db".to_string()), Some("table".to_string())]
973 );
974 }
975
976 #[test]
977 fn test_merge_ranges() {
978 assert_eq!(merge_ranges(vec![(1, 3), (2, 6)]), vec![(1, 6)]);
979 assert_eq!(
980 merge_ranges(vec![(1, 3), (2, 6), (8, 10)]),
981 vec![(1, 6), (8, 10)]
982 );
983 assert_eq!(merge_ranges(vec![(1, 5), (2, 3)]), vec![(1, 5)]);
984 assert_eq!(merge_ranges::<i32>(vec![]), vec![]);
985 }
986
987 #[test]
988 fn test_is_date_unit() {
989 assert!(is_date_unit("day"));
990 assert!(is_date_unit("MONTH"));
991 assert!(is_date_unit("Year"));
992 assert!(!is_date_unit("hour"));
993 assert!(!is_date_unit("minute"));
994 }
995
996 #[test]
997 fn test_apply_index_offset() {
998 assert_eq!(apply_index_offset("0", 1), Some("1".to_string()));
1000 assert_eq!(apply_index_offset("5", 1), Some("6".to_string()));
1001 assert_eq!(apply_index_offset("10", -1), Some("9".to_string()));
1002
1003 assert_eq!(apply_index_offset("5", 0), Some("5".to_string()));
1005
1006 assert_eq!(apply_index_offset("-1", 1), Some("0".to_string()));
1008
1009 assert_eq!(apply_index_offset("col", 1), None);
1011 assert_eq!(apply_index_offset("1.5", 1), None);
1012 assert_eq!(apply_index_offset("abc", 1), None);
1013 }
1014
1015 #[test]
1016 fn test_single_valued_mapping() {
1017 let columns = HashSet::from([
1018 "id".to_string(),
1019 "name".to_string(),
1020 "email".to_string(),
1021 ]);
1022 let mapping = SingleValuedMapping::new(columns, "users".to_string());
1023
1024 assert_eq!(mapping.get(&"id".to_string()), Some(&"users".to_string()));
1026 assert_eq!(mapping.get(&"name".to_string()), Some(&"users".to_string()));
1027 assert_eq!(mapping.get(&"email".to_string()), Some(&"users".to_string()));
1028
1029 assert_eq!(mapping.get(&"unknown".to_string()), None);
1031
1032 assert_eq!(mapping.len(), 3);
1034 assert!(!mapping.is_empty());
1035
1036 assert!(mapping.contains_key(&"id".to_string()));
1038 assert!(!mapping.contains_key(&"unknown".to_string()));
1039
1040 assert_eq!(mapping.value(), &"users".to_string());
1042 }
1043
1044 #[test]
1045 fn test_single_valued_mapping_from_iter() {
1046 let mapping = SingleValuedMapping::from_iter(
1047 vec!["a".to_string(), "b".to_string()],
1048 42,
1049 );
1050
1051 assert_eq!(mapping.get(&"a".to_string()), Some(&42));
1052 assert_eq!(mapping.get(&"b".to_string()), Some(&42));
1053 assert_eq!(mapping.len(), 2);
1054 }
1055
1056 #[test]
1057 fn test_to_bool() {
1058 assert_eq!(to_bool("true"), Some(true));
1059 assert_eq!(to_bool("TRUE"), Some(true));
1060 assert_eq!(to_bool("True"), Some(true));
1061 assert_eq!(to_bool("1"), Some(true));
1062
1063 assert_eq!(to_bool("false"), Some(false));
1064 assert_eq!(to_bool("FALSE"), Some(false));
1065 assert_eq!(to_bool("False"), Some(false));
1066 assert_eq!(to_bool("0"), Some(false));
1067
1068 assert_eq!(to_bool("maybe"), None);
1069 assert_eq!(to_bool("yes"), None);
1070 assert_eq!(to_bool("no"), None);
1071 }
1072}