planting a seed

This commit is contained in:
りき萌 2023-08-17 21:59:56 +02:00
commit e69dcdc197
11 changed files with 561 additions and 0 deletions

View file

@ -0,0 +1,7 @@
[package]
name = "treehouse-format"
version = "0.1.0"
edition = "2021"
[dependencies]
thiserror = "1.0.47"

View file

@ -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<usize>,
pub kind: BranchKind,
pub content: Range<usize>,
}
#[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<usize>,
}
impl<'a> Parser<'a> {
fn current(&self) -> Option<char> {
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<Option<Branch>, 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,
}))
}
}

View file

@ -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 }

View file

@ -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<dyn std::error::Error>> {
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!(
"<!DOCTYPE html><html><head></head><body>{}</body></html>",
generator.finish()
),
)?;
Ok(())
}

View file

@ -0,0 +1,30 @@
use treehouse_format::Branch;
#[derive(Debug, Clone, Default)]
pub struct HtmlGenerator {
buffer: String,
indent_level_stack: Vec<usize>,
}
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("<ul>");
}
while Some(&branch.indent_level) < self.indent_level_stack.last() {
self.indent_level_stack.pop();
self.buffer.push_str("</ul>");
}
self.buffer.push_str("<li>");
self.buffer.push_str(&source[branch.content.clone()]);
self.buffer.push_str("</li>");
}
pub fn finish(mut self) -> String {
for _ in self.indent_level_stack.drain(..) {
self.buffer.push_str("</ul>");
}
self.buffer
}
}