commit
d61d408c68
|
@ -22,15 +22,16 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.41"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "15af2628f6890fe2609a3b91bef4c83450512802e59489f9c1cb1fa5df064a61"
|
||||
checksum = "595d3cfa7a60d4555cb5067b99f07142a08ea778de5cf993f7b75c7d8fabc486"
|
||||
|
||||
[[package]]
|
||||
name = "arse"
|
||||
version = "0.10.1"
|
||||
version = "0.11.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"clap",
|
||||
"glob",
|
||||
"hyper",
|
||||
|
@ -38,6 +39,7 @@ dependencies = [
|
|||
"pulldown-cmark",
|
||||
"rand",
|
||||
"routerify",
|
||||
"rss",
|
||||
"serde",
|
||||
"simplelog",
|
||||
"tempfile",
|
||||
|
@ -46,6 +48,18 @@ dependencies = [
|
|||
"toml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atom_syndication"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d5016bf52ff4f3ed28bf3ec1fed96b53daf4b137d5e6b9f97a8cfae7b57a3a2"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"derive_builder",
|
||||
"diligent-date-parser",
|
||||
"quick-xml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
|
@ -155,7 +169,7 @@ dependencies = [
|
|||
"ansi_term",
|
||||
"atty",
|
||||
"bitflags",
|
||||
"strsim",
|
||||
"strsim 0.8.0",
|
||||
"textwrap",
|
||||
"unicode-width",
|
||||
"vec_map",
|
||||
|
@ -171,6 +185,66 @@ dependencies = [
|
|||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d706e75d87e35569db781a9b5e2416cff1236a47ed380831f959382ccd5f858"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c960ae2da4de88a91b2d920c2a7233b400bc33cb28453a2987822d8392519b"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim 0.9.3",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9b5a2f4ac4969822c62224815d069952656cadc7084fdca9751e6d959189b72"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2658621297f2cf68762a6f7dc0bb7e1ff2cfd6583daef8ee0fed6f7ec468ec0"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"derive_builder_core",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2791ea3e372c8495c0bc2033991d76b512cd799d07491fbd6890124db9458bef"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deunicode"
|
||||
version = "0.4.3"
|
||||
|
@ -186,6 +260,24 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "diligent-date-parser"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e37ea528f01b8bfca1f71bcd06a8e6c898bf8fdfbf24dd9dbc7fb49338ed6d84"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding_rs"
|
||||
version = "0.8.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fake-simd"
|
||||
version = "0.1.2"
|
||||
|
@ -364,9 +456,9 @@ checksum = "02296996cb8796d7c6e3bc2d9211b7802812d36999a51bb754123ead7d37d026"
|
|||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.9"
|
||||
version = "0.14.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07d6baa1b441335f3ce5098ac421fb6547c46dda735ca1bc6d0153c838f9dd83"
|
||||
checksum = "7728a72c4c7d72665fde02204bcbd93b247721025b222ef78606f14513e0fd03"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
|
@ -386,6 +478,12 @@ dependencies = [
|
|||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "ignore"
|
||||
version = "0.4.18"
|
||||
|
@ -437,9 +535,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.97"
|
||||
version = "0.2.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12b8adadd720df158f4d70dfe7ccc6adb0472d7c55ca83445f6a5ab3e36f8fb6"
|
||||
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
|
@ -664,6 +762,16 @@ dependencies = [
|
|||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-xml"
|
||||
version = "0.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26aab6b48e2590e4a64d1ed808749ba06257882b461d01ca71baeb747074a6dd"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.9"
|
||||
|
@ -761,6 +869,17 @@ dependencies = [
|
|||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rss"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02e70d6ae72f8a4333af8ce9dce58942020528430eb0d46ee2fcb5e8d4d16377"
|
||||
dependencies = [
|
||||
"atom_syndication",
|
||||
"derive_builder",
|
||||
"quick-xml",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.5"
|
||||
|
@ -882,6 +1001,12 @@ version = "0.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.73"
|
||||
|
@ -968,9 +1093,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "570c2eb13b3ab38208130eccd41be92520388791207fde783bda7c1e8ace28d4"
|
||||
checksum = "98c8b05dc14c75ea83d63dd391100353789f5f24b8b3866542a5e85c8be8e985"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
|
@ -988,9 +1113,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.2.0"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c49e3df43841dafb86046472506755d8501c5615673955f6aa17181125d13c37"
|
||||
checksum = "54473be61f4ebe4efd09cec9bd5d16fa51d70ea0192213d754d2d500457db110"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "arse"
|
||||
version = "0.10.1"
|
||||
version = "0.11.0"
|
||||
authors = ["Anthony Martinez"]
|
||||
edition = "2018"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
@ -15,6 +15,7 @@ categories = ["command-line-utilities", "web-programming::http-server"]
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
chrono = "0.4"
|
||||
clap = "2.33.3"
|
||||
glob = "0.3.0"
|
||||
hyper = "0.14"
|
||||
|
@ -22,6 +23,7 @@ log = "0.4"
|
|||
pulldown-cmark = { version = "0.8", default-features = false, features = ["simd"] }
|
||||
rand = "0.8"
|
||||
routerify = "2"
|
||||
rss = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
simplelog = "0.10.0"
|
||||
tera = "1"
|
||||
|
|
10
README.md
10
README.md
|
@ -7,6 +7,7 @@ and flexible base for serving sites using:
|
|||
* [pulldown-cmark](https://crates.io/crates/pulldown-cmark) for CommonMark rendering
|
||||
* [routerify](https://crates.io/crates/routerify) to serve the site
|
||||
* [simplecss](https://simplecss.org) for default styling
|
||||
* [rss](https://crates.io/crates/rss) for generating a full-site RSS feed
|
||||
|
||||
## Usage
|
||||
|
||||
|
@ -22,6 +23,8 @@ Please enter a name for the site:
|
|||
Example Site
|
||||
Please enter the site author's name:
|
||||
Arthur Writeson
|
||||
Please enter the base URL for your site:
|
||||
https://www.example.com
|
||||
Please enter comma-separated site topics:
|
||||
one, two, three
|
||||
2021-05-01T17:34:26.501980660+00:00 [INFO] Creating site filesystem tree
|
||||
|
@ -36,6 +39,7 @@ An example config, as generated, is shown below:
|
|||
[site]
|
||||
name = 'Example Site'
|
||||
author = 'Arthur Writeson'
|
||||
url = 'https://www.example.com'
|
||||
template = 'default.tmpl'
|
||||
topics = [
|
||||
'one',
|
||||
|
@ -76,7 +80,9 @@ The following elements are available within the Tera context for rendering:
|
|||
* Items in `[docpaths]` are generated as full paths for completeness, however relative paths will work if desired
|
||||
* From the example above the user is free to simply use `site/templates` and `site/webroot` and move the directory out of `/home/user`
|
||||
* Note that `arse new` creates the site tree, and all other output files, in the current working directory.
|
||||
* If `gallery` is one of the topics requested, a simple image slideshow will be generated for `/gallery/ext/*.jpg` with the same lexical reverse order as posts.
|
||||
* If `gallery` is one of the topics requested
|
||||
* A simple image slideshow will be generated for `/gallery/ext/*.jpg`
|
||||
* Display will follow the same lexical reverse order as posts.
|
||||
|
||||
## Path to 1.0
|
||||
|
||||
|
@ -87,7 +93,7 @@ The following elements are available within the Tera context for rendering:
|
|||
- [x] Support custom bind address and port
|
||||
- [x] Support favicons
|
||||
- [x] Support a special `gallery` topic
|
||||
- [ ] Support RSS feeds
|
||||
- [x] Support RSS feeds
|
||||
|
||||
### License
|
||||
|
||||
|
|
|
@ -131,6 +131,7 @@ fn csv_to_vec(csv: &str) -> Vec<String> {
|
|||
pub(crate) struct Site {
|
||||
pub name: String,
|
||||
pub author: String,
|
||||
pub url: String,
|
||||
pub template: String,
|
||||
pub topics: Vec<String>,
|
||||
}
|
||||
|
@ -140,10 +141,11 @@ impl Site {
|
|||
pub(crate) fn new_from_input<R: BufRead>(reader: &mut R) -> Result<Site> {
|
||||
let name = get_input("Please enter a name for the site: ", reader)?;
|
||||
let author = get_input("Please enter the site author's name: ", reader)?;
|
||||
let url = get_input("Please enter the base URL for your site: ", reader)?;
|
||||
let topics = get_input("Please enter comma-separated site topics: ", reader)?;
|
||||
let topics = csv_to_vec(&topics);
|
||||
let template = "default.tmpl".to_owned();
|
||||
let site = Site { name, author, template, topics };
|
||||
let site = Site { name, author, url, template, topics };
|
||||
|
||||
trace!("Site: {:?}", site);
|
||||
Ok(site)
|
||||
|
@ -275,7 +277,7 @@ mod tests {
|
|||
fn build_config_from_input() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
// Setup all target fields
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nOne, Two, Three, And More\n";
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nhttps://my.example.site\nOne, Two, Three, And More\n";
|
||||
let config = AppConfig::generate(&dir, &mut src);
|
||||
assert!(config.is_ok());
|
||||
|
||||
|
|
116
src/render.rs
116
src/render.rs
|
@ -10,6 +10,7 @@ copied, modified, or distributed except according to those terms.
|
|||
|
||||
//! Provides the rendering engine for topics and posts using [`AppConfig`], [`Tera`], and [`pulldown_cmark`].
|
||||
|
||||
use std::fs::File;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -17,10 +18,11 @@ use super::config::AppConfig;
|
|||
use super::common;
|
||||
use super::{Context, Result};
|
||||
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::{debug, trace};
|
||||
use pulldown_cmark::{Parser, html};
|
||||
use tera::Tera;
|
||||
use tera::Context as TemplateContext;
|
||||
use rss::{Channel, Item};
|
||||
use tera::{Tera, Context as TemplateContext};
|
||||
|
||||
/// Static defaults for the rendering engine.
|
||||
mod default;
|
||||
|
@ -150,7 +152,6 @@ impl Engine {
|
|||
Self::read_post_to_html(post_path)
|
||||
}
|
||||
|
||||
|
||||
fn read_post_to_html<P: AsRef<Path>>(path: P) -> Result<String> {
|
||||
debug!("Rendering Post Markdown to HTML");
|
||||
trace!("Rendering {} to HTML", &path.as_ref().display());
|
||||
|
@ -162,7 +163,64 @@ impl Engine {
|
|||
|
||||
Ok(html_output)
|
||||
}
|
||||
|
||||
/// Renders `/rss.xml` for all topics
|
||||
pub(crate) fn rss(&self) -> Result<String> {
|
||||
debug!("Rendering RSS Feed");
|
||||
let site = &self.app.site;
|
||||
let items = Self::rss_items(&self)?;
|
||||
let mut channel = Channel::default();
|
||||
channel.set_title(&site.name);
|
||||
channel.set_link(&site.url);
|
||||
channel.set_description(format!("{} RSS Feed", &site.name));
|
||||
channel.set_items(items);
|
||||
|
||||
Ok(channel.to_string())
|
||||
}
|
||||
|
||||
fn rss_items(&self) -> Result<Vec<Item>> {
|
||||
debug!("Building RSS Items");
|
||||
let mut items: Vec<Item> = Vec::new();
|
||||
items.append(&mut Self::topic_to_item(&self, "main")?);
|
||||
|
||||
for topic in &self.app.site.topics {
|
||||
let mut topic_items = Self::topic_to_item(&self, &common::slugify(&topic))?;
|
||||
items.append(&mut topic_items);
|
||||
}
|
||||
|
||||
Ok(items)
|
||||
}
|
||||
|
||||
fn topic_to_item(&self, topic_slug: &str) -> Result<Vec<Item>> {
|
||||
trace!("Generating RSS Items for topic: {}", &topic_slug);
|
||||
let mut items: Vec<Item> = Vec::new();
|
||||
let topic_path = Path::new(&self.app.docpaths.webroot).join(topic_slug).join("posts");
|
||||
let pat = format!("{}/*.md", topic_path.display());
|
||||
let paths = common::path_matches(&pat)?;
|
||||
for path in paths {
|
||||
trace!("Generating RSS Item for post at: {}", path.display());
|
||||
let link = format!("{}/{}/{}",
|
||||
&self.app.site.url,
|
||||
path.strip_prefix(&self.app.docpaths.webroot)?.parent().unwrap().to_str().unwrap(),
|
||||
path.file_stem().unwrap().to_str().unwrap());
|
||||
let f = File::open(&path)?;
|
||||
let updated = f.metadata()?.modified()?;
|
||||
let updated: DateTime<Utc> = updated.into();
|
||||
let updated = updated.to_rfc2822();
|
||||
|
||||
let description = Self::read_post_to_html(path)?;
|
||||
|
||||
let mut item = Item::default();
|
||||
item.set_link(link);
|
||||
item.set_pub_date(updated);
|
||||
item.set_description(description.to_owned());
|
||||
items.push(item);
|
||||
}
|
||||
|
||||
Ok(items)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -172,7 +230,7 @@ mod tests {
|
|||
#[test]
|
||||
fn check_default_template() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nOne, Two, Three, And More\nadmin\n";
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nhttps://special.example.site\nOne, Gallery\nadmin\n";
|
||||
let config = AppConfig::generate(&dir, &mut src).unwrap();
|
||||
let config = Arc::new(config);
|
||||
let tera = Engine::load_template(config);
|
||||
|
@ -182,7 +240,7 @@ mod tests {
|
|||
#[test]
|
||||
fn check_render_post() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nOne, Two, Three, And More\nadmin\n";
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nhttps://special.example.site\nOne, Gallery\nadmin\n";
|
||||
let config = AppConfig::generate(&dir, &mut src).unwrap();
|
||||
let config = Arc::new(config);
|
||||
let engine = Engine::new(config);
|
||||
|
@ -214,7 +272,7 @@ Super Wow!
|
|||
#[test]
|
||||
fn check_render_topic() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nOne, Two, Three, And More\nadmin\n";
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nhttps://special.example.site\nOne, Gallery\nadmin\n";
|
||||
let config = AppConfig::generate(&dir, &mut src).unwrap();
|
||||
let config = Arc::new(config);
|
||||
let engine = Engine::new(config);
|
||||
|
@ -245,7 +303,7 @@ Super Wow!
|
|||
#[test]
|
||||
fn check_render_empty_topic() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nOne, Two, Three, And More\nadmin\n";
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nhttps://special.example.site\nOne, Gallery\nadmin\n";
|
||||
let config = AppConfig::generate(&dir, &mut src).unwrap();
|
||||
let config = Arc::new(config);
|
||||
let engine = Engine::new(config);
|
||||
|
@ -258,7 +316,7 @@ Super Wow!
|
|||
#[test]
|
||||
fn check_render_gallery_topic() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nOne, Gallery\nadmin\n";
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nhttps://special.example.site\nOne, Gallery\nadmin\n";
|
||||
let config = AppConfig::generate(&dir, &mut src).unwrap();
|
||||
let config = Arc::new(config);
|
||||
let engine = Engine::new(config);
|
||||
|
@ -279,7 +337,7 @@ Super Wow!
|
|||
#[test]
|
||||
fn check_render_empty_gallery() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nOne, Gallery\nadmin\n";
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nhttps://special.example.site\nOne, Gallery\nadmin\n";
|
||||
let config = AppConfig::generate(&dir, &mut src).unwrap();
|
||||
let config = Arc::new(config);
|
||||
let engine = Engine::new(config);
|
||||
|
@ -288,4 +346,44 @@ Super Wow!
|
|||
|
||||
assert!(page.contains("Coming Soon"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_render_rss() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nhttps://special.example.site\nOne, Gallery\nadmin\n";
|
||||
let config = AppConfig::generate(&dir, &mut src).unwrap();
|
||||
let config = Arc::new(config);
|
||||
let engine = Engine::new(config);
|
||||
|
||||
let main_post = r#"
|
||||
### The Main Page
|
||||
|
||||
This is just a main topic page.
|
||||
"#;
|
||||
let one_post1 = r#"
|
||||
### A first post in One
|
||||
|
||||
Super Wow!
|
||||
"#;
|
||||
let one_post2 = r#"
|
||||
### [A second post in One](/one/posts/2)
|
||||
|
||||
Super Wow TWICE!
|
||||
"#;
|
||||
|
||||
let mut f = File::create(&dir.path().join("site/webroot/main/posts/index.md")).unwrap();
|
||||
f.write_all(&main_post.as_bytes()).unwrap();
|
||||
|
||||
let mut f = File::create(&dir.path().join("site/webroot/one/posts/1.md")).unwrap();
|
||||
f.write_all(&one_post1.as_bytes()).unwrap();
|
||||
|
||||
let mut f = File::create(&dir.path().join("site/webroot/one/posts/2.md")).unwrap();
|
||||
f.write_all(&one_post2.as_bytes()).unwrap();
|
||||
|
||||
let rss = engine.rss().unwrap();
|
||||
|
||||
assert!(rss.contains("The Main Page"));
|
||||
assert!(rss.contains("Super Wow!"));
|
||||
assert!(rss.contains("A second post in One"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ pub(crate) const TEMPLATE: &str = r#"
|
|||
{%- for topic in site.topics %}
|
||||
<a href="/{{ topic | slugify }}">{{ topic }}</a>
|
||||
{%- endfor -%}
|
||||
<a href="/rss.xml">RSS</a>
|
||||
</nav>
|
||||
</center>
|
||||
</header>
|
||||
|
|
|
@ -30,6 +30,7 @@ pub(crate) fn router(engine: Arc<Engine>) -> Router<Body, Error> {
|
|||
.data(engine)
|
||||
.get("/static/:fname", static_assets)
|
||||
.get("/favicon.ico", favicon)
|
||||
.get("/rss.xml", rss_handler)
|
||||
.get("/:topic", topic_handler)
|
||||
.get("/:topic/ext/:fname", topic_assets)
|
||||
.get("/:topic/posts/:post", post_handler)
|
||||
|
@ -56,6 +57,14 @@ async fn index_handler(req: Request<Body>) -> Result<Response<Body>> {
|
|||
topic_posts(engine.clone(), "main".to_owned()).await
|
||||
}
|
||||
|
||||
/// Handler for "/rss"
|
||||
async fn rss_handler(req: Request<Body>) -> Result<Response<Body>> {
|
||||
let engine = req.data::<Arc<Engine>>().unwrap();
|
||||
info!("Handling request to '/rss.xml'");
|
||||
let rss = engine.rss()?;
|
||||
Ok(Response::new(Body::from(rss)))
|
||||
}
|
||||
|
||||
/// Handler for "/:topic"
|
||||
async fn topic_handler(req: Request<Body>) -> Result<Response<Body>> {
|
||||
let engine = req.data::<Arc<Engine>>().unwrap();
|
||||
|
@ -137,7 +146,7 @@ mod tests {
|
|||
#[tokio::test]
|
||||
async fn check_all_handlers() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nOne, Two, Three, And More\nadmin\n";
|
||||
let mut src: &[u8] = b"Site Name\nAuthor Name\nhttps://some.special.site\nOne, Two, Three, And More\nadmin\n";
|
||||
let app = AppConfig::generate(&dir, &mut src).unwrap();
|
||||
let engine = Engine::new(Arc::new(app));
|
||||
let engine = Arc::new(engine);
|
||||
|
@ -229,6 +238,12 @@ One Important Test
|
|||
.body(Body::default())
|
||||
.unwrap();
|
||||
|
||||
let rss_request = Request::builder()
|
||||
.method("GET")
|
||||
.uri("http://localhost:9090/rss.xml")
|
||||
.body(Body::default())
|
||||
.unwrap();
|
||||
|
||||
let service = RouterService::new(router).unwrap();
|
||||
let addr = format!("{}:{}", engine.app.server.bind, engine.app.server.port);
|
||||
let addr: SocketAddr = addr.parse().unwrap();
|
||||
|
@ -256,12 +271,14 @@ One Important Test
|
|||
let topic_asset_resp = client.request(topic_asset_request).await.unwrap();
|
||||
let static_asset_resp = client.request(static_asset_request).await.unwrap();
|
||||
let favicon_resp = client.request(favicon_request).await.unwrap();
|
||||
let rss_resp = client.request(rss_request).await.unwrap();
|
||||
assert_eq!(index_resp.status(), StatusCode::OK);
|
||||
assert_eq!(post_resp.status(), StatusCode::OK);
|
||||
assert_eq!(topic_resp.status(), StatusCode::OK);
|
||||
assert_eq!(topic_asset_resp.status(), StatusCode::OK);
|
||||
assert_eq!(static_asset_resp.status(), StatusCode::OK);
|
||||
assert_eq!(favicon_resp.status(), StatusCode::OK);
|
||||
assert_eq!(rss_resp.status(), StatusCode::OK);
|
||||
|
||||
let bad_topic_resp = client.request(bad_topic_request).await.unwrap();
|
||||
let bad_post_resp = client.request(bad_post_request).await.unwrap();
|
||||
|
@ -305,6 +322,12 @@ One Important Test
|
|||
.body(Body::default())
|
||||
.unwrap();
|
||||
|
||||
let rss_request = Request::builder()
|
||||
.method("GET")
|
||||
.uri("http://localhost:8901/rss.xml")
|
||||
.body(Body::default())
|
||||
.unwrap();
|
||||
|
||||
let service = RouterService::new(router).unwrap();
|
||||
let addr = format!("{}:{}", engine.app.server.bind, engine.app.server.port);
|
||||
let addr: SocketAddr = addr.parse().unwrap();
|
||||
|
@ -330,10 +353,12 @@ One Important Test
|
|||
let post_resp = client.request(post_request).await.unwrap();
|
||||
let topic_resp = client.request(topic_request).await.unwrap();
|
||||
let gallery_resp = client.request(gallery_request).await.unwrap();
|
||||
let rss_resp = client.request(rss_request).await.unwrap();
|
||||
assert_eq!(index_resp.status(), StatusCode::OK);
|
||||
assert_eq!(post_resp.status(), StatusCode::OK);
|
||||
assert_eq!(topic_resp.status(), StatusCode::OK);
|
||||
assert_eq!(gallery_resp.status(), StatusCode::OK);
|
||||
assert_eq!(rss_resp.status(), StatusCode::OK);
|
||||
let _ = tx.send(());
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ function change_img(dir) {
|
|||
</main>
|
||||
<footer>
|
||||
<p>© {{ site.author }}</p>
|
||||
<p><a href="/rss.xml">RSS</a></p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
[site]
|
||||
name = "My Awesome Blog!"
|
||||
author = "Neo"
|
||||
url = "https://something.cool.here"
|
||||
template = "special.tmpl"
|
||||
topics = ["one", "two", "three", "gallery"]
|
||||
|
||||
|
|
Loading…
Reference in New Issue