use serde::{Deserialize, Serialize};
use super::{JsonStore, StoreLocation};
use crate::providers::ProviderId;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LibraryKind {
Album,
Artist,
Playlist,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LibraryItem {
pub kind: LibraryKind,
pub id: String,
pub title: String,
pub thumbnail: String,
#[serde(default)]
pub provider: ProviderId,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct LibraryStore {
pub items: Vec<LibraryItem>,
}
impl JsonStore for LibraryStore {
const FILE: &'static str = "library.json";
const LOCATION: StoreLocation = StoreLocation::Data;
}
impl LibraryStore {
pub fn contains(&self, kind: LibraryKind, id: &str) -> bool {
self.items.iter().any(|it| it.kind == kind && it.id == id)
}
pub fn add(&mut self, item: LibraryItem) {
if !self.contains(item.kind, &item.id) {
self.items.push(item);
self.save_to_disk();
}
}
pub fn remove(&mut self, kind: LibraryKind, id: &str) -> bool {
let before = self.items.len();
self.items.retain(|it| !(it.kind == kind && it.id == id));
let removed = self.items.len() != before;
if removed {
self.save_to_disk();
}
removed
}
pub fn insert(&mut self, item: LibraryItem, pos: usize) {
if self.contains(item.kind, &item.id) {
return;
}
let pos = pos.min(self.items.len());
self.items.insert(pos, item);
self.save_to_disk();
}
pub fn move_item(&mut self, from: usize, to: usize) {
if from >= self.items.len() {
return;
}
let item = self.items.remove(from);
let to = to.min(self.items.len());
self.items.insert(to, item);
self.save_to_disk();
}
fn save_to_disk(&self) {
JsonStore::save(self);
}
}
#[cfg(test)]
mod tests {
use super::*;
fn item(kind: LibraryKind, id: &str) -> LibraryItem {
LibraryItem {
kind,
id: id.to_string(),
title: format!("Title {id}"),
thumbnail: String::new(),
provider: ProviderId::default(),
}
}
#[test]
fn save_is_idempotent() {
let mut s = LibraryStore::default();
s.add(item(LibraryKind::Album, "a1"));
s.add(item(LibraryKind::Album, "a1"));
assert_eq!(s.items.len(), 1);
}
#[test]
fn save_keeps_distinct_kinds_same_id() {
let mut s = LibraryStore::default();
s.add(item(LibraryKind::Album, "x"));
s.add(item(LibraryKind::Artist, "x"));
assert_eq!(s.items.len(), 2);
}
#[test]
fn remove_uses_kind_and_id() {
let mut s = LibraryStore::default();
s.add(item(LibraryKind::Album, "x"));
s.add(item(LibraryKind::Artist, "x"));
assert!(s.remove(LibraryKind::Album, "x"));
assert!(!s.remove(LibraryKind::Album, "x"));
assert_eq!(s.items.len(), 1);
}
#[test]
fn insert_and_move_items() {
let mut s = LibraryStore::default();
s.add(item(LibraryKind::Artist, "a"));
s.add(item(LibraryKind::Album, "b"));
s.add(item(LibraryKind::Playlist, "c"));
s.insert(item(LibraryKind::Artist, "d"), 1);
assert_eq!(s.items.len(), 4);
assert_eq!(s.items[1].id, "d");
s.insert(item(LibraryKind::Artist, "a"), 0);
assert_eq!(s.items.len(), 4);
s.move_item(3, 1);
assert_eq!(s.items.len(), 4);
assert_eq!(s.items[1].id, "c");
}
}