diff options
Diffstat (limited to 'server/src/library.rs')
-rw-r--r-- | server/src/library.rs | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/server/src/library.rs b/server/src/library.rs new file mode 100644 index 0000000..0c42a73 --- /dev/null +++ b/server/src/library.rs @@ -0,0 +1,141 @@ +use crate::metadata::{DirectoryInfo, ItemInfo}; +use anyhow::{anyhow, bail, Context, Ok}; +use std::{ffi::OsStr, fs::File, path::PathBuf, sync::Arc}; + +pub struct Library { + pub root: Arc<Node>, +} + +#[derive(Debug, Clone)] +pub enum Node { + Directory(Arc<Directory>), + Item(Arc<Item>), +} + +#[derive(Debug, Clone)] +pub struct Directory { + pub lib_path: PathBuf, + pub identifier: String, + pub data: DirectoryInfo, + pub children: Vec<Arc<Node>>, +} + +#[derive(Debug, Clone)] +pub struct Item { + pub lib_path: PathBuf, + pub identifier: String, + pub data: ItemInfo, +} + +impl Library { + pub fn open(path: &str) -> anyhow::Result<Self> { + Ok(Self { + root: Node::from_path(path.into(), PathBuf::new(), true).context("indexing root")?, + }) + } + pub fn nested(&self, path: &str) -> anyhow::Result<Arc<Node>> { + let mut n = self.root.clone(); + if path == "" { + return Ok(n); + } + for seg in path.split("/") { + n = n + .get_directory()? + .child_by_ident(seg) + .ok_or(anyhow!("does not exist"))?; + } + Ok(n) + } +} + +impl Node { + pub fn get_directory(&self) -> anyhow::Result<&Directory> { + match self { + Node::Directory(d) => Ok(d), + Node::Item(_) => bail!("not a directory"), + } + } + pub fn title(&self) -> &str { + match self { + Node::Directory(d) => &d.data.name, + Node::Item(i) => &i.data.title, + } + } + pub fn identifier(&self) -> &str { + match self { + Node::Directory(d) => &d.identifier, + Node::Item(i) => &i.identifier, + } + } + pub fn from_path( + path: PathBuf, + mut lib_path: PathBuf, + root: bool, + ) -> anyhow::Result<Arc<Node>> { + if path.is_dir() { + let mpath = path.join("directory.json"); + let data: DirectoryInfo = + serde_json::from_reader(File::open(mpath).context("metadata missing")?)?; + + let identifier = path.file_name().unwrap().to_str().unwrap().to_string(); + if !root { + lib_path = lib_path.join(identifier.clone()); + } + + let children = path + .read_dir()? + .map(|e| e.unwrap().path()) + .filter(|e| e.extension() != Some(OsStr::new("json"))) + .map(|e| { + Node::from_path(e.clone(), lib_path.clone(), false) + .context(format!("loading {e:?}")) + }) + .into_iter() + .collect::<anyhow::Result<Vec<_>>>()?; + + Ok(Node::Directory(Arc::new(Directory { + lib_path, + children, + data, + identifier, + })) + .into()) + } else if path.is_file() { + let mpath = path.clone().with_extension("metadata.json"); + let datafile = File::open(mpath.clone()) + .context(format!("metadata missing, tried path {mpath:?}"))?; + let data: ItemInfo = serde_json::from_reader(datafile).context("invalid metadata")?; + let identifier = path + .with_extension("") + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(); + Ok(Node::Item(Arc::new(Item { + lib_path: lib_path.join(identifier.clone()), + data, + identifier, + })) + .into()) + } else { + bail!("did somebody really put a fifo or socket in the library?!") + } + } +} +impl Item { + pub fn path(&self) -> String { + self.lib_path.to_str().unwrap().to_string() + } +} +impl Directory { + pub fn path(&self) -> String { + self.lib_path.to_str().unwrap().to_string() + } + pub fn child_by_ident(&self, i: &str) -> Option<Arc<Node>> { + self.children + .iter() + .find(|e| e.identifier() == i) + .map(|e| e.to_owned()) + } +} |