spotify_cli/io/formatters/
artist.rs

1//! Artist formatting functions
2
3use serde_json::Value;
4
5use crate::io::common::{format_number, print_table, truncate};
6
7pub fn format_artist_detail(payload: &Value) {
8    let name = payload
9        .get("name")
10        .and_then(|v| v.as_str())
11        .unwrap_or("Unknown");
12    let followers = payload
13        .get("followers")
14        .and_then(|f| f.get("total"))
15        .and_then(|v| v.as_u64())
16        .unwrap_or(0);
17    let genres = payload
18        .get("genres")
19        .and_then(|v| v.as_array())
20        .map(|arr| {
21            arr.iter()
22                .filter_map(|g| g.as_str())
23                .collect::<Vec<_>>()
24                .join(", ")
25        })
26        .unwrap_or_default();
27    let popularity = payload
28        .get("popularity")
29        .and_then(|v| v.as_u64())
30        .unwrap_or(0);
31    let uri = payload.get("uri").and_then(|v| v.as_str()).unwrap_or("");
32
33    println!("{}", name);
34    println!("  Followers: {}", format_number(followers));
35    println!("  Popularity: {}%", popularity);
36    if !genres.is_empty() {
37        println!("  Genres: {}", genres);
38    }
39    if !uri.is_empty() {
40        println!("  URI: {}", uri);
41    }
42}
43
44pub fn format_top_artists(artists: &[Value], message: &str) {
45    println!("{}:", message);
46    println!();
47    print_table(
48        "Your Top Artists",
49        &["#", "Name", "Genres", "Popularity"],
50        &artists
51            .iter()
52            .enumerate()
53            .map(|(i, artist)| {
54                let name = artist
55                    .get("name")
56                    .and_then(|v| v.as_str())
57                    .unwrap_or("Unknown");
58                let genres = artist
59                    .get("genres")
60                    .and_then(|v| v.as_array())
61                    .map(|arr| {
62                        arr.iter()
63                            .take(2)
64                            .filter_map(|g| g.as_str())
65                            .collect::<Vec<_>>()
66                            .join(", ")
67                    })
68                    .unwrap_or_default();
69                let popularity = artist
70                    .get("popularity")
71                    .and_then(|v| v.as_u64())
72                    .map(|p| format!("{}%", p))
73                    .unwrap_or_else(|| "-".to_string());
74                vec![
75                    (i + 1).to_string(),
76                    truncate(name, 25),
77                    truncate(&genres, 25),
78                    popularity,
79                ]
80            })
81            .collect::<Vec<_>>(),
82        &[3, 25, 25, 10],
83    );
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use serde_json::json;
90
91    #[test]
92    fn format_artist_detail_full() {
93        let payload = json!({
94            "name": "Test Artist",
95            "followers": { "total": 5000000 },
96            "genres": ["rock", "alternative rock", "indie"],
97            "popularity": 85,
98            "uri": "spotify:artist:abc123"
99        });
100        format_artist_detail(&payload);
101    }
102
103    #[test]
104    fn format_artist_detail_minimal() {
105        let payload = json!({});
106        format_artist_detail(&payload);
107    }
108
109    #[test]
110    fn format_artist_detail_no_genres() {
111        let payload = json!({
112            "name": "New Artist",
113            "followers": { "total": 100 },
114            "popularity": 10
115        });
116        format_artist_detail(&payload);
117    }
118
119    #[test]
120    fn format_artist_detail_empty_genres() {
121        let payload = json!({
122            "name": "Artist",
123            "genres": [],
124            "followers": { "total": 500 }
125        });
126        format_artist_detail(&payload);
127    }
128
129    #[test]
130    fn format_top_artists_with_data() {
131        let artists = vec![
132            json!({
133                "name": "Artist One",
134                "genres": ["pop", "dance", "electronic"],
135                "popularity": 90
136            }),
137            json!({
138                "name": "Artist Two",
139                "genres": ["rock"],
140                "popularity": 75
141            }),
142        ];
143        format_top_artists(&artists, "Your Top Artists This Month");
144    }
145
146    #[test]
147    fn format_top_artists_empty() {
148        let artists: Vec<Value> = vec![];
149        format_top_artists(&artists, "No Top Artists");
150    }
151
152    #[test]
153    fn format_top_artists_minimal() {
154        let artists = vec![json!({})];
155        format_top_artists(&artists, "Artists");
156    }
157
158    #[test]
159    fn format_top_artists_many_genres() {
160        let artists = vec![json!({
161            "name": "Multi-Genre Artist",
162            "genres": ["genre1", "genre2", "genre3", "genre4", "genre5"],
163            "popularity": 60
164        })];
165        format_top_artists(&artists, "Artists");
166    }
167}