feat: Add Post struct

This commit is contained in:
Nathan McCarty 2023-03-16 16:06:39 -04:00
parent 013c5c211b
commit 76fe128587
Signed by: thatonelutenist
GPG Key ID: D70DA3DD4D1E9F96
4 changed files with 376 additions and 1 deletions

230
Cargo.lock generated
View File

@ -48,6 +48,15 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]]
name = "annotate-snippets"
version = "0.9.1"
@ -99,6 +108,12 @@ dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "cc"
version = "1.0.79"
@ -111,6 +126,22 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e3c5919066adf22df73762e50cffcde3a758f2a848b113b586d1f86728b673b"
dependencies = [
"iana-time-zone",
"js-sys",
"num-integer",
"num-traits",
"serde",
"time",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "clap"
version = "4.1.8"
@ -148,6 +179,16 @@ dependencies = [
"os_str_bytes",
]
[[package]]
name = "codespan-reporting"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
dependencies = [
"termcolor",
"unicode-width",
]
[[package]]
name = "color-eyre"
version = "0.6.2"
@ -175,6 +216,12 @@ dependencies = [
"tracing-error",
]
[[package]]
name = "core-foundation-sys"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"
[[package]]
name = "cpufeatures"
version = "0.2.5"
@ -200,6 +247,50 @@ dependencies = [
"typenum",
]
[[package]]
name = "cxx"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a140f260e6f3f79013b8bfc65e7ce630c9ab4388c6a89c71e07226f49487b72"
dependencies = [
"cc",
"cxxbridge-flags",
"cxxbridge-macro",
"link-cplusplus",
]
[[package]]
name = "cxx-build"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da6383f459341ea689374bf0a42979739dc421874f112ff26f829b8040b8e613"
dependencies = [
"cc",
"codespan-reporting",
"once_cell",
"proc-macro2",
"quote",
"scratch",
"syn",
]
[[package]]
name = "cxxbridge-flags"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90201c1a650e95ccff1c8c0bb5a343213bdd317c6e600a93075bca2eff54ec97"
[[package]]
name = "cxxbridge-macro"
version = "1.0.92"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b75aed41bb2e6367cae39e6326ef817a851db13c13e4f3263714ca3cfb8de56"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "dhall"
version = "0.12.0"
@ -371,6 +462,30 @@ dependencies = [
"winapi",
]
[[package]]
name = "iana-time-zone"
version = "0.1.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"winapi",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca"
dependencies = [
"cxx",
"cxx-build",
]
[[package]]
name = "idna"
version = "0.3.0"
@ -434,6 +549,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791cdfc00394bec07a92cf16f4b54b653a62307d17f42ddb671b0401f8f84281"
[[package]]
name = "js-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -446,6 +570,15 @@ version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "link-cplusplus"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5"
dependencies = [
"cc",
]
[[package]]
name = "linux-raw-sys"
version = "0.1.4"
@ -523,6 +656,25 @@ dependencies = [
"winapi",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "object"
version = "0.30.3"
@ -708,6 +860,12 @@ dependencies = [
"windows-sys",
]
[[package]]
name = "scratch"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1792db035ce95be60c3f8853017b3999209281c24e2ba5bc8e59bf97a0c590c1"
[[package]]
name = "serde"
version = "1.0.156"
@ -799,6 +957,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
name = "stranger-site-gen"
version = "0.0.1-dev"
dependencies = [
"chrono",
"clap",
"color-eyre",
"jotdown",
@ -865,6 +1024,17 @@ dependencies = [
"once_cell",
]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi",
"winapi",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
@ -1022,6 +1192,66 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "winapi"
version = "0.3.9"

View File

@ -8,6 +8,7 @@ readme = "README.md"
license = "MIT"
[dependencies]
chrono = { version = "0.4.24", features = ["serde"] }
clap = { version = "4.1.8", features = ["derive", "env"] }
color-eyre = "0.6.2"
jotdown = "0.1.0"

View File

@ -10,10 +10,11 @@ use std::{
use snafu::{ensure, ResultExt, Snafu};
use tracing::{debug, instrument};
use self::{config::Config, page::Page};
use self::{config::Config, page::Page, post::Post};
pub mod config;
pub mod page;
pub mod post;
/// Error encountered interacting with a [`Site`]
#[derive(Snafu, Debug)]
@ -70,6 +71,22 @@ pub enum SiteError {
#[snafu(source(from(SiteError, Box::new)))]
source: Box<SiteError>,
},
/// Failed to write out a post stub
WritePostStub {
/// The path we tried to write the page stub to
path: PathBuf,
/// The underlying error
source: std::io::Error,
},
/// Error writing out a post
#[snafu(display("Error writing out post {:?}", post))]
PostWrite {
/// The page we were trying to write out
post: PathBuf,
/// The underlying error
#[snafu(source(from(SiteError, Box::new)))]
source: Box<SiteError>,
},
}
/// Representation of the on-disk structure of a site
@ -79,15 +96,20 @@ pub struct Site {
pub config: Config,
/// Non-post static pages
pub pages: HashMap<PathBuf, Page>,
/// Posts
pub posts: HashMap<PathBuf, Post>,
}
impl Default for Site {
fn default() -> Self {
let mut pages = HashMap::new();
pages.insert("index".into(), Page::default());
let mut posts = HashMap::new();
posts.insert("new-blog-who-this".into(), Post::default());
Self {
config: Config::default(),
pages,
posts,
}
}
}
@ -127,6 +149,11 @@ impl Site {
page.write(site_dir, page_path)
.context(PageWriteSnafu { page: page_path })?;
}
// Write out the posts
for (post_path, post) in &self.posts {
post.write(site_dir, post_path)
.context(PostWriteSnafu { post: post_path })?;
}
Ok(())
}
}

117
src/site/post.rs Normal file
View File

@ -0,0 +1,117 @@
//! Management of a post
use std::{
fs::{create_dir_all, File},
io::Write,
path::{Path, PathBuf},
};
use chrono::{Local, NaiveDate};
use serde::{Deserialize, Serialize};
use snafu::{ensure, ResultExt};
use tracing::{debug, instrument};
use super::{
ConfigReifySnafu, CreateDirectorySnafu, ExistanceCheckSnafu, NotADirectorySnafu, SiteError,
WriteConfigSnafu, WritePostStubSnafu,
};
/// Representation of the configuration for a post
#[derive(Serialize, Deserialize, Debug)]
pub struct PostConfig {
/// Title of the post
title: String,
/// Date for the post
date: NaiveDate,
/// Style sheets to apply
stylesheets: Vec<String>,
}
impl Default for PostConfig {
fn default() -> Self {
let date = Local::now().date_naive();
Self {
title: "New Blog Who This".to_string(),
date,
stylesheets: ["default", "post"]
.into_iter()
.map(str::to_string)
.collect(),
}
}
}
/// Representation of a post
#[derive(Debug)]
pub struct Post {
/// Configuration for the post
pub config: PostConfig,
/// Path to the djot file for the post
///
/// This can be any file in the appropiate directory with a `md` or `djot` extension, however
/// there can only be one file within the directory
///
/// This path is relative to the directory of the post
pub file: PathBuf,
}
impl Default for Post {
fn default() -> Self {
Self {
config: PostConfig::default(),
file: "new-blog-who-this.djot".into(),
}
}
}
impl Post {
/// Writes out the configuration for a `Post`, and makes sure the file for the post exists
///
/// # Errors
///
/// Will return an error if serialization fails, the user does not have write permissions for
/// the directory, or any other IO errors occur while writing out the config.
#[instrument(skip(site_path, post_dir))]
pub fn write(
&self,
site_path: impl AsRef<Path>,
post_dir: impl AsRef<Path>,
) -> Result<(), SiteError> {
// build the post_dir
let post_dir = site_path.as_ref().join("posts").join(post_dir.as_ref());
debug!(?post_dir);
// Make sure the post path is a dir if it exists, otherwise create it
if post_dir
.try_exists()
.context(ExistanceCheckSnafu { path: &post_dir })?
{
ensure!(post_dir.is_dir(), NotADirectorySnafu { path: &post_dir });
} else {
create_dir_all(&post_dir).context(CreateDirectorySnafu { path: &post_dir })?;
}
// Serialize the config
let config_path = post_dir.join("post.dhall");
let serialized_config = serde_dhall::serialize(&self.config)
.to_string()
.context(ConfigReifySnafu)?;
let mut config_file =
File::create(&config_path).context(WriteConfigSnafu { path: &config_path })?;
config_file
.write_all(serialized_config.as_bytes())
.context(WriteConfigSnafu { path: &config_path })?;
// Create the post file if it doesn't exist
let post_path = post_dir.join(&self.file);
if !post_path
.try_exists()
.context(ExistanceCheckSnafu { path: &post_dir })?
{
let contents = format!("# {}\n\n", self.config.title);
let mut post_file =
File::create(&post_path).context(WritePostStubSnafu { path: &post_path })?;
post_file
.write_all(contents.as_bytes())
.context(WritePostStubSnafu { path: &post_path })?;
}
Ok(())
}
}