basic generic post loading

This commit is contained in:
Cadey Ratio 2020-07-12 21:50:45 -04:00
parent 02031f9062
commit 92c9daeb82
5 changed files with 272 additions and 0 deletions

87
Cargo.lock generated
View File

@ -187,6 +187,17 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "chrono"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c74d84029116787153e02106bf53e66828452a4b325cc8652b788b5967c0a0b6"
dependencies = [
"num-integer",
"num-traits",
"time",
]
[[package]] [[package]]
name = "clap" name = "clap"
version = "2.33.1" version = "2.33.1"
@ -524,6 +535,12 @@ dependencies = [
"wasi", "wasi",
] ]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.2.5" version = "0.2.5"
@ -759,6 +776,12 @@ version = "0.2.72"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701" checksum = "a9f8082297d534141b30c8d39e9b1773713ab50fdbe4ff30f750d063b3bfd701"
[[package]]
name = "linked-hash-map"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a"
[[package]] [[package]]
name = "log" name = "log"
version = "0.3.9" version = "0.3.9"
@ -938,6 +961,25 @@ dependencies = [
"version_check 0.9.2", "version_check 0.9.2",
] ]
[[package]]
name = "num-integer"
version = "0.1.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b"
dependencies = [
"autocfg 1.0.0",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611"
dependencies = [
"autocfg 1.0.0",
]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.13.0" version = "1.13.0"
@ -1604,6 +1646,18 @@ dependencies = [
"url", "url",
] ]
[[package]]
name = "serde_yaml"
version = "0.8.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae3e2dd40a7cdc18ca80db804b7f461a39bb721160a85c9a1fa30134bf3c02a5"
dependencies = [
"dtoa",
"linked-hash-map",
"serde",
"yaml-rust",
]
[[package]] [[package]]
name = "sha-1" name = "sha-1"
version = "0.8.2" version = "0.8.2"
@ -1713,6 +1767,26 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "thiserror"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.0.1" version = "1.0.1"
@ -2184,8 +2258,10 @@ name = "xesite"
version = "2.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"chrono",
"comrak", "comrak",
"envy", "envy",
"glob",
"log 0.4.8", "log 0.4.8",
"mime 0.3.16", "mime 0.3.16",
"pretty_env_logger", "pretty_env_logger",
@ -2193,6 +2269,17 @@ dependencies = [
"ructe", "ructe",
"serde", "serde",
"serde_dhall", "serde_dhall",
"serde_yaml",
"thiserror",
"tokio", "tokio",
"warp", "warp",
] ]
[[package]]
name = "yaml-rust"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39f0c922f1a334134dc2f7a8b67dc5d25f0735263feec974345ff706bcf20b0d"
dependencies = [
"linked-hash-map",
]

View File

@ -9,17 +9,21 @@ build = "src/build.rs"
[dependencies] [dependencies]
anyhow = "1" anyhow = "1"
chrono = "0.4"
comrak = "0.8" comrak = "0.8"
envy = "0.4" envy = "0.4"
glob = "0.3"
log = "0" log = "0"
mime = "0.3.0" mime = "0.3.0"
pretty_env_logger = "0" pretty_env_logger = "0"
rand = "0" rand = "0"
ructe = "0.11" ructe = "0.11"
serde_dhall = "0.5.3" serde_dhall = "0.5.3"
serde_yaml = "0.8"
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
tokio = { version = "0.2", features = ["macros"] } tokio = { version = "0.2", features = ["macros"] }
warp = "0.2" warp = "0.2"
thiserror = "1"
[build-dependencies] [build-dependencies]
ructe = { version = "0.11", features = ["warp02"] } ructe = { version = "0.11", features = ["warp02"] }

View File

@ -4,6 +4,7 @@ use warp::{path, Filter};
pub mod app; pub mod app;
pub mod handlers; pub mod handlers;
pub mod post;
pub mod signalboost; pub mod signalboost;
use app::State; use app::State;

114
src/post/frontmatter.rs Normal file
View File

