pub mod config;
use std::borrow::Cow;
use std::collections::HashMap;
use config::ConfigManager;
use derive_builder::Builder;
use reqwest::Proxy;
use serde::{Deserialize, Serialize};
use typed_builder::TypedBuilder;
pub type SerializedIdentifier = String;
#[derive(Clone, Copy, Serialize, Deserialize, Default)]
#[non_exhaustive]
pub enum SearchMode {
#[serde(alias = "fast_first")]
#[default]
FastFirst,
#[serde(alias = "order_first")]
OrderFirst,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TypedBuilder)]
#[non_exhaustive]
pub struct Artist {
#[builder(default = "".to_string())]
pub id: String,
pub name: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TypedBuilder)]
#[non_exhaustive]
pub struct Album {
#[builder(default = "".to_string())]
pub id: String,
pub name: String,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TypedBuilder)]
#[non_exhaustive]
pub struct Song {
#[builder(default = "".to_string())]
pub id: String,
pub name: String,
#[builder(default)]
pub duration: Option<i64>,
#[builder(default)]
pub artists: Vec<Artist>,
#[builder(default)]
pub album: Option<Album>,
#[builder(default)]
pub context: Option<HashMap<String, String>>,
}
#[derive(Clone, Serialize, Deserialize, TypedBuilder)]
#[non_exhaustive]
pub struct SongSearchInformation {
pub source: Cow<'static, str>,
pub identifier: SerializedIdentifier,
#[builder(default)]
pub song: Option<Song>,
#[builder(default)]
pub pre_retrieve_result: Option<RetrievedSongInfo>,
}
#[derive(Clone, Serialize, Deserialize, TypedBuilder)]
#[non_exhaustive]
pub struct RetrievedSongInfo {
pub source: Cow<'static, str>,
pub url: String,
}
#[derive(Clone, Default, Serialize, Deserialize, Builder)]
#[builder(setter(into), default)]
#[non_exhaustive]
pub struct Context {
pub proxy_uri: Option<Cow<'static, str>>,
#[serde(default)]
pub enable_flac: bool,
#[serde(default)]
pub search_mode: SearchMode,
pub config: Option<ConfigManager>,
}
impl Context {
#[deprecated = "use unm_request::build_client instead"]
pub fn try_get_proxy(&self) -> reqwest::Result<Option<Proxy>> {
self.proxy_uri
.as_ref()
.map(|uri| Proxy::all(uri.to_string()))
.transpose()
}
}
impl std::fmt::Display for Song {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.display_name())
}
}
impl Song {
fn get_name(&self, has_separator: bool) -> String {
let mut keyword = self.name.to_string();
if self.artists.is_empty() {
return keyword;
}
let max_idx = self.artists.len() - 1;
if has_separator {
keyword.push_str(" - ");
} else {
keyword.push(' ');
}
for (idx, artist) in self.artists.iter().enumerate() {
keyword.push_str(&artist.name);
if idx != max_idx {
if has_separator {
keyword.push_str(", ");
} else {
keyword.push(' ');
}
}
}
keyword
}
pub fn display_name(&self) -> String {
self.get_name(true)
}
pub fn keyword(&self) -> String {
self.get_name(false)
}
}
#[cfg(test)]
mod tests {
use crate::{Artist, Song};
#[test]
fn test_name_with_no_artist() {
let s = Song {
id: "114514".to_string(),
name: "Lost River".to_string(),
artists: vec![],
..Default::default()
};
assert_eq!(s.display_name(), "Lost River");
assert_eq!(s.keyword(), "Lost River");
assert_eq!(format!("{s}"), "Lost River");
}
#[test]
fn test_name_with_single_artist() {
let s = Song {
id: "123".to_string(),
name: "TT".to_string(),
artists: vec![Artist {
id: "114".to_string(),
name: "Twice".to_string(),
}],
..Default::default()
};
assert_eq!(s.display_name(), "TT - Twice");
assert_eq!(s.keyword(), "TT Twice");
assert_eq!(format!("{s}"), "TT - Twice");
}
#[test]
fn test_display_name_with_multiple_artist() {
let s = Song {
id: "123".to_string(),
name: "Hope for Tomorrow - Melchi Remix".to_string(),
artists: vec![
Artist {
id: "1".to_string(),
name: "Alex H".to_string(),
},
Artist {
id: "2".to_string(),
name: "Z8phyR".to_string(),
},
Artist {
id: "3".to_string(),
name: "Melchi".to_string(),
},
],
..Default::default()
};
assert_eq!(
s.display_name(),
"Hope for Tomorrow - Melchi Remix - Alex H, Z8phyR, Melchi"
);
assert_eq!(
s.keyword(),
"Hope for Tomorrow - Melchi Remix Alex H Z8phyR Melchi"
);
assert_eq!(
format!("{s}"),
"Hope for Tomorrow - Melchi Remix - Alex H, Z8phyR, Melchi"
);
}
}