1use crate::domain::album::Album;
3use crate::domain::artist::Artist;
4use crate::domain::auth::{AuthScopes, AuthStatus};
5use crate::domain::cache::CacheStatus;
6use crate::domain::device::Device;
7use crate::domain::pin::PinnedPlaylist;
8use crate::domain::player::PlayerStatus;
9use crate::domain::playlist::{Playlist, PlaylistDetail};
10use crate::domain::search::{SearchItem, SearchResults};
11use crate::domain::settings::Settings;
12use crate::domain::track::Track;
13use crate::error::Result;
14
15pub mod cache;
16pub mod human;
17pub mod json;
18pub mod pin;
19pub mod settings;
20
21#[derive(Debug, Clone, Copy)]
23pub enum OutputMode {
24 Human,
25 Json,
26}
27
28pub const DEFAULT_MAX_WIDTH: usize = 48;
29
30#[derive(Debug, Clone, Copy)]
32pub struct TableConfig {
33 pub max_width: Option<usize>,
34 pub truncate: bool,
35}
36
37#[derive(Debug, Clone)]
39pub struct Output {
40 mode: OutputMode,
41 user_name: Option<String>,
42 table: TableConfig,
43}
44
45impl Output {
46 pub fn new(
47 json: bool,
48 user_name: Option<String>,
49 max_width: Option<usize>,
50 no_trunc: bool,
51 ) -> Self {
52 let mode = if json {
53 OutputMode::Json
54 } else {
55 OutputMode::Human
56 };
57 let table = TableConfig {
58 max_width,
59 truncate: !no_trunc,
60 };
61 Self {
62 mode,
63 user_name,
64 table,
65 }
66 }
67
68 pub fn auth_status(&self, status: AuthStatus) -> Result<()> {
69 match self.mode {
70 OutputMode::Human => human::auth_status(status),
71 OutputMode::Json => json::auth_status(status),
72 }
73 }
74
75 pub fn auth_scopes(&self, scopes: AuthScopes) -> Result<()> {
76 match self.mode {
77 OutputMode::Human => human::auth_scopes(scopes),
78 OutputMode::Json => json::auth_scopes(scopes),
79 }
80 }
81
82 pub fn player_status(&self, status: PlayerStatus) -> Result<()> {
83 match self.mode {
84 OutputMode::Human => human::player_status(status),
85 OutputMode::Json => json::player_status(status),
86 }
87 }
88
89 pub fn now_playing(&self, status: PlayerStatus) -> Result<()> {
90 match self.mode {
91 OutputMode::Human => human::now_playing(status),
92 OutputMode::Json => json::now_playing(status),
93 }
94 }
95
96 pub fn search_results(&self, results: SearchResults) -> Result<()> {
97 match self.mode {
98 OutputMode::Human => human::search_results(results, self.table),
99 OutputMode::Json => json::search_results(results),
100 }
101 }
102
103 pub fn queue(&self, now_playing_id: Option<&str>, items: Vec<Track>) -> Result<()> {
104 match self.mode {
105 OutputMode::Human => human::queue(items, now_playing_id, self.table),
106 OutputMode::Json => {
107 let items = items
108 .into_iter()
109 .map(|track| {
110 let id = track.id;
111 crate::domain::search::SearchItem {
112 id: id.clone(),
113 name: track.name,
114 uri: format!("spotify:track:{}", id),
115 kind: crate::domain::search::SearchType::Track,
116 artists: track.artists,
117 album: track.album,
118 duration_ms: track.duration_ms,
119 owner: None,
120 score: None,
121 }
122 })
123 .collect();
124 json::queue(now_playing_id, items)
125 }
126 }
127 }
128
129 pub fn recently_played(
130 &self,
131 now_playing_id: Option<&str>,
132 items: Vec<SearchItem>,
133 ) -> Result<()> {
134 match self.mode {
135 OutputMode::Human => human::recently_played(items, now_playing_id, self.table),
136 OutputMode::Json => json::recently_played(now_playing_id, items),
137 }
138 }
139
140 pub fn cache_status(&self, status: CacheStatus) -> Result<()> {
141 match self.mode {
142 OutputMode::Human => cache::status_human(status),
143 OutputMode::Json => cache::status_json(status),
144 }
145 }
146
147 pub fn action(&self, event: &str, message: &str) -> Result<()> {
148 match self.mode {
149 OutputMode::Human => human::action(message),
150 OutputMode::Json => json::action(event, message),
151 }
152 }
153
154 pub fn album_info(&self, album: Album) -> Result<()> {
155 match self.mode {
156 OutputMode::Human => human::album_info(album, self.table),
157 OutputMode::Json => json::album_info(album),
158 }
159 }
160
161 pub fn artist_info(&self, artist: Artist) -> Result<()> {
162 match self.mode {
163 OutputMode::Human => human::artist_info(artist),
164 OutputMode::Json => json::artist_info(artist),
165 }
166 }
167
168 pub fn playlist_list(&self, playlists: Vec<Playlist>) -> Result<()> {
169 match self.mode {
170 OutputMode::Human => {
171 human::playlist_list(playlists, self.user_name.as_deref(), self.table)
172 }
173 OutputMode::Json => json::playlist_list(playlists),
174 }
175 }
176
177 pub fn playlist_list_with_pins(
178 &self,
179 playlists: Vec<Playlist>,
180 pins: Vec<PinnedPlaylist>,
181 ) -> Result<()> {
182 match self.mode {
183 OutputMode::Human => human::playlist_list_with_pins(
184 playlists,
185 pins,
186 self.user_name.as_deref(),
187 self.table,
188 ),
189 OutputMode::Json => json::playlist_list_with_pins(playlists, pins),
190 }
191 }
192
193 pub fn playlist_info(&self, playlist: PlaylistDetail) -> Result<()> {
194 match self.mode {
195 OutputMode::Human => human::playlist_info(playlist, self.user_name.as_deref()),
196 OutputMode::Json => json::playlist_info(playlist),
197 }
198 }
199
200 pub fn device_list(&self, devices: Vec<Device>) -> Result<()> {
201 match self.mode {
202 OutputMode::Human => human::device_list(devices, self.table),
203 OutputMode::Json => json::device_list(devices),
204 }
205 }
206
207 pub fn settings(&self, settings: Settings) -> Result<()> {
208 match self.mode {
209 OutputMode::Human => settings::settings_human(settings),
210 OutputMode::Json => settings::settings_json(settings),
211 }
212 }
213
214 pub fn pin_list(&self, pins: Vec<PinnedPlaylist>) -> Result<()> {
215 match self.mode {
216 OutputMode::Human => pin::pin_list_human(pins, self.table),
217 OutputMode::Json => pin::pin_list_json(pins),
218 }
219 }
220
221 pub fn help(&self) -> Result<()> {
222 match self.mode {
223 OutputMode::Human => human::help(),
224 OutputMode::Json => json::help(),
225 }
226 }
227}