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::player::PlayerStatus;
8use crate::domain::playlist::{Playlist, PlaylistDetail};
9use crate::domain::pin::PinnedPlaylist;
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 { OutputMode::Json } else { OutputMode::Human };
53 let table = TableConfig {
54 max_width,
55 truncate: !no_trunc,
56 };
57 Self {
58 mode,
59 user_name,
60 table,
61 }
62 }
63
64 pub fn auth_status(&self, status: AuthStatus) -> Result<()> {
65 match self.mode {
66 OutputMode::Human => human::auth_status(status),
67 OutputMode::Json => json::auth_status(status),
68 }
69 }
70
71 pub fn auth_scopes(&self, scopes: AuthScopes) -> Result<()> {
72 match self.mode {
73 OutputMode::Human => human::auth_scopes(scopes),
74 OutputMode::Json => json::auth_scopes(scopes),
75 }
76 }
77
78 pub fn player_status(&self, status: PlayerStatus) -> Result<()> {
79 match self.mode {
80 OutputMode::Human => human::player_status(status),
81 OutputMode::Json => json::player_status(status),
82 }
83 }
84
85 pub fn now_playing(&self, status: PlayerStatus) -> Result<()> {
86 match self.mode {
87 OutputMode::Human => human::now_playing(status),
88 OutputMode::Json => json::now_playing(status),
89 }
90 }
91
92 pub fn search_results(&self, results: SearchResults) -> Result<()> {
93 match self.mode {
94 OutputMode::Human => human::search_results(results, self.table),
95 OutputMode::Json => json::search_results(results),
96 }
97 }
98
99 pub fn queue(&self, now_playing_id: Option<&str>, items: Vec<Track>) -> Result<()> {
100 match self.mode {
101 OutputMode::Human => human::queue(items, now_playing_id, self.table),
102 OutputMode::Json => {
103 let items = items
104 .into_iter()
105 .map(|track| {
106 let id = track.id;
107 crate::domain::search::SearchItem {
108 id: id.clone(),
109 name: track.name,
110 uri: format!("spotify:track:{}", id),
111 kind: crate::domain::search::SearchType::Track,
112 artists: track.artists,
113 album: track.album,
114 duration_ms: track.duration_ms,
115 owner: None,
116 score: None,
117 }
118 })
119 .collect();
120 json::queue(now_playing_id, items)
121 }
122 }
123 }
124
125 pub fn recently_played(&self, now_playing_id: Option<&str>, items: Vec<SearchItem>) -> Result<()> {
126 match self.mode {
127 OutputMode::Human => human::recently_played(items, now_playing_id, self.table),
128 OutputMode::Json => json::recently_played(now_playing_id, items),
129 }
130 }
131
132 pub fn cache_status(&self, status: CacheStatus) -> Result<()> {
133 match self.mode {
134 OutputMode::Human => cache::status_human(status),
135 OutputMode::Json => cache::status_json(status),
136 }
137 }
138
139 pub fn action(&self, event: &str, message: &str) -> Result<()> {
140 match self.mode {
141 OutputMode::Human => human::action(message),
142 OutputMode::Json => json::action(event, message),
143 }
144 }
145
146 pub fn album_info(&self, album: Album) -> Result<()> {
147 match self.mode {
148 OutputMode::Human => human::album_info(album, self.table),
149 OutputMode::Json => json::album_info(album),
150 }
151 }
152
153 pub fn artist_info(&self, artist: Artist) -> Result<()> {
154 match self.mode {
155 OutputMode::Human => human::artist_info(artist),
156 OutputMode::Json => json::artist_info(artist),
157 }
158 }
159
160 pub fn playlist_list(&self, playlists: Vec<Playlist>) -> Result<()> {
161 match self.mode {
162 OutputMode::Human => {
163 human::playlist_list(playlists, self.user_name.as_deref(), self.table)
164 }
165 OutputMode::Json => json::playlist_list(playlists),
166 }
167 }
168
169 pub fn playlist_list_with_pins(
170 &self,
171 playlists: Vec<Playlist>,
172 pins: Vec<PinnedPlaylist>,
173 ) -> Result<()> {
174 match self.mode {
175 OutputMode::Human => {
176 human::playlist_list_with_pins(
177 playlists,
178 pins,
179 self.user_name.as_deref(),
180 self.table,
181 )
182 }
183 OutputMode::Json => json::playlist_list_with_pins(playlists, pins),
184 }
185 }
186
187 pub fn playlist_info(&self, playlist: PlaylistDetail) -> Result<()> {
188 match self.mode {
189 OutputMode::Human => human::playlist_info(playlist, self.user_name.as_deref()),
190 OutputMode::Json => json::playlist_info(playlist),
191 }
192 }
193
194 pub fn device_list(&self, devices: Vec<Device>) -> Result<()> {
195 match self.mode {
196 OutputMode::Human => human::device_list(devices, self.table),
197 OutputMode::Json => json::device_list(devices),
198 }
199 }
200
201 pub fn settings(&self, settings: Settings) -> Result<()> {
202 match self.mode {
203 OutputMode::Human => settings::settings_human(settings),
204 OutputMode::Json => settings::settings_json(settings),
205 }
206 }
207
208 pub fn pin_list(&self, pins: Vec<PinnedPlaylist>) -> Result<()> {
209 match self.mode {
210 OutputMode::Human => pin::pin_list_human(pins, self.table),
211 OutputMode::Json => pin::pin_list_json(pins),
212 }
213 }
214
215 pub fn help(&self) -> Result<()> {
216 match self.mode {
217 OutputMode::Human => human::help(),
218 OutputMode::Json => json::help(),
219 }
220 }
221}