use clap::ArgMatches;
use rspotify::{
model::enums::RepeatState,
model::idtypes::Id,
model::{
album::SimplifiedAlbum, artist::FullArtist, artist::SimplifiedArtist,
playlist::SimplifiedPlaylist, show::FullEpisode, show::SimplifiedShow, track::FullTrack,
},
};
use std::time::Duration;
use crate::user_config::UserConfig;
fn get_uri_or_fallback<T: Id>(
id: &Option<T>,
external_urls: &std::collections::HashMap<String, String>,
) -> String {
if let Some(id) = id {
id.uri()
} else {
external_urls
.get("spotify")
.cloned()
.unwrap_or_else(|| "N/A".to_string())
}
}
#[derive(Debug)]
pub enum Type {
Playlist,
Track,
Artist,
Album,
Show,
Device,
Liked,
}
impl Type {
pub fn play_from_matches(m: &ArgMatches) -> Self {
if m.get_flag("playlist") {
Self::Playlist
} else if m.get_flag("track") {
Self::Track
} else if m.get_flag("artist") {
Self::Artist
} else if m.get_flag("album") {
Self::Album
} else if m.get_flag("show") {
Self::Show
}
else {
unreachable!()
}
}
pub fn search_from_matches(m: &ArgMatches) -> Self {
if m.get_flag("playlists") {
Self::Playlist
} else if m.get_flag("tracks") {
Self::Track
} else if m.get_flag("artists") {
Self::Artist
} else if m.get_flag("albums") {
Self::Album
} else if m.get_flag("shows") {
Self::Show
}
else {
unreachable!()
}
}
pub fn list_from_matches(m: &ArgMatches) -> Self {
if m.get_flag("playlists") {
Self::Playlist
} else if m.get_flag("devices") {
Self::Device
} else if m.get_flag("liked") {
Self::Liked
}
else {
unreachable!()
}
}
}
pub enum Flag {
Like(bool),
Shuffle,
Repeat,
}
impl Flag {
pub fn from_matches(m: &ArgMatches) -> Vec<Self> {
let mut flags = Vec::new();
if m.get_flag("like") {
flags.push(Self::Like(true));
} else if m.get_flag("dislike") {
flags.push(Self::Like(false));
}
if m.get_flag("shuffle") {
flags.push(Self::Shuffle);
}
if m.get_flag("repeat") {
flags.push(Self::Repeat);
}
flags
}
}
pub enum JumpDirection {
Next,
Previous,
}
impl JumpDirection {
pub fn from_matches(m: &ArgMatches) -> (Self, u64) {
let next_count = m.get_count("next");
let prev_count = m.get_count("previous");
if next_count > 0 {
(Self::Next, next_count as u64)
} else if prev_count > 0 {
(Self::Previous, prev_count as u64)
} else {
unreachable!()
}
}
}
pub enum FormatType {
Album(Box<SimplifiedAlbum>),
Artist(Box<FullArtist>),
Playlist(Box<SimplifiedPlaylist>),
Track(Box<FullTrack>),
Episode(Box<FullEpisode>),
Show(Box<SimplifiedShow>),
}
#[derive(Clone)]
pub enum Format {
Album(String),
Artist(String),
Playlist(String),
Track(String),
Show(String),
Uri(String),
Device(String),
Volume(u32),
Position((u32, u32)),
Flags((RepeatState, bool, bool)),
Playing(bool),
}
pub fn join_artists(a: Vec<SimplifiedArtist>) -> String {
a.iter()
.map(|l| l.name.clone())
.collect::<Vec<String>>()
.join(", ")
}
impl Format {
pub fn from_type(t: FormatType) -> Vec<Self> {
match t {
FormatType::Album(a) => {
let joined_artists = join_artists(a.artists.clone());
let uri = get_uri_or_fallback(&a.id, &a.external_urls);
vec![
Self::Album(a.name),
Self::Artist(joined_artists),
Self::Uri(uri),
]
}
FormatType::Artist(a) => {
let uri = a.id.uri();
vec![Self::Artist(a.name), Self::Uri(uri)]
}
FormatType::Playlist(p) => {
let uri = p.id.uri();
vec![Self::Playlist(p.name), Self::Uri(uri)]
}
FormatType::Track(t) => {
let joined_artists = join_artists(t.artists.clone());
let uri = get_uri_or_fallback(&t.id, &t.external_urls);
vec![
Self::Album(t.album.name),
Self::Artist(joined_artists),
Self::Track(t.name),
Self::Uri(uri),
]
}
FormatType::Show(r) => {
let uri = r.id.uri();
vec![
Self::Artist(r.publisher),
Self::Show(r.name),
Self::Uri(uri),
]
}
FormatType::Episode(e) => {
let uri = e.id.uri();
vec![
Self::Show(e.show.name),
Self::Artist(e.show.publisher),
Self::Track(e.name),
Self::Uri(uri),
]
}
}
}
pub fn inner(&self, conf: UserConfig) -> String {
match self {
Self::Album(s) => s.clone(),
Self::Artist(s) => s.clone(),
Self::Playlist(s) => s.clone(),
Self::Track(s) => s.clone(),
Self::Show(s) => s.clone(),
Self::Uri(s) => s.clone(),
Self::Device(s) => s.clone(),
Self::Volume(s) => s.to_string(),
Self::Position((curr, duration)) => {
let current_progress_ms = *curr as u128;
let duration = Duration::from_millis(*duration as u64);
crate::ui::util::display_track_progress(current_progress_ms, duration)
}
Self::Flags((r, s, l)) => {
let like = if *l {
conf.behavior.liked_icon
} else {
String::new()
};
let shuffle = if *s {
conf.behavior.shuffle_icon
} else {
String::new()
};
let repeat = match r {
RepeatState::Off => String::new(),
RepeatState::Track => conf.behavior.repeat_track_icon,
RepeatState::Context => conf.behavior.repeat_context_icon,
};
[shuffle, repeat, like]
.iter()
.filter(|a| !a.is_empty())
.map(|s| s.to_string())
.collect::<Vec<String>>()
.join(" ")
}
Self::Playing(s) => {
if *s {
conf.behavior.playing_icon
} else {
conf.behavior.paused_icon
}
}
}
}
pub fn get_placeholder(&self) -> &str {
match self {
Self::Album(_) => "%b",
Self::Artist(_) => "%a",
Self::Playlist(_) => "%p",
Self::Track(_) => "%t",
Self::Show(_) => "%h",
Self::Uri(_) => "%u",
Self::Device(_) => "%d",
Self::Volume(_) => "%v",
Self::Position(_) => "%r",
Self::Flags(_) => "%f",
Self::Playing(_) => "%s",
}
}
}