use anyhow::{anyhow, Result};
use std::{fs, path};
extern crate dirs;
use crate::media;
pub struct Repo {
pub path: path::PathBuf,
items: Vec<media::Media>,
}
impl Repo {
pub fn new(path: &path::Path) -> Result<Self> {
let mut repo = Repo {
path: path.to_path_buf(),
items: vec![],
};
repo.read()?;
Ok(repo)
}
pub fn get(&mut self, handle: &media::handle::Handle) -> Option<&mut media::Media> {
self.items.iter_mut().find(|m| m.matches_handle(handle))
}
pub fn get_or_create(&mut self, handle: &media::handle::Handle) -> Result<&mut media::Media> {
if self.get(handle).is_none() {
self.add(media::Media::from_handle(handle))?;
println!("Added new item: {handle}");
}
Ok(self.get(handle).unwrap())
}
pub fn get_all(&self) -> Vec<&media::Media> {
self.items.iter().collect()
}
pub fn update(
&mut self,
handle: &media::handle::Handle,
f: impl FnOnce(&mut media::Media),
) -> Result<()> {
match self.get(handle) {
Some(item) => {
f(item);
Ok(())
}
None => Err(anyhow!("item not found: {handle}")),
}
}
pub fn add(&mut self, item: media::Media) -> Result<()> {
self.items.push(item);
Ok(())
}
pub fn remove_by_handle(&mut self, handle: &media::handle::Handle) -> Result<()> {
match self.items.iter().position(|m| m.matches_handle(handle)) {
Some(index) => {
self.items.swap_remove(index);
Ok(())
}
None => Err(anyhow!("item not found: {}", &handle)),
}
}
fn read(&mut self) -> Result<()> {
let file_content = fs::read_to_string(&self.path).unwrap_or_default();
let blocks = file_content
.split("\n\n")
.filter(|b| !b.is_empty())
.map(str::trim);
for block in blocks {
self.items.push(media::Media::from_db_entry(block)?);
}
Ok(())
}
pub fn write(&self) -> Result<()> {
std::fs::create_dir_all(self.path.parent().unwrap())?;
let mut output = String::new();
for entry in self.items.iter().map(media::Media::to_db_entry) {
output += &entry;
output += "\n\n";
}
Ok(fs::write(&self.path, output.trim())?)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn reads() {
let mut path = std::env::temp_dir();
path.push("mtracker_test_reads.txt");
fs::write(
&path,
"Forrest Gump
year: 1994
Alien
year: 1979
Aliens
year: 1986
Alien 3
year: 1992
The Terminator
year: 1984
",
)
.unwrap();
let repo = Repo::new(&path).unwrap();
let items = repo.get_all();
assert_eq!(items[0].name, "Forrest Gump");
assert_eq!(items[0].year, Some(1994));
assert_eq!(items[1].name, "Alien");
assert_eq!(items[1].year, Some(1979));
assert_eq!(items[2].name, "Aliens");
assert_eq!(items[2].year, Some(1986));
assert_eq!(items[3].name, "Alien 3");
assert_eq!(items[3].year, Some(1992));
assert_eq!(items[4].name, "The Terminator");
assert_eq!(items[4].year, Some(1984));
fs::remove_file(&path).ok();
}
#[test]
fn writes() {
let mut path = std::env::temp_dir();
path.push("mtracker_test_writes.txt");
fs::remove_file(&path).ok();
let mut repo = Repo::new(&path).unwrap();
repo.add(media::Media::new("Forrest Gump", Some(1994))).ok();
repo.add(media::Media::new("Alien", Some(1979))).ok();
repo.write().unwrap();
assert_eq!(
fs::read_to_string(&path).unwrap(),
"Forrest Gump
year: 1994
Alien
year: 1979"
);
fs::remove_file(&path).ok();
}
}