From 76fe128587fe45fd1052840c16ce514bfe8aa165 Mon Sep 17 00:00:00 2001 From: Nathan McCarty Date: Thu, 16 Mar 2023 16:06:39 -0400 Subject: [PATCH] feat: Add Post struct --- Cargo.lock | 230 +++++++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 1 + src/site.rs | 29 +++++- src/site/post.rs | 117 ++++++++++++++++++++++++ 4 files changed, 376 insertions(+), 1 deletion(-) create mode 100644 src/site/post.rs diff --git a/Cargo.lock b/Cargo.lock index 0f01abb..c548ec4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/Cargo.toml b/Cargo.toml index e698621..6aa2bae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/src/site.rs b/src/site.rs index a25c702..ebd39ae 100644 --- a/src/site.rs +++ b/src/site.rs @@ -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, }, + /// 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, + }, } /// 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, + /// Posts + pub posts: HashMap, } 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(()) } } diff --git a/src/site/post.rs b/src/site/post.rs new file mode 100644 index 0000000..2da0ee7 --- /dev/null +++ b/src/site/post.rs @@ -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, +} + +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, + post_dir: impl AsRef, + ) -> 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(()) + } +}