spotify_cli/io/formatters/
library.rs

1//! Library formatting functions
2
3use serde_json::Value;
4
5use crate::io::common::extract_artist_names;
6
7pub fn format_saved_tracks(items: &[Value]) {
8    println!("Saved Tracks:");
9    for (i, item) in items.iter().take(20).enumerate() {
10        if let Some(track) = item.get("track") {
11            let name = track
12                .get("name")
13                .and_then(|v| v.as_str())
14                .unwrap_or("Unknown");
15            let artists = extract_artist_names(track);
16            println!("  {}. {} - {}", i + 1, name, artists);
17        }
18    }
19}
20
21pub fn format_library_check(results: &[Value]) {
22    println!("Library Check:");
23    for (i, result) in results.iter().enumerate() {
24        let saved = result.as_bool().unwrap_or(false);
25        let status = if saved { "Saved" } else { "Not saved" };
26        println!("  Track {}: {}", i + 1, status);
27    }
28}
29
30pub fn format_markets(markets: &[Value]) {
31    if markets.is_empty() {
32        println!("No markets available.");
33        return;
34    }
35
36    println!("Available Markets ({} countries):", markets.len());
37    let market_codes: Vec<&str> = markets.iter().filter_map(|v| v.as_str()).collect();
38
39    // Print in columns
40    for chunk in market_codes.chunks(10) {
41        println!("  {}", chunk.join(", "));
42    }
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48    use serde_json::json;
49
50    #[test]
51    fn format_saved_tracks_with_items() {
52        let items = vec![
53            json!({ "track": { "name": "Track 1", "artists": [{ "name": "Artist 1" }] } }),
54            json!({ "track": { "name": "Track 2", "artists": [{ "name": "Artist 2" }] } }),
55        ];
56        format_saved_tracks(&items);
57    }
58
59    #[test]
60    fn format_saved_tracks_empty() {
61        let items: Vec<Value> = vec![];
62        format_saved_tracks(&items);
63    }
64
65    #[test]
66    fn format_saved_tracks_more_than_twenty() {
67        let items: Vec<Value> = (0..25)
68            .map(|i| json!({ "track": { "name": format!("Track {}", i), "artists": [{ "name": "Artist" }] } }))
69            .collect();
70        format_saved_tracks(&items);
71    }
72
73    #[test]
74    fn format_saved_tracks_no_track_field() {
75        let items = vec![json!({ "name": "Direct Item" })];
76        format_saved_tracks(&items);
77    }
78
79    #[test]
80    fn format_library_check_mixed() {
81        let results = vec![json!(true), json!(false), json!(true)];
82        format_library_check(&results);
83    }
84
85    #[test]
86    fn format_library_check_empty() {
87        let results: Vec<Value> = vec![];
88        format_library_check(&results);
89    }
90
91    #[test]
92    fn format_library_check_all_saved() {
93        let results = vec![json!(true), json!(true)];
94        format_library_check(&results);
95    }
96
97    #[test]
98    fn format_library_check_none_saved() {
99        let results = vec![json!(false), json!(false)];
100        format_library_check(&results);
101    }
102
103    #[test]
104    fn format_library_check_non_bool() {
105        let results = vec![json!("invalid"), json!(null)];
106        format_library_check(&results);
107    }
108}