1use crate::{Error, FlatPage, Result};
2use serde::de::DeserializeOwned;
3use std::{collections::HashMap, fs, path::PathBuf};
4
5#[derive(Debug)]
7pub struct FlatPageStore {
8 root: PathBuf,
10 pub pages: HashMap<String, FlatPageMeta>,
12}
13
14#[derive(Debug)]
16pub struct FlatPageMeta {
17 pub title: String,
19 pub description: Option<String>,
21}
22
23impl FlatPageStore {
24 pub fn read_dir(root: impl Into<PathBuf>) -> Result<Self> {
26 let root = root.into();
27 let mut pages = HashMap::new();
28 let md_ext = Some(std::ffi::OsStr::new("md"));
29 for entry in fs::read_dir(&root).map_err(|e| Error::ReadDir(e, root.clone()))? {
30 let entry = entry.map_err(Error::DirEntry)?;
31 let path = entry.path();
32 if !path.is_file() || path.extension() != md_ext {
33 continue;
34 }
35 let stem = match path.file_stem().and_then(|x| x.to_str()) {
36 Some(s) => s,
37 None => continue,
38 };
39 let page = match FlatPage::by_path(&path)? {
40 Some(p) => p,
41 None => continue,
42 };
43 pages.insert(stem.into(), page.into());
44 }
45 Ok(Self { root, pages })
46 }
47
48 pub fn meta_by_url(&self, url: &str) -> Option<&FlatPageMeta> {
50 let stem = Self::url_to_stem(url);
51 self.meta_by_stem(&stem)
52 }
53
54 pub fn page_by_url<E: DeserializeOwned>(&self, url: &str) -> Result<Option<FlatPage<E>>> {
56 let stem = Self::url_to_stem(url);
57 self.page_by_stem(&stem)
58 }
59
60 pub fn meta_by_stem(&self, stem: &str) -> Option<&FlatPageMeta> {
62 self.pages.get(stem)
63 }
64
65 pub fn page_by_stem<E: DeserializeOwned>(&self, stem: &str) -> Result<Option<FlatPage<E>>> {
67 if self.pages.contains_key(stem) {
68 let mut path = self.root.clone();
69 path.push(format!("{stem}.md"));
70 FlatPage::by_path(path)
71 } else {
72 Ok(None)
73 }
74 }
75
76 fn url_to_stem(url: &str) -> String {
78 url.replace('/', "^")
79 }
80}
81
82impl From<FlatPage> for FlatPageMeta {
83 fn from(p: FlatPage) -> Self {
84 Self {
85 title: p.title,
86 description: p.description,
87 }
88 }
89}