@ -0,0 +1,114 @@
/// This code was borrowed from @fasterthanlime.
use anyhow::{Result};
use serde::{Serialize, Deserialize};
#[derive(Eq, PartialEq, Deserialize, Default, Debug, Serialize, Clone)]
pub struct Data {
pub title: String,
pub date: String,
pub series: Option<String>,
pub tags: Option<Vec<String>>,
pub slides_link: Option<String>,
pub image: Option<String>,
pub thumb: Option<String>,
pub show: Option<bool>,
}
enum State {
SearchForStart,
ReadingMarker { count: usize, end: bool },
ReadingFrontMatter { buf: String, line_start: bool },
SkipNewline { end: bool },
}
#[derive(Debug, thiserror::Error)]
enum Error {
#[error("EOF while parsing frontmatter")]
EOF,
#[error("Error parsing yaml: {0:?}")]
Yaml(#[from] serde_yaml::Error),
}
impl Data {
pub fn parse(input: &str) -> Result<(Data, usize)> {
let mut state = State::SearchForStart;
let mut payload = None;
let offset;
let mut chars = input.char_indices();
'parse: loop {
let (idx, ch) = match chars.next() {
Some(x) => x,
None => return Err(Error::EOF)?,
};
match &mut state {
State::SearchForStart => match ch {
'-' => {
state = State::ReadingMarker {
count: 1,
end: false,
};
}
'\n' | '\t' | ' ' => {
// ignore whitespace
}
_ => {
panic!("Start of frontmatter not found");
}
},
State::ReadingMarker { count, end } => match ch {
'-' => {
*count += 1;
if *count == 3 {
state = State::SkipNewline { end: *end };
}
}
_ => {
panic!("Malformed frontmatter marker");
}
},
State::SkipNewline { end } => match ch {
'\n' => {
if *end {
offset = idx + 1;
break 'parse;
} else {
state = State::ReadingFrontMatter {
buf: String::new(),
line_start: true,
};
}
}
_ => panic!("Expected newline, got {:?}",),
},
State::ReadingFrontMatter { buf, line_start } => match ch {
'-' if *line_start => {
let mut state_temp = State::ReadingMarker {
count: 1,
end: true,
};
std::mem::swap(&mut state, &mut state_temp);
if let State::ReadingFrontMatter { buf, .. } = state_temp {
payload = Some(buf);
} else {
unreachable!();
}
}
ch => {
buf.push(ch);
*line_start = ch == '\n';
}
},
}
}
// unwrap justification: option set in state machine, Rust can't statically analyze it
let payload = payload.unwrap();
let fm: Self = serde_yaml::from_str(&payload)?;
Ok((fm, offset))
}
}

66
src/post/mod.rs Normal file
View File

@ -0,0 +1,66 @@
use anyhow::{anyhow, Result};
use chrono::prelude::*;
use glob::glob;
use std::{cmp::Ordering, fs};
pub mod frontmatter;
#[derive(Eq, PartialEq, Debug)]
pub struct Post {
pub front_matter: frontmatter::Data,
pub link: String,
pub body: String,
pub body_html: String,
pub date: NaiveDate,
}
impl Ord for Post {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(&other).unwrap()
}
}
impl PartialOrd for Post {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.date.cmp(&other.date))
}
}
pub fn load(dir: &str) -> Result<Vec<Post>> {
let mut result: Vec<Post> = vec![];
for path in glob(&format!("{}/*.markdown", dir))?.filter_map(Result::ok) {
let body = fs::read_to_string(path.clone())?;
let (fm, content_offset) = frontmatter::Data::parse(body.clone().as_str())?;
let markup = &body[content_offset..];
let date = NaiveDate::parse_from_str(&fm.clone().date, "%Y-%m-%d")?;
result.push(Post {
front_matter: fm,
link: format!("{}/{}", dir, path.file_stem().unwrap().to_str().unwrap()),
body: markup.to_string(),
body_html: crate::app::markdown(&markup),
date: date,
})
}
if result.len() == 0 {
Err(anyhow!("no posts loaded"))
} else {
result.sort();
result.reverse();
Ok(result)
}
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Result;
#[test]
fn blog() -> Result<()> {
load("./blog")?;
Ok(())
}
}