spotify_cli/io/formatters/
playlist.rs1use serde_json::Value;
4
5use crate::io::common::{extract_artist_names, format_number};
6
7pub fn format_playlists(items: &[Value]) {
8 println!("Your Playlists:");
9 for (i, item) in items.iter().enumerate() {
10 let name = item
11 .get("name")
12 .and_then(|v| v.as_str())
13 .unwrap_or("Unknown");
14 let tracks = item
15 .get("tracks")
16 .and_then(|t| t.get("total"))
17 .and_then(|v| v.as_u64())
18 .unwrap_or(0);
19 let owner = item
20 .get("owner")
21 .and_then(|o| o.get("display_name"))
22 .and_then(|v| v.as_str())
23 .unwrap_or("Unknown");
24
25 println!(" {}. {} ({} tracks) - {}", i + 1, name, tracks, owner);
26 }
27}
28
29pub fn format_playlist_detail(payload: &Value) {
30 let name = payload
31 .get("name")
32 .and_then(|v| v.as_str())
33 .unwrap_or("Unknown");
34 let owner = payload
35 .get("owner")
36 .and_then(|o| o.get("display_name"))
37 .and_then(|v| v.as_str())
38 .unwrap_or("Unknown");
39 let description = payload
40 .get("description")
41 .and_then(|v| v.as_str())
42 .filter(|s| !s.is_empty());
43 let total = payload
44 .get("tracks")
45 .and_then(|t| t.get("total"))
46 .and_then(|v| v.as_u64())
47 .unwrap_or(0);
48 let followers = payload
49 .get("followers")
50 .and_then(|f| f.get("total"))
51 .and_then(|v| v.as_u64())
52 .unwrap_or(0);
53
54 println!("{}", name);
55 println!(
56 " by {} | {} tracks | {} followers",
57 owner,
58 total,
59 format_number(followers)
60 );
61 if let Some(desc) = description {
62 println!(" {}", desc);
63 }
64
65 if let Some(tracks) = payload
67 .get("tracks")
68 .and_then(|t| t.get("items"))
69 .and_then(|i| i.as_array())
70 && !tracks.is_empty()
71 {
72 println!("\nTracks:");
73 for (i, item) in tracks.iter().take(10).enumerate() {
74 if let Some(track) = item.get("track") {
75 let track_name = track
76 .get("name")
77 .and_then(|v| v.as_str())
78 .unwrap_or("Unknown");
79 let artists = extract_artist_names(track);
80 println!(" {}. {} - {}", i + 1, track_name, artists);
81 }
82 }
83 if tracks.len() > 10 {
84 println!(" ... and {} more", total as usize - 10);
85 }
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92 use serde_json::json;
93
94 #[test]
95 fn format_playlists_with_items() {
96 let items = vec![
97 json!({
98 "name": "My Playlist",
99 "tracks": { "total": 50 },
100 "owner": { "display_name": "user123" }
101 }),
102 json!({
103 "name": "Another Playlist",
104 "tracks": { "total": 100 },
105 "owner": { "display_name": "user456" }
106 }),
107 ];
108 format_playlists(&items);
109 }
110
111 #[test]
112 fn format_playlists_empty() {
113 let items: Vec<Value> = vec![];
114 format_playlists(&items);
115 }
116
117 #[test]
118 fn format_playlists_minimal_data() {
119 let items = vec![json!({})];
120 format_playlists(&items);
121 }
122
123 #[test]
124 fn format_playlist_detail_full() {
125 let payload = json!({
126 "name": "Test Playlist",
127 "owner": { "display_name": "Test User" },
128 "description": "A great playlist for testing",
129 "tracks": {
130 "total": 25,
131 "items": [
132 { "track": { "name": "Track 1", "artists": [{ "name": "Artist 1" }] } },
133 { "track": { "name": "Track 2", "artists": [{ "name": "Artist 2" }] } }
134 ]
135 },
136 "followers": { "total": 5000 }
137 });
138 format_playlist_detail(&payload);
139 }
140
141 #[test]
142 fn format_playlist_detail_minimal() {
143 let payload = json!({});
144 format_playlist_detail(&payload);
145 }
146
147 #[test]
148 fn format_playlist_detail_empty_description() {
149 let payload = json!({
150 "name": "Playlist",
151 "description": "",
152 "owner": { "display_name": "User" }
153 });
154 format_playlist_detail(&payload);
155 }
156
157 #[test]
158 fn format_playlist_detail_many_tracks() {
159 let tracks: Vec<Value> = (0..15)
160 .map(|i| json!({ "track": { "name": format!("Track {}", i), "artists": [{ "name": "Artist" }] } }))
161 .collect();
162 let payload = json!({
163 "name": "Big Playlist",
164 "owner": { "display_name": "User" },
165 "tracks": { "total": 100, "items": tracks },
166 "followers": { "total": 1000000 }
167 });
168 format_playlist_detail(&payload);
169 }
170
171 #[test]
172 fn format_playlist_detail_no_tracks() {
173 let payload = json!({
174 "name": "Empty Playlist",
175 "owner": { "display_name": "User" },
176 "tracks": { "total": 0, "items": [] }
177 });
178 format_playlist_detail(&payload);
179 }
180}