use crate::types::{Collection, VirtualCollectionRow};
use super::Endpoint;
#[derive(Debug, serde::Deserialize)]
#[serde(untagged)]
pub enum CollectionsList {
List(Vec<Collection>),
Paged { items: Vec<Collection> },
}
impl CollectionsList {
pub fn into_vec(self) -> Vec<Collection> {
match self {
CollectionsList::List(v) => v,
CollectionsList::Paged { items } => items,
}
}
}
pub fn merge_all_collection_sources(
mut manual: Vec<Collection>,
mut smart: Vec<Collection>,
virtual_rows: Vec<VirtualCollectionRow>,
) -> Vec<Collection> {
for c in &mut manual {
c.is_smart = false;
c.is_virtual = false;
c.virtual_id = None;
}
for c in &mut smart {
c.is_smart = true;
c.is_virtual = false;
c.virtual_id = None;
}
let mut virtual_collections: Vec<Collection> =
virtual_rows.into_iter().map(Collection::from).collect();
manual.append(&mut smart);
manual.append(&mut virtual_collections);
manual
}
pub fn merge_manual_and_smart(manual: Vec<Collection>, smart: Vec<Collection>) -> Vec<Collection> {
merge_all_collection_sources(manual, smart, Vec::new())
}
#[derive(Debug, Default, Clone)]
pub struct ListCollections;
impl Endpoint for ListCollections {
type Output = CollectionsList;
fn method(&self) -> &'static str {
"GET"
}
fn path(&self) -> String {
"/api/collections".into()
}
}
#[derive(Debug, Default, Clone)]
pub struct ListSmartCollections;
impl Endpoint for ListSmartCollections {
type Output = CollectionsList;
fn method(&self) -> &'static str {
"GET"
}
fn path(&self) -> String {
"/api/collections/smart".into()
}
}
#[derive(Debug, Default, Clone)]
pub struct ListVirtualCollections;
impl Endpoint for ListVirtualCollections {
type Output = Vec<VirtualCollectionRow>;
fn method(&self) -> &'static str {
"GET"
}
fn path(&self) -> String {
"/api/collections/virtual".into()
}
fn query(&self) -> Vec<(String, String)> {
vec![("type".into(), "all".into())]
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::{Collection, VirtualCollectionRow};
#[test]
fn decode_bare_array() {
let v = serde_json::json!([
{"id": 1, "name": "A", "rom_count": 2}
]);
let list: CollectionsList = serde_json::from_value(v).unwrap();
let list = list.into_vec();
assert_eq!(list.len(), 1);
assert_eq!(list[0].id, 1);
assert_eq!(list[0].name, "A");
}
#[test]
fn decode_paged_object() {
let v = serde_json::json!({
"items": [{"id": 2, "name": "B", "rom_count": 0}],
"total": 1
});
let list: CollectionsList = serde_json::from_value(v).unwrap();
let list = list.into_vec();
assert_eq!(list.len(), 1);
assert_eq!(list[0].id, 2);
}
#[test]
fn merge_manual_and_smart_marks_flags() {
let manual = vec![Collection {
id: 1,
name: "m".into(),
collection_type: None,
rom_count: Some(1),
is_smart: true,
is_virtual: false,
virtual_id: None,
}];
let smart = vec![Collection {
id: 2,
name: "s".into(),
collection_type: None,
rom_count: Some(2),
is_smart: false,
is_virtual: false,
virtual_id: None,
}];
let merged = super::merge_manual_and_smart(manual, smart);
assert_eq!(merged.len(), 2);
assert!(!merged[0].is_smart);
assert!(merged[1].is_smart);
}
#[test]
fn merge_all_includes_virtual() {
let manual = vec![Collection {
id: 1,
name: "m".into(),
collection_type: None,
rom_count: Some(1),
is_smart: false,
is_virtual: false,
virtual_id: None,
}];
let virtual_rows = vec![VirtualCollectionRow {
id: "recent".into(),
name: "Recent".into(),
collection_type: "recent".into(),
rom_count: 3,
is_virtual: true,
}];
let merged = super::merge_all_collection_sources(manual, Vec::new(), virtual_rows);
assert_eq!(merged.len(), 2);
assert!(merged[1].is_virtual);
assert_eq!(merged[1].virtual_id.as_deref(), Some("recent"));
}
}