Echo Writes Code

content.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use crate::errors::{ Error, Result };

use serde::{ Deserialize };
use serde_json::{ self };

use std::fs::{ self };
use std::path::{ Path, PathBuf };

#[derive(Debug)]
pub(crate) struct Provider {
	root: PathBuf,
	metadata: Metadata,
}

impl Provider {
	pub(crate) fn new<P: AsRef<Path>>(root: P, metadata_file: &str) -> Result<Provider> {
		let metadata_json = fs::read_to_string(root.as_ref().join(metadata_file))?;
		let metadata: Metadata = serde_json::from_str(&metadata_json)?;

		Ok(Provider {
			root: root.as_ref().to_path_buf(),
			metadata,
		})
	}

	pub(crate) fn site_link(&self) -> String {
		self.metadata.site.link.clone()
	}

	pub(crate) fn site_name(&self) -> String {
		self.metadata.site.name.clone()
	}

	pub(crate)fn site_menu(&self) -> Vec<Link> {
		self.metadata.site.menu.clone()
	}

	pub(crate) fn find_list(&self, list_path: &str) -> Result<List> {
		let maybe_list = self.metadata.lists
			.iter()
			.find(|list| list.slug == list_path);

		let Some(list) = maybe_list else {
			return Err(Error::ListNotFound(list_path.to_string()))
		};

		Ok(list.clone())
	}

	pub(crate) fn find_list_items(&self, list: &List) -> Vec<Page> {
		let mut items = Vec::new();

		for page in &self.metadata.pages {
			if page.slug.starts_with(&list.slug) {
				items.push(page.clone());
			}
		}

		items
	}

	pub(crate) fn find_page(&self, page_path: &str) -> Result<Page> {
		let maybe_page = self.metadata.pages
			.iter()
			.find(|page| page.slug == page_path);

		let Some(page) = maybe_page else {
			return Err(Error::PageNotFound(page_path.to_string()));
		};

		Ok(page.clone())
	}

	pub(crate) fn render_page(&self, page: &Page) -> Result<String> {
		let content_path = self.root.join("content").join(&page.slug).with_extension("md");
		let content = fs::read_to_string(content_path)?;

		let parser = pulldown_cmark::Parser::new(&content);
		let mut rendered = String::new();
		pulldown_cmark::html::push_html(&mut rendered, parser);

		Ok(rendered)
	}
}

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct Metadata {
	pub(crate) site: Site,
	pub(crate) lists: Vec<List>,
	pub(crate) pages: Vec<Page>,
}

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct Site {
	pub(crate) link: String,
	pub(crate) name: String,
	pub(crate) menu: Vec<Link>,
}

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct List {
	pub(crate) name: String,
	pub(crate) slug: String,
}

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct Page {
	pub(crate) name: String,
	pub(crate) slug: String,
}

#[derive(Clone, Debug, Deserialize)]
pub(crate) struct Link {
	pub(crate) link: String,
	pub(crate) name: String,
}