spotify_cli/io/formatters/
show.rs1use serde_json::Value;
4
5use crate::io::common::{DurationFormat, format_duration_as, print_table, truncate};
6
7pub fn format_show_detail(payload: &Value) {
8 let name = payload
9 .get("name")
10 .and_then(|v| v.as_str())
11 .unwrap_or("Unknown");
12 let publisher = payload
13 .get("publisher")
14 .and_then(|v| v.as_str())
15 .unwrap_or("Unknown");
16 let description = payload
17 .get("description")
18 .and_then(|v| v.as_str())
19 .unwrap_or("");
20 let total_episodes = payload
21 .get("total_episodes")
22 .and_then(|v| v.as_u64())
23 .unwrap_or(0);
24 let explicit = payload
25 .get("explicit")
26 .and_then(|v| v.as_bool())
27 .unwrap_or(false);
28 let uri = payload.get("uri").and_then(|v| v.as_str()).unwrap_or("");
29
30 println!("{}", name);
31 println!(" Publisher: {}", publisher);
32 if !description.is_empty() {
33 let desc = if description.len() > 200 {
34 format!("{}...", &description[..200])
35 } else {
36 description.to_string()
37 };
38 println!(" Description: {}", desc);
39 }
40 println!(" Total Episodes: {}", total_episodes);
41 if explicit {
42 println!(" Explicit: Yes");
43 }
44 if !uri.is_empty() {
45 println!(" URI: {}", uri);
46 }
47}
48
49pub fn format_shows(items: &[Value], message: &str) {
50 println!("{}:", message);
51 println!();
52
53 let rows: Vec<Vec<String>> = items
54 .iter()
55 .enumerate()
56 .map(|(i, item)| {
57 let show = item.get("show").unwrap_or(item);
59 let name = show
60 .get("name")
61 .and_then(|v| v.as_str())
62 .unwrap_or("Unknown");
63 let publisher = show
64 .get("publisher")
65 .and_then(|v| v.as_str())
66 .unwrap_or("Unknown");
67 let episodes = show
68 .get("total_episodes")
69 .and_then(|v| v.as_u64())
70 .unwrap_or(0);
71 vec![
72 (i + 1).to_string(),
73 truncate(name, 30),
74 truncate(publisher, 20),
75 episodes.to_string(),
76 ]
77 })
78 .collect();
79
80 print_table(
81 "Shows",
82 &["#", "Name", "Publisher", "Episodes"],
83 &rows,
84 &[3, 30, 20, 8],
85 );
86}
87
88pub fn format_show_episodes(items: &[Value], message: &str) {
89 println!("{}:", message);
90 println!();
91
92 let rows: Vec<Vec<String>> = items
93 .iter()
94 .enumerate()
95 .map(|(i, item)| {
96 let name = item
97 .get("name")
98 .and_then(|v| v.as_str())
99 .unwrap_or("Unknown");
100 let duration_ms = item
101 .get("duration_ms")
102 .and_then(|v| v.as_u64())
103 .unwrap_or(0);
104 let duration = format_duration_as(duration_ms, DurationFormat::Long);
105 let release = item
106 .get("release_date")
107 .and_then(|v| v.as_str())
108 .unwrap_or("");
109 vec![
110 (i + 1).to_string(),
111 truncate(name, 35),
112 duration,
113 release.to_string(),
114 ]
115 })
116 .collect();
117
118 print_table(
119 "Episodes",
120 &["#", "Name", "Duration", "Released"],
121 &rows,
122 &[3, 35, 10, 12],
123 );
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129 use serde_json::json;
130
131 #[test]
132 fn format_show_detail_full() {
133 let payload = json!({
134 "name": "Test Podcast",
135 "publisher": "Test Publisher",
136 "description": "A great podcast about testing",
137 "total_episodes": 100,
138 "explicit": true,
139 "uri": "spotify:show:abc123"
140 });
141 format_show_detail(&payload);
142 }
143
144 #[test]
145 fn format_show_detail_minimal() {
146 let payload = json!({});
147 format_show_detail(&payload);
148 }
149
150 #[test]
151 fn format_show_detail_long_description() {
152 let long_desc = "A".repeat(300);
153 let payload = json!({
154 "name": "Podcast",
155 "publisher": "Publisher",
156 "description": long_desc,
157 "total_episodes": 50
158 });
159 format_show_detail(&payload);
160 }
161
162 #[test]
163 fn format_show_detail_not_explicit() {
164 let payload = json!({
165 "name": "Family Podcast",
166 "publisher": "Family",
167 "explicit": false
168 });
169 format_show_detail(&payload);
170 }
171
172 #[test]
173 fn format_shows_with_items() {
174 let items = vec![
175 json!({
176 "name": "Podcast One",
177 "publisher": "Publisher A",
178 "total_episodes": 150
179 }),
180 json!({
181 "name": "Podcast Two",
182 "publisher": "Publisher B",
183 "total_episodes": 75
184 }),
185 ];
186 format_shows(&items, "Your Podcasts");
187 }
188
189 #[test]
190 fn format_shows_empty() {
191 let items: Vec<Value> = vec![];
192 format_shows(&items, "No Podcasts");
193 }
194
195 #[test]
196 fn format_shows_wrapped() {
197 let items = vec![json!({
198 "show": {
199 "name": "Wrapped Podcast",
200 "publisher": "Publisher",
201 "total_episodes": 50
202 }
203 })];
204 format_shows(&items, "Saved Shows");
205 }
206
207 #[test]
208 fn format_show_episodes_with_items() {
209 let items = vec![
210 json!({
211 "name": "Episode One",
212 "duration_ms": 3600000,
213 "release_date": "2024-01-15"
214 }),
215 json!({
216 "name": "Episode Two",
217 "duration_ms": 1800000,
218 "release_date": "2024-01-08"
219 }),
220 ];
221 format_show_episodes(&items, "Recent Episodes");
222 }
223
224 #[test]
225 fn format_show_episodes_empty() {
226 let items: Vec<Value> = vec![];
227 format_show_episodes(&items, "No Episodes");
228 }
229
230 #[test]
231 fn format_show_episodes_minimal() {
232 let items = vec![json!({})];
233 format_show_episodes(&items, "Episodes");
234 }
235}