summary refs log tree commit diff
path: root/src/data/page.rs
diff options
context:
space:
mode:
authortempest <git@ashen.earth>2025-04-15 00:08:12 -0600
committertempest <git@ashen.earth>2025-04-15 00:08:12 -0600
commitb856f12cf422b96c37c12df3d7829e4d15ef4453 (patch)
tree4547df3ec3d347715292a860a5a63207fe428de3 /src/data/page.rs
parent128cc42557c8d7da46c63a40ea4469ed0eb7f26d (diff)
Can find content data
Diffstat (limited to 'src/data/page.rs')
-rw-r--r--src/data/page.rs151
1 files changed, 151 insertions, 0 deletions
diff --git a/src/data/page.rs b/src/data/page.rs
new file mode 100644
index 0000000..6d0b802
--- /dev/null
+++ b/src/data/page.rs
@@ -0,0 +1,151 @@
+use std::{
+    collections::HashMap,
+    fs::{self, File},
+    io::{BufRead, BufReader},
+    path::Path,
+};
+
+use chrono::{DateTime, Utc};
+use serde::{Deserialize, Serialize};
+use uuid::Uuid;
+
+use super::PageUuid;
+
+#[derive(Clone, Deserialize, Serialize, Debug)]
+pub struct Page {
+    pub uuid: PageUuid,
+    pub author: Uuid,
+    pub title: String,
+    pub current_version: DateTime<Utc>,
+    pub prev_versions: Vec<DateTime<Utc>>,
+    content_offset: usize,
+}
+
+pub struct Pages {
+    pub pages: HashMap<PageUuid, Page>,
+}
+
+const METADATA_DIVIDER: &'static str = "<!-- trans rights ~ath&+ -->";
+
+#[cfg(feature = "ssr")]
+impl Pages {
+    pub fn init(pages_dir: &Path) -> Result<Self, String> {
+        // Read dir
+        let page_dirs = fs::read_dir(&pages_dir)
+            .map_err(|_| "Could not open pages data directory".to_string())?;
+
+        // Parse each
+        let pages = page_dirs
+            .map(|dir_entry| -> Result<Page, String> {
+                let page_dir_path = dir_entry.as_ref().unwrap().path();
+
+                Pages::read_page(&page_dir_path)
+            })
+            .collect::<Result<Vec<Page>, String>>()?;
+
+        // Build lookup
+        Ok(Self {
+            pages: pages
+                .into_iter()
+                .map(|page| (page.uuid.clone(), page))
+                .collect::<HashMap<_, _>>(),
+        })
+    }
+
+    fn read_page(page_dir: &Path) -> Result<Page, String> {
+        let current_page = page_dir
+            .join("current")
+            .canonicalize()
+            .map_err(|_| "Could not canonicalize page location".to_string())?;
+
+        let mut reader = BufReader::new(
+            File::open(&current_page).map_err(|_| "Could not open page file".to_string())?,
+        );
+        let page_uuid = PageUuid(
+            Uuid::try_parse(
+                &page_dir
+                    .file_name()
+                    .ok_or("Could not read page directory".to_string())?
+                    .to_str()
+                    .unwrap(),
+            )
+            .map_err(|_| "Could not parse page UUID".to_string())?,
+        );
+
+        let mut metadata_string = String::new();
+        let mut current_line = String::new();
+        let mut content_offset = 0;
+        'readloop: while let Ok(size) = reader.read_line(&mut current_line) {
+            content_offset += size;
+            if size == 0 {
+                return Err("Page file is invalid".to_string());
+            }
+
+            if current_line.trim() == METADATA_DIVIDER {
+                break 'readloop;
+            }
+
+            metadata_string.push_str(&current_line);
+            current_line.truncate(0);
+        }
+
+        #[derive(Deserialize)]
+        struct PageMetadata {
+            title: String,
+            author: String,
+            prev_versions: Option<Vec<String>>,
+        }
+
+        let metadata: PageMetadata = toml::from_str(&metadata_string).map_err(|err| {
+            println!("{err:?}");
+            "Page metadata is invalid".to_string()
+        })?;
+        let current_version = DateTime::parse_from_rfc3339(
+            current_page
+                .file_name()
+                .unwrap()
+                .to_str()
+                .unwrap()
+                .replace("_", ":")
+                .as_str(),
+        )
+        .map_err(|_| "Invalid date format".to_string())?
+        .to_utc();
+        let prev_versions = metadata
+            .prev_versions
+            .unwrap_or(Vec::new())
+            .iter()
+            .filter_map(|str| {
+                DateTime::parse_from_rfc3339(str.replace("_", ":").as_str())
+                    .ok()
+                    .map(|timestamp| timestamp.to_utc())
+            })
+            .collect::<Vec<_>>();
+
+        Ok(Page {
+            uuid: page_uuid,
+            author: Uuid::try_parse(&metadata.author)
+                .map_err(|_| "Could not parse author UUID".to_string())?,
+            title: metadata.title,
+            current_version,
+            prev_versions,
+            content_offset,
+        })
+    }
+
+    pub fn get_page(&self, uuid: PageUuid) -> Option<Page> {
+        todo!()
+    }
+
+    pub fn create_page(&self, page: Page) {
+        todo!()
+    }
+
+    pub fn update_page(&self, page: Page) {
+        todo!()
+    }
+
+    pub fn delete_page(&self, uuid: PageUuid) -> Result<(), String> {
+        todo!()
+    }
+}