diff options
Diffstat (limited to 'src/data')
-rw-r--r-- | src/data/mod.rs | 58 | ||||
-rw-r--r-- | src/data/namespace.rs | 9 | ||||
-rw-r--r-- | src/data/page.rs | 38 |
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) + } +} |