Skip to content

Commit

Permalink
Merge pull request #192 from epage/slug
Browse files Browse the repository at this point in the history
feat(attributes): Add input-file based attributes
  • Loading branch information
johannhof committed Mar 19, 2017
2 parents 1a215f0 + 5083b8e commit 46b3b22
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 18 deletions.
16 changes: 16 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Expand Up @@ -32,7 +32,8 @@ glob = "0.2.11"
regex = "0.1"
clippy = {version = "0.0", optional = true}
error-chain = "0.5.0"
lazy_static = {version = "0.2", optional =true}
lazy_static = "0.2"
itertools = "0.5.9"

[dependencies.hyper]
version = "0.9"
Expand All @@ -54,5 +55,5 @@ default = []
unstable = []
dev = []

syntax-highlight = ["syntect", "lazy_static"]
syntax-highlight = ["syntect"]

91 changes: 76 additions & 15 deletions src/document.rs
@@ -1,7 +1,6 @@
use std::fs::File;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use std::default::Default;
use error::Result;
Expand All @@ -10,6 +9,7 @@ use yaml_rust::{Yaml, YamlLoader};
use std::io::Read;
use regex::Regex;
use rss;
use itertools::Itertools;

#[cfg(all(feature="syntax-highlight", not(windows)))]
use syntax_highlight::{initialize_codeblock, decorate_markdown};
Expand All @@ -19,6 +19,14 @@ use liquid::{Renderable, LiquidOptions, Context, Value};
use pulldown_cmark as cmark;
use liquid;

lazy_static!{
static ref DATE_VARIABLES: Regex =
Regex::new(":(year|month|i_month|day|i_day|short_year|hour|minute|second)").unwrap();
static ref SLUG_INVALID_CHARS: Regex = Regex::new(r"([^a-zA-Z0-9]+)").unwrap();
static ref FRONT_MATTER_DIVIDE: Regex = Regex::new(r"---\s*\r?\n").unwrap();
static ref MARKDOWN_REF: Regex = Regex::new(r"(?m:^ {0,3}\[[^\]]+\]:.+$)").unwrap();
}

#[derive(Debug)]
pub struct Document {
pub path: String,
Expand Down Expand Up @@ -59,9 +67,7 @@ fn format_path(p: &str,
-> Result<String> {
let mut p = p.to_owned();

let time_vars = Regex::new(":(year|month|i_month|day|i_day|short_year|hour|minute|second)")
.unwrap();
if time_vars.is_match(&p) {
if DATE_VARIABLES.is_match(&p) {
let date =
try!(date.ok_or(format!("Can not format file path without a valid date ({:?})", p)));

Expand All @@ -83,8 +89,6 @@ fn format_path(p: &str,
}
}

// TODO if title is present inject title slug

let mut path = Path::new(&p);

// remove the root prefix (leading slash on unix systems)
Expand All @@ -104,6 +108,31 @@ fn format_path(p: &str,
Ok(path_buf.to_string_lossy().into_owned())
}

/// The base-name without an extension. Correlates to Jekyll's :name path tag
fn file_stem(p: &Path) -> String {
p.file_stem().map(|os| os.to_string_lossy().into_owned()).unwrap_or_else(|| "".to_owned())
}

/// Create a slug for a given file. Correlates to Jekyll's :slug path tag
fn slugify(name: &str) -> String {
let slug = SLUG_INVALID_CHARS.replace_all(name, "-");
slug.trim_matches('-').to_lowercase()
}

/// Title-case a single word
fn title_case(s: &str) -> String {
let mut c = s.chars();
match c.next() {
None => String::new(),
Some(f) => f.to_uppercase().chain(c.flat_map(|t| t.to_lowercase())).collect(),
}
}

/// Format a user-visible title out of a slug. Correlates to Jekyll's "title" attribute
fn titleize_slug(slug: &str) -> String {
slug.split('-').map(title_case).join(" ")
}

impl Document {
pub fn new(path: String,
attributes: HashMap<String, Value>,
Expand Down Expand Up @@ -137,9 +166,8 @@ impl Document {
let content = try!(read_file(file_path));

// if there is front matter, split the file and parse it
let splitter = Regex::new(r"---\s*\r?\n").unwrap();
let content = if splitter.is_match(&content) {
let mut splits = splitter.splitn(&content, 2);
let content = if FRONT_MATTER_DIVIDE.is_match(&content) {
let mut splits = FRONT_MATTER_DIVIDE.splitn(&content, 2);

// above the split are the attributes
let attribute_split = splits.next().unwrap_or("");
Expand Down Expand Up @@ -181,9 +209,24 @@ impl Document {
.and_then(|d| d.as_str())
.and_then(|d| DateTime::parse_from_str(d, "%d %B %Y %H:%M:%S %z").ok());

// if the file has a .md extension we assume it's markdown
// TODO add a "markdown" flag to yaml front matter
let markdown = file_path.extension().unwrap_or_else(|| OsStr::new("")) == OsStr::new("md");
let file_stem = file_stem(new_path);
let slug = slugify(&file_stem);
attributes.entry("title".to_owned())
.or_insert_with(|| Value::Str(titleize_slug(slug.as_str())));
attributes.entry("slug".to_owned())
.or_insert_with(|| Value::Str(slug));

let mut markdown = false;
if let Value::Str(ref ext) =
*attributes.entry("ext".to_owned())
.or_insert_with(|| {
Value::Str(new_path.extension()
.and_then(|os| os.to_str())
.unwrap_or("")
.to_owned())
}) {
markdown = ext == "md";
}

let layout = attributes.get("extends").and_then(|l| l.as_str()).map(|x| x.to_owned());

Expand Down Expand Up @@ -300,10 +343,9 @@ impl Document {
/// Extracts references iff markdown content.
pub fn extract_markdown_references(&self, excerpt_separator: &str) -> String {
let mut trail = String::new();
let re = Regex::new(r"(?m:^ {0,3}\[[^\]]+\]:.+$)").unwrap();

if self.markdown && re.is_match(&self.content) {
for mat in re.find_iter(&self.content) {
if self.markdown && MARKDOWN_REF.is_match(&self.content) {
for mat in MARKDOWN_REF.find_iter(&self.content) {
trail.push_str(&self.content[mat.0..mat.1]);
trail.push('\n');
}
Expand Down Expand Up @@ -388,3 +430,22 @@ impl Document {
}
}
}

#[test]
fn test_file_stem() {
let input = PathBuf::from("/embedded/path/___filE-worlD-__09___.md");
let actual = file_stem(input.as_path());
assert_eq!(actual, "___filE-worlD-__09___");
}

#[test]
fn test_slugify() {
let actual = slugify("___filE-worlD-__09___");
assert_eq!(actual, "file-world-09");
}

#[test]
fn test_titleize_slug() {
let actual = titleize_slug("tItLeIzE-sLuG");
assert_eq!(actual, "Titleize Slug");
}
3 changes: 2 additions & 1 deletion src/lib.rs
Expand Up @@ -18,6 +18,8 @@ extern crate rss;
extern crate glob;
extern crate regex;

extern crate itertools;

#[cfg(all(feature="syntax-highlight", not(windows)))]
extern crate syntect;

Expand All @@ -42,6 +44,5 @@ mod new;
#[cfg(feature="syntax-highlight")]
mod syntax_highlight;

#[cfg(feature="syntax-highlight")]
#[macro_use]
extern crate lazy_static;

0 comments on commit 46b3b22

Please sign in to comment.