blog_tools/low/
types.rs

1use std::fs;
2
3use chrono::NaiveDate;
4use markdown::{to_html_with_options, Options};
5use serde::{Deserialize, Serialize};
6
7use crate::{
8    common::{get_json_data, preview::get_preview, toc, BlogError, BlogJson},
9    types::Blog,
10};
11
12/// An individual blog post
13#[derive(Debug, Serialize, Deserialize, Clone)]
14pub struct LowBlogEntry {
15    /// Title of the blog post
16    pub title: String,
17    /// Date published
18    pub date: NaiveDate,
19    /// Description
20    pub desc: Option<String>,
21    /// The blog post HTML that would be included into a template
22    pub html: String,
23    /// The URL slug
24    pub slug: String,
25    /// `Vec` of tags for this blog
26    pub tags: Vec<String>,
27    /// Table of contents
28    pub toc: Option<String>,
29    /// Optional `Vec` of keywords. Intended for SEO in comparison to tags
30    pub keywords: Option<Vec<String>>,
31    /// Optional canonical link, intended for SEO
32    pub canonical_link: Option<String>,
33    /// Optional author name
34    pub author_name: Option<String>,
35    /// Optional URL for the author
36    pub author_webpage: Option<String>,
37    last_modified: Option<NaiveDate>,
38    priority: Option<f64>,
39    previw_chars: Option<usize>,
40}
41
42impl Blog for LowBlogEntry {
43    fn create<T: AsRef<std::path::Path>>(
44        blog: T,
45        toc_generation_func: Option<&dyn Fn(&markdown::mdast::Node) -> String>,
46        preview_chars: Option<usize>,
47    ) -> Result<Self, crate::common::BlogError> {
48        let json = get_json_data(&blog)?;
49
50        let markdown = match fs::read_to_string(blog) {
51            Ok(x) => x,
52            Err(y) => return Err(BlogError::File(y)),
53        };
54
55        let html = match to_html_with_options(
56            &markdown,
57            &Options {
58                compile: markdown::CompileOptions {
59                    allow_dangerous_html: true,
60                    allow_dangerous_protocol: true,
61
62                    ..markdown::CompileOptions::default()
63                },
64                ..markdown::Options::default()
65            },
66        ) {
67            Ok(x) => x,
68            Err(y) => return Err(BlogError::Markdown(y.to_string())),
69        };
70
71        let toc = toc(&markdown, toc_generation_func)?;
72
73        return Ok(LowBlogEntry {
74            title: json.title,
75            date: json.date,
76            desc: json.desc,
77            html: html,
78            slug: json.slug,
79            tags: json.tags,
80            toc: toc,
81            keywords: json.keywords,
82            canonical_link: json.canonical_link,
83            author_name: json.author_name,
84            author_webpage: json.author_webpage,
85            last_modified: json.last_modified,
86            priority: json.priority,
87            previw_chars: preview_chars,
88        });
89    }
90    fn get_title(&self) -> String {
91        return self.title.clone();
92    }
93
94    fn get_date_listed(&self) -> NaiveDate {
95        return self.date.clone();
96    }
97
98    fn get_description(&self) -> Option<String> {
99        return self.desc.clone();
100    }
101
102    fn get_html(&self) -> String {
103        return self.html.clone();
104    }
105
106    fn get_full_slug(&self) -> String {
107        return format!("{}/{}", self.get_date_listed(), self.get_part_slug());
108    }
109
110    fn get_part_slug(&self) -> String {
111        return self.slug.clone();
112    }
113
114    fn get_tags(&self) -> Vec<String> {
115        return self.tags.clone();
116    }
117
118    fn get_table_of_contents(&self) -> Option<String> {
119        return self.toc.clone();
120    }
121
122    fn get_keywords(&self) -> Option<Vec<String>> {
123        return self.keywords.clone();
124    }
125
126    fn get_canonicle_link(&self) -> Option<String> {
127        return self.canonical_link.clone();
128    }
129
130    fn get_author_name(&self) -> Option<String> {
131        return self.author_name.clone();
132    }
133
134    fn get_author_webpage(&self) -> Option<String> {
135        return self.author_webpage.clone();
136    }
137
138    fn get_preview(&self) -> String {
139        let preview = get_preview(&self.html, self.previw_chars);
140
141        return preview;
142    }
143
144    fn get_last_modified(&self) -> Option<NaiveDate> {
145        return self.last_modified.clone();
146    }
147
148    fn get_priority(&self) -> Option<f64> {
149        return self.priority.clone();
150    }
151}
152
153impl LowBlogEntry {
154    pub(crate) fn new(json: BlogJson, html: String, toc: Option<String>) -> Self {
155        return LowBlogEntry {
156            title: json.title,
157            date: json.date,
158            desc: json.desc,
159            html: html,
160            slug: json.slug,
161            tags: json.tags,
162            toc: toc,
163            keywords: json.keywords,
164            canonical_link: json.canonical_link,
165            author_name: json.author_name,
166            author_webpage: json.author_webpage,
167            last_modified: json.last_modified,
168            priority: json.priority,
169            previw_chars: None,
170        };
171    }
172}
173
174/// An individual blog post
175#[derive(Debug, Serialize, Deserialize, Clone)]
176pub struct PreviewBlogEntry {
177    /// Title of the blog post
178    pub title: String,
179    /// Date published
180    pub date: NaiveDate,
181    /// Description
182    pub desc: Option<String>,
183    /// The URL slug
184    pub slug: String,
185    /// `Vec` of tags for this blog
186    pub tags: Vec<String>,
187    /// Optional `Vec` of keywords. Intended for SEO in comparison to tags
188    pub keywords: Option<Vec<String>>,
189    /// Optional canonical link, intended for SEO
190    pub canonical_link: Option<String>,
191    /// Optional author name
192    pub author_name: Option<String>,
193    /// Optional URL for the author
194    pub author_webpage: Option<String>,
195    /// Preview of the blogpost, useful for showing on index pages
196    pub preview: String,
197}
198
199impl PreviewBlogEntry {
200    pub(crate) fn new(json: BlogJson, preview: String) -> Self {
201        return PreviewBlogEntry {
202            title: json.title,
203            date: json.date,
204            desc: json.desc,
205            slug: json.slug,
206            tags: json.tags,
207            keywords: json.keywords,
208            canonical_link: json.canonical_link,
209            author_name: json.author_name,
210            author_webpage: json.author_webpage,
211            preview: preview,
212        };
213    }
214}