aboutsummaryrefslogtreecommitdiff
path: root/server/src/library.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/library.rs')
-rw-r--r--server/src/library.rs141
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())
+ }
+}