Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Atom syndication #26

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 113 additions & 80 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ version = "0.6.0"

[dependencies]
badlog = "1.0"
chrono = "0.4"
chrono = { version = "0.4", features = ["serde"] }
clap = "2.26"
git2 = "0.6.8"
log = "0.3"
Expand Down
59 changes: 53 additions & 6 deletions src/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::path::Path;
use std::sync::mpsc::channel;
use tenjin::Tenjin;
use toml;
use metadata;
use util::{InjectDate, cd2root, load_config, build_tenjin, cpr};

const DEFAULT_RECENTS: usize = 5;
Expand All @@ -22,6 +23,7 @@ struct Post {
content: Html,
title: String,
date: DateTime<Local>,
updated_date: DateTime<Local>,
slug: String,
}

Expand Down Expand Up @@ -54,6 +56,10 @@ context! {
date: self.post.date,
format: self.date_format,
},
updated_date => InjectDate {
date: self.post.updated_date,
format: self.date_format,
},
slug => self.post.slug.as_str(),
}

Expand All @@ -80,19 +86,29 @@ impl Post {
let mut src = String::new();
File::open(&path)?.read_to_string(&mut src)?;

let (md, src) = metadata::parse_metadata(&src);

let mut content = String::new();
let parser = Parser::new_ext(&src, Options::all());
pulldown_cmark::html::push_html(&mut content, parser);

let title = path.file_stem()
.unwrap()
.to_string_lossy()
.into();
let title = md.get_string("title").unwrap_or(
path.file_stem()
.unwrap()
.to_string_lossy()
.into()
);

let updated_date = md.get::<DateTime<Local>>("updated_date").unwrap_or(
path.metadata()?.modified()?.into()
);

let date = path.metadata()?.modified()?.into();
let date = md.get::<DateTime<Local>>("date").unwrap_or(
path.metadata()?.modified()?.into()
);
let slug = slug::slugify(&title);

Ok(Post { content, title, date, slug })
Ok(Post { content, title, date, updated_date, slug })
}

fn wrap<'a>(&'a self, date_format: &'a str) -> PostWrap<'a> {
Expand Down Expand Up @@ -196,6 +212,12 @@ fn build() -> Result<()> {
error!("Failed to build `archive.html`: {}.", e);
}

// Build `feed.xml`.

if let Err(e) = build_atom_feed(&config, &tenjin, &posts) {
error!("Failed to build `feed.xml`: {}.", e);
}

// Build posts.

if let Err(e) = build_posts(&config, date_format, &tenjin, &posts) {
Expand Down Expand Up @@ -279,6 +301,31 @@ fn build_archive(
Ok(())
}

fn build_atom_feed(
config: &toml::Value,
tenjin: &Tenjin,
posts: &[Post]
) -> Result<()> {

// date_format needs to be rfc3339 for atom
let ctx = ListContext {
config,
date_format:"%FT%T%:z",
posts,
};

let template = match tenjin.get("feed") {
Some(template) => template,
None => return Err(Error::TemplateNotFound("feed".into())),
};

let mut file = File::create("out/feed.xml")?;

tenjin.render(template, &ctx, &mut file)?;

Ok(())
}

fn build_index(
config: &toml::Value,
date_format: &str,
Expand Down
28 changes: 28 additions & 0 deletions src/init/theme/templates/feed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>{ config.name }</title>
<id>urn:uuid:{ config.uuid }</id>
<updated>{ config.date }</updated>
<link href="{ config.feed_url }" rel="self"/>
<link href="{ config.home_url }" rel="alternate"/>
{ for post in posts }
<entry>
<title>{ post.title }</title>
<id>{ config.base_url }{ post.slug }.html</id>
{ if post.updated_date }
<updated>{ post.updated_date }</updated>
{ end }
<author>
<name>{ config.author }</name>
<email>{ config.email }</email>
<uri>{ config.home_url }</uri>
</author>
<published>{ post.date }</published>
<content type="xhtml">
<div xmlns="http://www.w3.org/1999/xhtml">
{ post.content }
</div>
</content>
</entry>
{ end }
</feed>
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod error;
mod init;
mod theme;
mod util;
mod metadata;


fn main() {
Expand Down
59 changes: 59 additions & 0 deletions src/metadata.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
use std::str::FromStr;
use std::fmt::Debug;

use toml;

#[derive(Default)]
pub struct Metadata(toml::value::Table);

pub fn parse_metadata(src:&str) -> (Metadata, &str) {
let mut md = Metadata::default();
if !src.starts_with("+++") {
return (md, src)
}
let v:Vec<&str> = src.splitn(3, "+++").collect();
if v.len() != 3 {
return (md, src)
}
let md_str = v[1];
match md_str.parse::<toml::Value>() {
Ok(v) => {
match v {
toml::Value::Table(t) => {
md = Metadata(t)
}
x => error!("Unexpected Toml::Value: {:?}", x),
}
}
Err(e) => error!("Error parsing metadata: {:?}", e),
}
return (md, v[2])
}

impl Metadata {
pub fn get_string(&self, name:&str) -> Option<String> {
match self.0.get(name) {
None => None,
Some(v) => match *v {
toml::Value::String(ref s) => Some(s.clone()),
_ => None,
}
}
}

pub fn get<T>(&self, name:&str) -> Option<T>
where T:FromStr, <T as FromStr>::Err: Debug
{
if let Some(s) = self.get_string(name) {
match s.parse::<T>() {
Ok(t) => Some(t),
Err(e) => {
error!("Error parsing {}: {:?}", s, e);
None
}
}
} else {
None
}
}
}
14 changes: 12 additions & 2 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,18 @@ impl<'a, W: Write> Context<W> for InjectDate<'a> {
pub fn load_config() -> Result<toml::Value> {
let mut config = String::new();
File::open("Leven.toml")?
.read_to_string(&mut config)?;
Ok(toml::Value::from_str(&config)?)
.read_to_string(&mut config)?;
let mut config = toml::Value::from_str(&config)?;
if let Some(table) = config.as_table_mut() {
match table.get("date") {
None => {
let s = Local::now().to_rfc3339();
table.insert("date".into(), toml::Value::String(s));
},
_ => (),
}
}
Ok(config)
}

pub fn build_tenjin() -> Result<Tenjin> {
Expand Down