use crate::prelude::*;
use urlencoding::encode;
#[derive(Clone, Debug, Default)]
pub struct BrowseRequest {
pub format: Option<Format>,
pub encoding: Option<Quality>,
pub category: Option<Category>,
pub order_by: Option<OrderBy>,
pub order_way: Option<OrderWay>,
pub page: Option<u32>,
pub group_results: Option<bool>,
pub search: Option<String>,
pub artist: Option<String>,
pub album: Option<String>,
pub tags: Option<Vec<String>>,
pub tags_operator: Option<TagsOperator>,
pub year: Option<u32>,
pub edition_title: Option<String>,
pub edition_year: Option<u32>,
pub media: Option<Media>,
pub release_type: Option<ReleaseType>,
}
impl BrowseRequest {
#[must_use]
pub fn to_query(&self) -> String {
let mut parts: Vec<(&str, String)> = vec![("action", "browse".to_owned())];
if let Some(format) = &self.format {
parts.push(("format", format.to_string()));
}
if let Some(encoding) = &self.encoding {
parts.push(("encoding", encoding.to_string()));
}
if let Some(category) = &self.category {
parts.push(("filter_cat", category.to_group().to_string()));
}
if let Some(order_by) = &self.order_by {
parts.push(("order_by", order_by.as_query().to_owned()));
}
if let Some(order_way) = &self.order_way {
parts.push(("order_way", order_way.as_query().to_owned()));
}
if let Some(page) = self.page {
parts.push(("page", page.to_string()));
}
if let Some(group_results) = self.group_results {
parts.push((
"group_results",
if group_results { "1" } else { "0" }.to_owned(),
));
}
if let Some(search) = &self.search {
parts.push(("searchstr", search.clone()));
}
if let Some(artist) = &self.artist {
parts.push(("artistname", artist.clone()));
}
if let Some(album) = &self.album {
parts.push(("groupname", album.clone()));
}
if let Some(tags) = &self.tags {
parts.push(("taglist", tags.join(",")));
}
if let Some(tags_operator) = &self.tags_operator {
parts.push(("tags_type", tags_operator.as_query().to_owned()));
}
if let Some(year) = self.year {
parts.push(("year", year.to_string()));
}
if let Some(edition_title) = &self.edition_title {
parts.push(("remastertitle", edition_title.clone()));
}
if let Some(edition_year) = self.edition_year {
parts.push(("remasteryear", edition_year.to_string()));
}
if let Some(media) = &self.media {
parts.push(("media", media.to_string()));
}
if let Some(release_type) = &self.release_type {
parts.push(("releasetype", release_type.to_int().to_string()));
}
parts
.iter()
.map(|(k, v)| format!("{k}={}", encode(v)))
.collect::<Vec<_>>()
.join("&")
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum TagsOperator {
Or,
And,
}
impl TagsOperator {
#[must_use]
pub fn as_query(&self) -> &'static str {
match self {
Self::Or => "0",
Self::And => "1",
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn browse_request_to_query_empty() {
let request = BrowseRequest::default();
let output = request.to_query();
assert_eq!(output, "action=browse");
}
#[test]
fn browse_request_to_query_full() {
let request = BrowseRequest {
format: Some(Format::FLAC),
encoding: Some(Quality::Lossless24),
category: Some(Category::Music),
order_by: Some(OrderBy::Time),
order_way: Some(OrderWay::Desc),
page: Some(1),
group_results: Some(true),
..BrowseRequest::default()
};
let output = request.to_query();
assert_eq!(
output,
"action=browse&format=FLAC&encoding=24bit%20Lossless&filter_cat=1&order_by=time&order_way=desc&page=1&group_results=1"
);
}
#[test]
fn browse_request_to_query_search_filters() {
let request = BrowseRequest {
search: Some("test album".to_owned()),
artist: Some("test artist".to_owned()),
album: Some("test group".to_owned()),
tags: Some(vec!["jazz".to_owned(), "piano".to_owned()]),
tags_operator: Some(TagsOperator::And),
year: Some(2024),
edition_title: Some("Deluxe".to_owned()),
edition_year: Some(2025),
media: Some(Media::WEB),
release_type: Some(ReleaseType::Album),
..BrowseRequest::default()
};
let output = request.to_query();
assert_eq!(
output,
"action=browse&searchstr=test%20album&artistname=test%20artist&groupname=test%20group&taglist=jazz%2Cpiano&tags_type=1&year=2024&remastertitle=Deluxe&remasteryear=2025&media=WEB&releasetype=1"
);
}
#[test]
fn browse_request_to_query_partial_filters() {
let request = BrowseRequest {
artist: Some("Logistics".to_owned()),
category: Some(Category::Music),
order_by: Some(OrderBy::Time),
order_way: Some(OrderWay::Desc),
page: Some(1),
group_results: Some(true),
..BrowseRequest::default()
};
let output = request.to_query();
assert_eq!(
output,
"action=browse&filter_cat=1&order_by=time&order_way=desc&page=1&group_results=1&artistname=Logistics"
);
}
}