From e69dcdc197332879977f2eb9e67025bc2b4d10e5 Mon Sep 17 00:00:00 2001 From: lqdev Date: Thu, 17 Aug 2023 21:59:56 +0200 Subject: [PATCH] planting a seed --- .gitignore | 1 + Cargo.lock | 295 ++++++++++++++++++++ Cargo.toml | 6 + content/tree/root.tree | 37 +++ crates/treehouse-format/Cargo.toml | 7 + crates/treehouse-format/src/lib.rs | 113 ++++++++ crates/treehouse-incubator/Cargo.toml | 11 + crates/treehouse-incubator/src/main.rs | 45 +++ crates/treehouse-incubator/src/tree_html.rs | 30 ++ static/index.hbs | 13 + treehouse.toml | 3 + 11 files changed, 561 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 content/tree/root.tree create mode 100644 crates/treehouse-format/Cargo.toml create mode 100644 crates/treehouse-format/src/lib.rs create mode 100644 crates/treehouse-incubator/Cargo.toml create mode 100644 crates/treehouse-incubator/src/main.rs create mode 100644 crates/treehouse-incubator/src/tree_html.rs create mode 100644 static/index.hbs create mode 100644 treehouse.toml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..22f201a --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,295 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "handlebars" +version = "4.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83c3372087601b532857d332f5957cbae686da52bb7810bf038c3e3c3cc2fa0d" +dependencies = [ + "log", + "pest", + "pest_derive", + "serde", + "serde_json", + "thiserror", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "memchr" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "pest" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1acb4a4365a13f749a93f1a094a7805e5cfa0955373a9de860d962eaa3a5fe5a" +dependencies = [ + "thiserror", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666d00490d4ac815001da55838c500eafb0320019bbaa44444137c48b443a853" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ca01446f50dbda87c1786af8770d535423fa8a53aec03b8f4e3d7eb10e0929" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pest_meta" +version = "2.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56af0a30af74d0445c0bf6d9d051c979b516a1a5af790d251daee76005420a48" +dependencies = [ + "once_cell", + "pest", + "sha2", +] + +[[package]] +name = "proc-macro2" +version = "1.0.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "pulldown-cmark" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a1a2f1f0a7ecff9c31abbe177637be0e97a0aef46cf8738ece09327985d998" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" + +[[package]] +name = "serde" +version = "1.0.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" + +[[package]] +name = "serde_json" +version = "1.0.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "syn" +version = "2.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97a802ec30afc17eee47b2855fc72e0c4cd62be9b4efe6591edde0ec5bd68d8f" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bb623b56e39ab7dcd4b1b98bb6c8f8d907ed255b18de254088016b27a8ee19b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "treehouse-format" +version = "0.1.0" +dependencies = [ + "thiserror", +] + +[[package]] +name = "treehouse-incubator" +version = "0.1.0" +dependencies = [ + "handlebars", + "pulldown-cmark", + "thiserror", + "treehouse-format", +] + +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + +[[package]] +name = "ucd-trie" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..06237c5 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[workspace] +members = ["crates/*"] +resolver = "2" + +[workspace.dependencies] +treehouse-format = { path = "crates/treehouse-format" } diff --git a/content/tree/root.tree b/content/tree/root.tree new file mode 100644 index 0000000..0c3df33 --- /dev/null +++ b/content/tree/root.tree @@ -0,0 +1,37 @@ +- # treehouse + + - welcome to treehouse! + + - treehouse is a brand new static website generator, inspired by the likes of Jekyll and Hugo, but offering a writing experience more close to Logseq + + - ie. a public braindump + + - since you're here, you're probably just setting up + + - i'd highly recommend familiarizing yourself with the syntax by visiting `content/tree/root.tree` + + - this special file is almost like your index.html + + + the syntax is pretty simple + + - separate blocks are delimited with a blank line + + - the indentation level (number of spaces) decides how nested the block is + + - the exact amount doesn't matter, just that more nested blocks must have more spaces before them than their parents + + - also blocks on the same level must be indented with the same number of spaces but that's hopefully pretty obvious + + + the block content begins with a dash `-` or a plus `+` + + - a dash `-` means that the block is open by default + + - a plus `+` means that the block is hidden by default + + - before the block content, there can be an arbitrary amount of TOML specifying the block config + + - many keys are available but they aren't really documented outside of code + + - blocks can span multiple lines as long as they are not broken apart with a blank line + + - that means each block can contain at most one paragraph, unless you use dirty HTML hacks (cheater!) diff --git a/crates/treehouse-format/Cargo.toml b/crates/treehouse-format/Cargo.toml new file mode 100644 index 0000000..7db6b26 --- /dev/null +++ b/crates/treehouse-format/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "treehouse-format" +version = "0.1.0" +edition = "2021" + +[dependencies] +thiserror = "1.0.47" diff --git a/crates/treehouse-format/src/lib.rs b/crates/treehouse-format/src/lib.rs new file mode 100644 index 0000000..722d226 --- /dev/null +++ b/crates/treehouse-format/src/lib.rs @@ -0,0 +1,113 @@ +use std::ops::Range; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BranchKind { + /// Expanded by default. + Expanded, + /// Folded by default. + Collapsed, +} + +impl BranchKind { + pub fn char(&self) -> char { + match self { + BranchKind::Expanded => '-', + BranchKind::Collapsed => '+', + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Branch { + pub indent_level: usize, + pub config: Range, + pub kind: BranchKind, + pub content: Range, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Parser<'a> { + pub input: &'a str, + pub position: usize, +} + +#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)] +pub enum ParseErrorKind { + #[error("branch kind (`+` or `-`) expected")] + BranchKindExpected, +} + +#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] +#[error("{range:?}: {kind}")] +pub struct ParseError { + pub kind: ParseErrorKind, + pub range: Range, +} + +impl<'a> Parser<'a> { + fn current(&self) -> Option { + self.input[self.position..].chars().next() + } + + fn advance(&mut self) { + self.position += self.current().map(|c| c.len_utf8()).unwrap_or(0); + } + + fn eat_as_long_as(&mut self, c: char) -> usize { + let mut count = 0; + while self.current() == Some(c) { + count += 1; + self.advance(); + } + count + } + + fn eat_until(&mut self, c: char) { + while self.current() != Some(c) { + self.advance(); + } + self.advance(); + } + + pub fn next_branch(&mut self) -> Result, ParseError> { + if self.current().is_none() { + return Ok(None); + } + + let indent_level = self.eat_as_long_as(' '); + + // TODO: Configs + let config_start = self.position; + let config_end = self.position; + + let branch_kind_start = self.position; + let branch_kind = match self.current() { + Some('-') => BranchKind::Expanded, + Some('+') => BranchKind::Collapsed, + _ => { + return Err(ParseError { + kind: ParseErrorKind::BranchKindExpected, + range: branch_kind_start..branch_kind_start + 1, + }) + } + }; + self.advance(); + + let content_start = self.position; + loop { + self.eat_until('\n'); + if let Some('\n') | None = self.current() { + self.advance(); + break; + } + } + let content_end = self.position; + + Ok(Some(Branch { + indent_level, + config: config_start..config_end, + kind: branch_kind, + content: content_start..content_end, + })) + } +} diff --git a/crates/treehouse-incubator/Cargo.toml b/crates/treehouse-incubator/Cargo.toml new file mode 100644 index 0000000..d906aa5 --- /dev/null +++ b/crates/treehouse-incubator/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "treehouse-incubator" +version = "0.1.0" +edition = "2021" + +[dependencies] +handlebars = "4.3.7" +pulldown-cmark = { version = "0.9.3", default-features = false } +thiserror = "1.0.47" +treehouse-format = { workspace = true } + diff --git a/crates/treehouse-incubator/src/main.rs b/crates/treehouse-incubator/src/main.rs new file mode 100644 index 0000000..88d1c2b --- /dev/null +++ b/crates/treehouse-incubator/src/main.rs @@ -0,0 +1,45 @@ +use tree_html::HtmlGenerator; + +mod tree_html; + +#[derive(Debug, thiserror::Error)] +enum Error { + #[error("I/O error: {0}")] + Io(#[from] std::io::Error), + + #[error("treehouse parsing error: {0}")] + Parse(#[from] treehouse_format::ParseError), +} + +fn main() -> Result<(), Box> { + let _ = std::fs::remove_dir_all("target/site"); + std::fs::create_dir_all("target/site")?; + + let root_file = std::fs::read_to_string("content/tree/root.tree")?; + + let mut parser = treehouse_format::Parser { + input: &root_file, + position: 0, + }; + let mut generator = HtmlGenerator::default(); + while let Some(branch) = parser.next_branch()? { + for _ in 0..branch.indent_level { + print!(" "); + } + println!( + "{} {:?}", + branch.kind.char(), + &root_file[branch.content.clone()] + ); + generator.add(&root_file, &branch); + } + std::fs::write( + "target/site/index.html", + format!( + "{}", + generator.finish() + ), + )?; + + Ok(()) +} diff --git a/crates/treehouse-incubator/src/tree_html.rs b/crates/treehouse-incubator/src/tree_html.rs new file mode 100644 index 0000000..c692793 --- /dev/null +++ b/crates/treehouse-incubator/src/tree_html.rs @@ -0,0 +1,30 @@ +use treehouse_format::Branch; + +#[derive(Debug, Clone, Default)] +pub struct HtmlGenerator { + buffer: String, + indent_level_stack: Vec, +} + +impl HtmlGenerator { + pub fn add(&mut self, source: &str, branch: &Branch) { + if Some(&branch.indent_level) > self.indent_level_stack.last() { + self.indent_level_stack.push(branch.indent_level); + self.buffer.push_str("
    "); + } + while Some(&branch.indent_level) < self.indent_level_stack.last() { + self.indent_level_stack.pop(); + self.buffer.push_str("
"); + } + self.buffer.push_str("
  • "); + self.buffer.push_str(&source[branch.content.clone()]); + self.buffer.push_str("
  • "); + } + + pub fn finish(mut self) -> String { + for _ in self.indent_level_stack.drain(..) { + self.buffer.push_str(""); + } + self.buffer + } +} diff --git a/static/index.hbs b/static/index.hbs new file mode 100644 index 0000000..7af0dbf --- /dev/null +++ b/static/index.hbs @@ -0,0 +1,13 @@ + + + + + + {{ config.treehouse.title }} + + + + {{ tree }} + + + diff --git a/treehouse.toml b/treehouse.toml new file mode 100644 index 0000000..453beb1 --- /dev/null +++ b/treehouse.toml @@ -0,0 +1,3 @@ +[treehouse] +title = "treehouse" +author = "liquidex"