use std::{fs::File, path::PathBuf, str::FromStr, sync::Arc}; use anyhow::{bail, Context, Ok}; use chashmap::CHashMap; use serde::{Deserialize, Serialize}; pub struct Library { path: PathBuf, cache: CHashMap, // TODO } #[derive(Debug, Clone)] pub enum LibNode { Directory(Arc), Item(Arc), } #[derive(Debug, Clone)] pub struct LibDirectory { pub path: PathBuf, pub child_nodes: Vec, pub data: LibDirectoryData, } #[derive(Debug, Clone)] pub struct LibItem { pub data: LibItemData, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct LibDirectoryData { pub name: String, } #[derive(Debug, Clone, Deserialize, Serialize)] pub struct LibItemData {} impl Library { pub fn open(path: &str) -> anyhow::Result { Ok(Self { path: PathBuf::from_str(path).unwrap(), cache: CHashMap::new(), }) } pub fn root(&self) -> anyhow::Result> { LibNode::from_path(self.path.clone()) } pub fn nested(&self, path: &str) -> anyhow::Result> { let mut n = self.root()?; if path == "" { return Ok(n); } for seg in path.split("/") { n = n.get_directory()?.get_child(seg)? } Ok(n) } } impl LibDirectory { pub fn get_child(&self, p: &str) -> anyhow::Result> { if p.contains("..") || p.starts_with("/") { bail!("no! dont do that.") } let path = self.path.join(p); // if !path.exists() {bail!("does not exist");} LibNode::from_path(path) } } impl LibNode { pub fn get_directory(&self) -> anyhow::Result<&LibDirectory> { match self { LibNode::Directory(d) => Ok(d), LibNode::Item(_) => bail!("not a directory"), } } pub fn from_path(path: PathBuf) -> anyhow::Result> { if path.is_dir() { let mpath = path.join("directory.json"); let data: LibDirectoryData = serde_json::from_reader(File::open(mpath).context("metadata missing")?)?; let child_nodes = path.read_dir()?.map(|e| e.unwrap().path()).collect(); Ok(LibNode::Directory(Arc::new(LibDirectory { path, child_nodes, data, })) .into()) } else if path.is_file() { let mpath = path.clone().with_extension(".metadata.json"); let data: LibItemData = serde_json::from_reader(File::open(mpath)?)?; Ok(LibNode::Item(Arc::new(LibItem { data })).into()) } else { bail!("did somebody really put a fifo or socket in the library?!") } } }