spotify_cli/io/formatters/
artist.rs1use 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}