summary refs log tree commit diff
path: root/src/data
diff options
context:
space:
mode:
Diffstat (limited to 'src/data')
-rw-r--r--src/data/mod.rs58
-rw-r--r--src/data/namespace.rs9
-rw-r--r--src/data/page.rs38
3 files changed, 91 insertions, 14 deletions
diff --git a/src/data/mod.rs b/src/data/mod.rs
index 0c61bd7..1465fee 100644
--- a/src/data/mod.rs
+++ b/src/data/mod.rs
@@ -1,4 +1,6 @@
 use serde::{Deserialize, Serialize};
+#[cfg(feature = "ssr")]
+use tokio::sync::Mutex;
 use uuid::Uuid;
 
 #[cfg(feature = "ssr")]
@@ -8,7 +10,7 @@ use std::fs::File;
 #[cfg(feature = "ssr")]
 use std::sync::LazyLock;
 
-use std::{collections::HashMap, path::Path};
+use std::{collections::HashMap, path::Path, sync::Arc};
 
 mod config;
 mod namespace;
@@ -44,29 +46,67 @@ static DATA_LOCK: LazyLock<StormscribeData> = LazyLock::new(|| {
 
     StormscribeData {
         file_lock: lockfile,
-        namespaces: Namespaces::init(&Path::join(&config.data_dir, "namespace/")).unwrap(),
-        pages: Pages::init(&Path::join(&config.data_dir, "pages/")).unwrap(),
+        data_snapshot: Mutex::new(Arc::new(DataSnapshot {
+            namespaces: Namespaces::init(&Path::join(&config.data_dir, "namespace/")).unwrap(),
+            pages: Pages::init(&Path::join(&config.data_dir, "pages/")).unwrap(),
+        })),
     }
 });
 
 #[cfg(feature = "ssr")]
 pub struct StormscribeData {
     file_lock: File,
+    data_snapshot: Mutex<Arc<DataSnapshot>>,
+}
+
+struct DataSnapshot {
     namespaces: Namespaces,
     pages: Pages,
 }
 
+#[derive(Deserialize, Serialize, Clone, Debug)]
+pub struct PageData {
+    path: String,
+    metadata: Option<Page>,
+    content: String,
+}
+
 #[cfg(feature = "ssr")]
 impl StormscribeData {
-    fn get_data() -> &'static Self {
-        &DATA_LOCK
+    async fn get_snapshot() -> Arc<DataSnapshot> {
+        DATA_LOCK.data_snapshot.lock().await.clone()
     }
 
-    pub fn get_namespace() -> Namespace {
-        DATA_LOCK.namespaces.root.clone()
+    pub async fn get_namespace() -> Namespace {
+        StormscribeData::get_snapshot()
+            .await
+            .namespaces
+            .root
+            .clone()
     }
 
-    pub fn get_pages() -> HashMap<PageUuid, Page> {
-        DATA_LOCK.pages.pages.clone()
+    pub async fn get_all_pages() -> HashMap<PageUuid, Page> {
+        StormscribeData::get_snapshot().await.pages.pages.clone()
+    }
+
+    pub async fn get_page_data(page_path: String) -> PageData {
+        let data = Self::get_snapshot().await;
+        let page = data
+            .namespaces
+            .get_page_uuid(&page_path)
+            .map(|page_uuid| data.pages.get_page(&page_uuid))
+            .flatten();
+
+        let content = if let Some(page) = page.cloned() {
+            page.read_content().await.ok()
+        } else {
+            None
+        };
+
+        PageData {
+            path: page_path,
+            metadata: page.cloned(),
+            content: content.unwrap_or(String::new()),
+        }
     }
 }
diff --git a/src/data/namespace.rs b/src/data/namespace.rs
index 714ab37..4aa0419 100644
--- a/src/data/namespace.rs
+++ b/src/data/namespace.rs
@@ -66,8 +66,13 @@ impl Namespaces {
         Ok(Self { root })
     }
 
-    pub fn get_page_uuid(&self, path: String) -> Option<PageUuid> {
-        todo!()
+    pub fn get_page_uuid(&self, path: &String) -> Option<PageUuid> {
+        let mut current_namespace = &self.root;
+        for segment in path.trim_matches('/').split('/') {
+            current_namespace = current_namespace.children.get(segment)?;
+        }
+
+        current_namespace.page.clone()
     }
 
     pub fn remove_page(&self, path: String) -> Result<(), String> {
diff --git a/src/data/page.rs b/src/data/page.rs
index 6d0b802..7b7d432 100644
--- a/src/data/page.rs
+++ b/src/data/page.rs
@@ -2,7 +2,7 @@ use std::{
     collections::HashMap,
     fs::{self, File},
     io::{BufRead, BufReader},
-    path::Path,
+    path::{Path, PathBuf},
 };
 
 use chrono::{DateTime, Utc};
@@ -18,6 +18,7 @@ pub struct Page {
     pub title: String,
     pub current_version: DateTime<Utc>,
     pub prev_versions: Vec<DateTime<Utc>>,
+    disk_path: PathBuf,
     content_offset: usize,
 }
 
@@ -129,12 +130,13 @@ impl Pages {
             title: metadata.title,
             current_version,
             prev_versions,
+            disk_path: current_page,
             content_offset,
         })
     }
 
-    pub fn get_page(&self, uuid: PageUuid) -> Option<Page> {
-        todo!()
+    pub fn get_page(&self, uuid: &PageUuid) -> Option<&Page> {
+        self.pages.get(uuid)
     }
 
     pub fn create_page(&self, page: Page) {
@@ -149,3 +151,33 @@ impl Pages {
         todo!()
     }
 }
+
+#[cfg(feature = "ssr")]
+impl Page {
+    pub async fn read_content(&self) -> Result<String, String> {
+        use std::io::Read;
+
+        let file_meta =
+            fs::metadata(&self.disk_path).map_err(|_| "Cannot retrieve file size".to_string())?;
+        let read_length = usize::try_from(file_meta.len())
+            .map_err(|_| "Cannot get file offset".to_string())?
+            - self.content_offset;
+
+        let mut reader = BufReader::new(
+            File::open(&self.disk_path).map_err(|_| "Could not open page file".to_string())?,
+        );
+
+        reader
+            .seek_relative(
+                i64::try_from(self.content_offset)
+                    .map_err(|_| "Invalid seek length".to_string())?,
+            )
+            .map_err(|_| "Could not seek in page file".to_string())?;
+
+        let mut contents = String::with_capacity(read_length);
+        reader
+            .read_to_string(&mut contents)
+            .map_err(|_| "Could not read file".to_string())?;
+        Ok(contents)
+    }
+}