crate_activity/
align_and_normalize.rs1crate::ix!();
2
3pub fn align_and_normalize_data(
4 version_downloads: &[VersionDownload],
5 full_date_range: &[NaiveDate],
6
7) -> Vec<i64> {
8
9 let downloads_map: HashMap<NaiveDate, i64> = version_downloads
10 .iter()
11 .map(|d| (*d.date(), *d.downloads())) .collect();
13
14 full_date_range
15 .iter()
16 .map(|date| *downloads_map.get(date).unwrap_or(&0)) .collect()
18}
19
20pub fn debug_alignment(
21 crate_a: &str,
22 crate_b: &str,
23 aligned_a: &[i64],
24 aligned_b: &[i64],
25) {
26 println!("Aligned data for {} and {}:", crate_a, crate_b);
27 println!(" Aligned A: {:?}", aligned_a);
28 println!(" Aligned B: {:?}", aligned_b);
29}
30
31#[cfg(test)]
32mod alignment_and_normalization_tests {
33 use super::*;
34 use chrono::NaiveDate;
35
36 #[test]
37 fn test_empty_input() {
38 let version_downloads = [];
39 let full_date_range = [];
40 let result = align_and_normalize_data(&version_downloads, &full_date_range);
41 assert_eq!(result, Vec::<i64>::new(), "Empty input should produce empty output.");
42 }
43
44 #[test]
45 fn test_full_date_range_matches_data() {
46 let version_downloads = [
47 VersionDownloadBuilder::default()
48 .version(1_i64)
49 .downloads(100_i64)
50 .date(NaiveDate::from_ymd_opt(2024, 12, 1).unwrap())
51 .build()
52 .unwrap(),
53 VersionDownloadBuilder::default()
54 .version(1_i64)
55 .downloads(200_i64)
56 .date(NaiveDate::from_ymd_opt(2024, 12, 2).unwrap())
57 .build()
58 .unwrap(),
59 ];
60 let full_date_range = vec![
61 NaiveDate::from_ymd_opt(2024, 12, 1).unwrap(),
62 NaiveDate::from_ymd_opt(2024, 12, 2).unwrap(),
63 ];
64 let result = align_and_normalize_data(&version_downloads, &full_date_range);
65 assert_eq!(result, vec![100, 200], "All dates should align correctly.");
66 }
67
68 #[test]
69 fn test_full_date_range_extends_beyond_data() {
70 let version_downloads = [
71 VersionDownloadBuilder::default()
72 .version(1_i64)
73 .downloads(200_i64)
74 .date(NaiveDate::from_ymd_opt(2024, 12, 2).unwrap())
75 .build()
76 .unwrap(),
77 ];
78 let full_date_range = vec![
79 NaiveDate::from_ymd_opt(2024, 12, 1).unwrap(),
80 NaiveDate::from_ymd_opt(2024, 12, 2).unwrap(),
81 NaiveDate::from_ymd_opt(2024, 12, 3).unwrap(),
82 ];
83 let result = align_and_normalize_data(&version_downloads, &full_date_range);
84 assert_eq!(
85 result,
86 vec![0, 200, 0],
87 "Missing dates should be filled with 0."
88 );
89 }
90
91 #[test]
92 fn test_full_date_range_subset_of_data() {
93 let version_downloads = [
94 VersionDownloadBuilder::default()
95 .version(1_i64)
96 .downloads(100_i64)
97 .date(NaiveDate::from_ymd_opt(2024, 12, 1).unwrap())
98 .build()
99 .unwrap(),
100 VersionDownloadBuilder::default()
101 .version(1_i64)
102 .downloads(200_i64)
103 .date(NaiveDate::from_ymd_opt(2024, 12, 2).unwrap())
104 .build()
105 .unwrap(),
106 VersionDownloadBuilder::default()
107 .version(1_i64)
108 .downloads(300_i64)
109 .date(NaiveDate::from_ymd_opt(2024, 12, 3).unwrap())
110 .build()
111 .unwrap(),
112 ];
113 let full_date_range = vec![
114 NaiveDate::from_ymd_opt(2024, 12, 2).unwrap(),
115 NaiveDate::from_ymd_opt(2024, 12, 3).unwrap(),
116 ];
117 let result = align_and_normalize_data(&version_downloads, &full_date_range);
118 assert_eq!(
119 result,
120 vec![200, 300],
121 "Only values within the full_date_range should be included."
122 );
123 }
124
125 #[test]
126 fn test_duplicate_dates_in_input() {
127 let version_downloads = [
128 VersionDownloadBuilder::default()
129 .version(1_i64)
130 .downloads(100_i64)
131 .date(NaiveDate::from_ymd_opt(2024, 12, 2).unwrap())
132 .build()
133 .unwrap(),
134 VersionDownloadBuilder::default()
135 .version(1_i64)
136 .downloads(200_i64)
137 .date(NaiveDate::from_ymd_opt(2024, 12, 2).unwrap())
138 .build()
139 .unwrap(), ];
141 let full_date_range = vec![
142 NaiveDate::from_ymd_opt(2024, 12, 2).unwrap(),
143 ];
144 let result = align_and_normalize_data(&version_downloads, &full_date_range);
145 assert_eq!(
146 result,
147 vec![200],
148 "The most recent value for a duplicate date should be used."
149 );
150 }
151}