1#![allow(clippy::print_stdout)]
8
9use serde::Serialize;
10use serde_json::Value;
11
12#[allow(clippy::expect_used)]
32pub fn print_json<T: Serialize>(value: &T, fields: Option<&[String]>) {
33 let json_value = serde_json::to_value(value).expect("value must be serializable to JSON");
34
35 let output = match fields {
36 Some(field_list) => {
37 if field_list.is_empty() {
38 json_value
39 } else {
40 let mut filtered = serde_json::Map::new();
41 if let Value::Object(map) = &json_value {
42 for field in field_list {
43 if let Some(val) = map.get(field) {
44 filtered.insert(field.clone(), val.clone());
45 }
46 }
47 }
48 Value::Object(filtered)
49 }
50 }
51 None => json_value,
52 };
53
54 let pretty = serde_json::to_string_pretty(&output).expect("value must be pretty-printable");
55 println!("{pretty}");
56}
57
58#[must_use]
74pub fn format_date(iso_date: &str) -> String {
75 let date_part = iso_date.get(..10).unwrap_or(iso_date);
77
78 let parts: Vec<&str> = date_part.split('-').collect();
80 if parts.len() != 3 {
81 return iso_date.to_string();
82 }
83
84 let year = parts[0];
85 let month: u32 = parts[1].parse().unwrap_or(0);
86 let day = parts[2];
87
88 let month_name = match month {
89 1 => "Jan",
90 2 => "Feb",
91 3 => "Mar",
92 4 => "Apr",
93 5 => "May",
94 6 => "Jun",
95 7 => "Jul",
96 8 => "Aug",
97 9 => "Sep",
98 10 => "Oct",
99 11 => "Nov",
100 12 => "Dec",
101 _ => return iso_date.to_string(),
102 };
103
104 let day_stripped = day.trim_start_matches('0');
106
107 format!("{month_name} {day_stripped}, {year}")
108}
109
110#[must_use]
123pub fn format_count(n: u64) -> String {
124 let s = n.to_string();
125 let mut result = String::with_capacity(s.len() + s.len() / 3);
126
127 for (i, c) in s.chars().enumerate() {
128 if i > 0 && (s.len() - i) % 3 == 0 {
130 result.push(',');
131 }
132 result.push(c);
133 }
134
135 result
136}
137
138#[cfg(test)]
139#[allow(clippy::expect_used)]
140mod tests {
141 use super::*;
142 use serde_json::json;
143
144 #[test]
145 fn print_json_full_output() {
146 let data = json!({"name": "hello-world", "stars": 42, "forks": 7});
147 print_json(&data, None::<&[String]>);
149 }
150
151 #[test]
152 fn print_json_with_fields() {
153 let data = json!({"name": "hello-world", "stars": 42, "forks": 7});
154 let fields = vec!["name".to_string(), "stars".to_string()];
155 print_json(&data, Some(&fields));
157 }
158
159 #[test]
160 fn print_json_with_empty_fields() {
161 let data = json!({"name": "hello-world", "stars": 42});
162 let fields: Vec<String> = vec![];
163 print_json(&data, Some(&fields));
165 }
166
167 #[test]
168 fn print_json_with_missing_field() {
169 let data = json!({"name": "hello-world"});
170 let fields = vec!["name".to_string(), "missing".to_string()];
171 print_json(&data, Some(&fields));
173 }
174
175 #[test]
176 fn format_date_standard() {
177 assert_eq!(format_date("2024-01-15T10:30:00Z"), "Jan 15, 2024");
178 }
179
180 #[test]
181 fn format_date_december() {
182 assert_eq!(format_date("2023-12-25T00:00:00Z"), "Dec 25, 2023");
183 }
184
185 #[test]
186 fn format_date_single_digit_day() {
187 assert_eq!(format_date("2024-03-05T12:00:00Z"), "Mar 5, 2024");
188 }
189
190 #[test]
191 fn format_date_invalid() {
192 assert_eq!(format_date("not-a-date"), "not-a-date");
193 }
194
195 #[test]
196 fn format_date_empty() {
197 assert_eq!(format_date(""), "");
198 }
199
200 #[test]
201 fn format_date_bad_month() {
202 assert_eq!(format_date("2024-13-01"), "2024-13-01");
203 }
204
205 #[test]
206 fn format_count_zero() {
207 assert_eq!(format_count(0), "0");
208 }
209
210 #[test]
211 fn format_count_small() {
212 assert_eq!(format_count(42), "42");
213 }
214
215 #[test]
216 fn format_count_thousands() {
217 assert_eq!(format_count(1_234), "1,234");
218 }
219
220 #[test]
221 fn format_count_millions() {
222 assert_eq!(format_count(1_000_000), "1,000,000");
223 }
224
225 #[test]
226 fn format_count_billions() {
227 assert_eq!(format_count(1_234_567_890), "1,234,567,890");
228 }
229
230 #[test]
231 fn format_count_ten_thousands() {
232 assert_eq!(format_count(10_000), "10,000");
233 }
234
235 #[test]
236 fn format_count_hundred_thousands() {
237 assert_eq!(format_count(100_000), "100,000");
238 }
239}