diff --git a/Cargo.lock b/Cargo.lock index 86dbd0c..beee4b2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -350,6 +350,15 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +[[package]] +name = "html-escape" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" +dependencies = [ + "utf8-width", +] + [[package]] name = "html5ever" version = "0.26.0" @@ -512,6 +521,7 @@ version = "0.2.0" dependencies = [ "chrono", "derive_more", + "html-escape", "markdown", "regex", "scraper", @@ -1194,6 +1204,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8-width" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86bd8d4e895da8537e5315b8254664e6b769c4ff3db18321b297a1e7004392e3" + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index a11a235..fe15dc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,3 +16,4 @@ derive_more = "0.99" scraper = "0.19" serde_json = "1.0" xml-builder = "0.5" +html-escape = "0.2" diff --git a/src/config.rs b/src/config.rs index 63cf390..0b422a6 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,6 +13,7 @@ pub struct Config { pub struct Syndication { pub title: String, pub link: String, + pub blog_root: String, pub icon: Option, pub subtitle: Option, pub atom: Option, diff --git a/src/emoji.rs b/src/emoji.rs index 19b63ea..ae1ee58 100644 --- a/src/emoji.rs +++ b/src/emoji.rs @@ -19,7 +19,7 @@ pub fn emoji_pass(markdown: &str, emoji_config: &Option) -> continue; } let html_string = format!( - "", + "", emoji_config.as_ref().unwrap().emoji_web_directory, emoji_file_name.unwrap().to_str().unwrap() ); diff --git a/src/index.rs b/src/index.rs index e661ea2..29bf178 100644 --- a/src/index.rs +++ b/src/index.rs @@ -8,12 +8,14 @@ use tera::{Context, Tera}; pub struct BlogPost { pub title: String, pub human_date: String, - pub sort_date: i64, // Sort date = unix timestamp - pub content: String, // Unformatted Content of blog post can be used to display a preview or reading time estimate. Will be html when assigned but later turned into raw text + pub sort_date: i64, // Sort date = unix timestamp + pub last_updated: i64, + pub content: String, pub output_file_name: String, } -pub fn generate(mut blog_posts: Vec, template_dir: &String) -> String { +pub fn generate(blog_posts: Vec, template_dir: &String) -> String { + let mut blog_posts = blog_posts; for post in &mut blog_posts { post.content = get_unformatted_text(post.content.clone()); } diff --git a/src/lib.rs b/src/lib.rs index ea00765..71bc29d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ #![feature(str_split_remainder)] - +#![feature(ascii_char)] use chrono::prelude::*; use derive_more::Constructor; use markdown::{to_html_with_options, CompileOptions, Options}; diff --git a/src/main.rs b/src/main.rs index 42336ea..7bf3048 100644 --- a/src/main.rs +++ b/src/main.rs @@ -39,15 +39,33 @@ fn generate() { &file.file_name.clone().into_string().unwrap(), ); + let last_updated: Option<&String> = frontmatter.get("updated_at"); + let updated = { + if last_updated.is_some() { + chrono::DateTime::parse_from_rfc3339(last_updated.unwrap()) + .unwrap() + .timestamp() + } else { + file.date.clone().timestamp() + } + }; + post_index.push(index::BlogPost::new( file.title, file.date.format("%Y-%m-%d").to_string(), file.date.timestamp(), + updated, index_content, format!("{}.html", file.file_name.to_str().unwrap()), )); } - + if config.syndication.is_some() { + if config.syndication.as_ref().unwrap().atom.is_some() { + let atom = syndication::atom::generate(config.syndication.unwrap(), &post_index); + std::fs::write(format!("{}/atom.xml", config.output_dir), atom) + .unwrap_or_else(|_| panic!("Error writing atom feed")); + } + } let index = index::generate(post_index, &config.templates_dir); std::fs::write(format!("{}/index.html", config.output_dir), index) diff --git a/src/syndication/atom.rs b/src/syndication/atom.rs index 0b54153..dcf75e0 100644 --- a/src/syndication/atom.rs +++ b/src/syndication/atom.rs @@ -1,40 +1,80 @@ use xml_builder::{XMLBuilder, XMLElement, XMLVersion}; +use crate::{xml_tag_attribute, xml_tag_text}; + pub fn generate( config: crate::config::Syndication, post_index: &Vec, ) -> String { let mut xml = XMLBuilder::new() - .version(XMLVersion::XML1_1) + .version(XMLVersion::XML1_0) .encoding("UTF-8".into()) .build(); let mut feed = XMLElement::new("feed"); feed.add_attribute("xmlns", "http://www.w3.org/2005/Atom"); - let mut id = XMLElement::new("id"); - id.add_text(config.link).unwrap(); - feed.add_child(id).unwrap(); + xml_tag_text!("id", config.link.clone(), feed); - let mut title = XMLElement::new("title"); - title.add_text(config.title).unwrap(); - feed.add_child(title).unwrap(); + xml_tag_text!("title", config.title, feed); + + xml_tag_attribute!("link", feed, ("href", &config.link)); + + let mut author = XMLElement::new("author"); + let mut name = XMLElement::new("name"); + name.add_text("kibty".to_string()).unwrap(); + author.add_child(name).unwrap(); + feed.add_child(author).unwrap(); - let mut updated = XMLElement::new("updated"); let last_update = chrono::DateTime::from_timestamp(last_update(post_index), 0).unwrap(); - updated.add_text(last_update.to_rfc3339()).unwrap(); - feed.add_child(updated).unwrap(); + xml_tag_text!("updated", last_update.to_rfc3339(), feed); if config.subtitle.is_some() { - let mut subtitle = XMLElement::new("subtitle"); - subtitle.add_text(config.subtitle.unwrap()).unwrap(); - feed.add_child(subtitle).unwrap(); + xml_tag_text!("subtitle", config.subtitle.unwrap(), feed); } if config.icon.is_some() { - let mut icon = XMLElement::new("icon"); - icon.add_text(config.icon.unwrap()).unwrap(); - feed.add_child(icon).unwrap(); + xml_tag_text!("icon", config.icon.unwrap(), feed); + } + + for post in post_index { + let mut entry = XMLElement::new("entry"); + + xml_tag_text!("title", post.title.clone(), entry); + + xml_tag_attribute!( + "link", + entry, + ( + "href", + &format!( + "{}{}/{}", + &config.link, &config.blog_root, &post.output_file_name + ) + ) + ); + + xml_tag_text!( + "id", + format!( + "{}{}/{}", + &config.link, &config.blog_root, &post.output_file_name + ), + entry + ); + let last_update = chrono::DateTime::from_timestamp(post.last_updated, 0) + .unwrap() + .to_rfc3339(); + xml_tag_text!("updated", last_update, entry); + + let mut content = XMLElement::new("content"); + content.add_attribute("type", "html"); + content + .add_text(crate::syndication::escape_html(post.content.clone())) + .unwrap(); + entry.add_child(content).unwrap(); + + feed.add_child(entry).unwrap(); } xml.set_root_element(feed); diff --git a/src/syndication/mod.rs b/src/syndication/mod.rs index 856b895..556112e 100644 --- a/src/syndication/mod.rs +++ b/src/syndication/mod.rs @@ -1 +1,26 @@ pub mod atom; + +#[macro_export] +macro_rules! xml_tag_text { + ($a: expr, $b: expr, $c: expr) => { + let mut tag = XMLElement::new($a); + tag.add_text($b).unwrap(); + $c.add_child(tag).unwrap(); + }; +} + +#[macro_export] +macro_rules! xml_tag_attribute { + ($a: expr, $b: expr, $( $c: expr),+ ) => { + let mut tag = XMLElement::new($a); + $( + tag.add_attribute($c.0,$c.1); + )+ + $b.add_child(tag).unwrap(); + + }; +} + +fn escape_html(unescaped_html: String) -> String { + html_escape::encode_text(&unescaped_html).to_string() +}