use std::{collections::HashMap, fs, path::PathBuf};
use chrono::{Datelike, NaiveDate};
use markdown::{to_html_with_options, Options};
use serde::{Deserialize, Serialize};
use crate::{
common::{get_json_data, preview::get_preview, toc, BlogError, BlogJson},
high::HighBlogEntry,
types::Blog,
};
#[derive(Serialize, Deserialize)]
pub struct MediumBlog {
pub hash: HashMap<String, MediumBlogEntry>,
pub entries: Vec<MediumBlogEntry>,
pub tags: Vec<String>,
pub sitemap: String,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct MediumBlogEntry {
title: String,
date: NaiveDate,
desc: Option<String>,
slug: String,
tags: Vec<String>,
toc: Option<String>,
keywords: Option<Vec<String>>,
canonical_link: Option<String>,
author_name: Option<String>,
author_webpage: Option<String>,
preview: String,
file_name: String, last_modified: Option<NaiveDate>,
priority: Option<f64>,
}
impl Blog for MediumBlogEntry {
fn create<T: AsRef<std::path::Path>>(
blog: T,
toc_generation_func: Option<&dyn Fn(&markdown::mdast::Node) -> String>,
preview_chars: Option<usize>,
) -> Result<Self, BlogError> {
let json = get_json_data(&blog)?;
let markdown = match fs::read_to_string(&blog) {
Ok(x) => x,
Err(y) => return Err(BlogError::File(y)),
};
let html = match to_html_with_options(
&markdown,
&Options {
compile: markdown::CompileOptions {
allow_dangerous_html: true,
allow_dangerous_protocol: true,
..markdown::CompileOptions::default()
},
..markdown::Options::default()
},
) {
Ok(x) => x,
Err(y) => return Err(BlogError::Markdown(y)),
};
let preview: String = get_preview(&html, preview_chars);
let toc = toc(&markdown, toc_generation_func)?;
let file_name = match blog.as_ref().file_name() {
Some(x) => x.to_str().unwrap().to_string(),
None => return Err(BlogError::FileNotFound),
};
return Ok(MediumBlogEntry::new(json, toc, preview, file_name));
}
fn get_title(&self) -> String {
return self.title.clone();
}
fn get_date_listed(&self) -> NaiveDate {
return self.date.clone();
}
fn get_description(&self) -> Option<String> {
return self.desc.clone();
}
fn get_html(&self) -> String {
todo!();
}
fn get_full_slug(&self) -> String {
return format!("{}/{}", self.get_date_listed(), self.get_part_slug());
}
fn get_part_slug(&self) -> String {
return self.slug.clone();
}
fn get_tags(&self) -> Vec<String> {
return self.tags.clone();
}
fn get_table_of_contents(&self) -> Option<String> {
return self.toc.clone();
}
fn get_keywords(&self) -> Option<Vec<String>> {
return self.keywords.clone();
}
fn get_canonicle_link(&self) -> Option<String> {
return self.canonical_link.clone();
}
fn get_author_name(&self) -> Option<String> {
return self.author_name.clone();
}
fn get_author_webpage(&self) -> Option<String> {
return self.author_webpage.clone();
}
fn get_preview(&self) -> String {
return self.preview.clone();
}
fn get_last_modified(&self) -> Option<NaiveDate> {
return self.last_modified.clone();
}
fn get_priority(&self) -> Option<f64> {
return self.priority.clone();
}
}
impl MediumBlogEntry {
pub(crate) fn new(
json: BlogJson,
toc: Option<String>,
preview: String,
file_name: String,
) -> Self {
return MediumBlogEntry {
title: json.title,
date: json.date,
desc: json.desc,
slug: json.slug,
tags: json.tags,
toc: toc,
keywords: json.keywords,
canonical_link: json.canonical_link,
author_name: json.author_name,
author_webpage: json.author_webpage,
preview: preview,
file_name,
last_modified: json.last_modified,
priority: json.priority,
};
}
pub fn render(&self, base: PathBuf) -> Result<HighBlogEntry, BlogError> {
let year = self.date.year();
let path = base
.join(format!("{}", year))
.join(format!("{}", self.date))
.join(self.file_name.clone());
let md = match fs::read_to_string(path) {
Ok(x) => x,
Err(y) => return Err(BlogError::File(y)),
};
let html = match to_html_with_options(
&md,
&Options {
compile: markdown::CompileOptions {
allow_dangerous_html: true,
allow_dangerous_protocol: true,
..markdown::CompileOptions::default()
},
..markdown::Options::default()
},
) {
Ok(x) => x,
Err(y) => return Err(BlogError::Markdown(y)),
};
let high = HighBlogEntry::new_from_medium(self, html);
return Ok(high);
}
}