From 3e0112c2065f86ee6b802f9bf6b388a7acee1433 Mon Sep 17 00:00:00 2001 From: Maciej Jur Date: Sun, 21 Apr 2024 23:02:30 +0200 Subject: [PATCH] basic wiki tree --- src/gen/render.rs | 31 +++++++++---- src/html.rs | 2 +- src/html/list.rs | 29 ++++++++---- src/html/post.rs | 39 ++++++++++------- src/html/wiki.rs | 104 ++++++++++++++++++++++++++++++++++++++++++- src/main.rs | 109 +++++++++++++++++++++++++++++++++------------- 6 files changed, 246 insertions(+), 68 deletions(-) diff --git a/src/gen/render.rs b/src/gen/render.rs index a7618ef..9224b14 100644 --- a/src/gen/render.rs +++ b/src/gen/render.rs @@ -1,32 +1,45 @@ +use std::fmt::Debug; use std::fs::{self, File}; use std::path::{Path, PathBuf}; use std::io::Write; -use crate::html::LinkableData; -use crate::Everything; +use crate::html::Linkable; +use crate::Sack; pub enum AssetKind { - Html(Box String>), + Html(Box String>), Image, Unknown, Bib(hayagriva::Library), } +impl Debug for AssetKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Html(ptr) => f.debug_tuple("Html").field(&"ptr").finish(), + Self::Image => write!(f, "Image"), + Self::Unknown => write!(f, "Unknown"), + Self::Bib(arg0) => f.debug_tuple("Bib").field(arg0).finish(), + } + } +} + +#[derive(Debug)] pub struct Asset { pub kind: AssetKind, pub out: PathBuf, - pub link: Option, + pub link: Option, pub meta: super::Source, } -pub struct Virtual(pub PathBuf, pub Box String>); +pub struct Virtual(pub PathBuf, pub Box String>); impl Virtual { pub fn new(path: P, call: F) -> Self where P: AsRef, - F: Fn(&Everything) -> String + 'static + F: Fn(&Sack) -> String + 'static { Self(path.as_ref().into(), Box::new(call)) } @@ -59,7 +72,7 @@ pub fn render(items: &[Item]) { }) .collect(); - let everything = Everything { assets: &assets }; + let everything = Sack { assets: &assets }; for item in items { match item { @@ -70,7 +83,7 @@ pub fn render(items: &[Item]) { } -fn render_real(item: &Asset, assets: &Everything) { +fn render_real(item: &Asset, assets: &Sack) { match &item.kind { AssetKind::Html(render) => { let i = &item.meta.path; @@ -101,7 +114,7 @@ fn render_real(item: &Asset, assets: &Everything) { } } -fn render_fake(item: &Virtual, assets: &Everything) { +fn render_fake(item: &Virtual, assets: &Sack) { let Virtual(out, render) = item; let o = Path::new("dist").join(&out); diff --git a/src/html.rs b/src/html.rs index 8b45cb9..8b5a1bf 100644 --- a/src/html.rs +++ b/src/html.rs @@ -15,4 +15,4 @@ pub use show::show; pub use special::{map, search}; pub use wiki::wiki; -pub use list::LinkableData; +pub use list::{Linkable, Link, LinkDate}; diff --git a/src/html/list.rs b/src/html/list.rs index ee535f8..bd20853 100644 --- a/src/html/list.rs +++ b/src/html/list.rs @@ -3,18 +3,29 @@ use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable}; use crate::html::page; -#[derive(Clone)] -pub struct LinkableData { +#[derive(Debug, Clone)] +pub struct Link { pub path: String, pub name: String, - pub date: DateTime, pub desc: Option, } +#[derive(Debug, Clone)] +pub struct LinkDate { + pub link: Link, + pub date: DateTime, +} + +#[derive(Debug, Clone)] +pub enum Linkable { + Link(Link), + Date(LinkDate), +} + pub fn list<'data, 'list>( title: &'data str, - groups: &'data [(i32, Vec)] + groups: &'data [(i32, Vec)] ) -> impl Renderable + 'list where 'data: 'list @@ -36,7 +47,7 @@ pub fn list<'data, 'list>( page(title, list) } -fn section(year: i32, group: &[LinkableData]) -> impl Renderable + '_ { +fn section(year: i32, group: &[LinkDate]) -> impl Renderable + '_ { maud_move!( section .page-list-year { header .page-list-year__header { @@ -49,19 +60,19 @@ fn section(year: i32, group: &[LinkableData]) -> impl Renderable + '_ { ) } -fn link(data: &LinkableData) -> impl Renderable + '_ { +fn link(data: &LinkDate) -> impl Renderable + '_ { let time = data.date.format("%m/%d"); maud_move!( - a .page-item href=(&data.path) { + a .page-item href=(&data.link.path) { div .page-item__header { h3 { - (&data.name) + (&data.link.name) } time datetime=(data.date.to_rfc3339()) { (time.to_string()) } } - @if let Some(ref desc) = data.desc { + @if let Some(ref desc) = data.link.desc { div .page-item__desc { (desc) } diff --git a/src/html/post.rs b/src/html/post.rs index b3724dc..72c98b0 100644 --- a/src/html/post.rs +++ b/src/html/post.rs @@ -4,6 +4,28 @@ use crate::html::page; use crate::text::md::Outline; +pub fn tree(outline: Outline) -> impl Renderable { + maud_move!( + section .link-tree { + h2 .link-tree__heading { + a .link-tree__heading-text href="#top" { "Content" } + } + nav #table-of-contents .link-tree__nav { + ul .link-tree__nav-list { + @for (title, id) in outline.0 { + li .link-tree__nav-list-item { + a .link-tree__nav-list-text.link href=(format!("#{id}")) { + (title) + } + } + } + } + } + } + ) +} + + pub fn post<'fm, 'md, 'post, T>( fm: &'fm Post, content: T, @@ -25,22 +47,7 @@ pub fn post<'fm, 'md, 'post, T>( label .wiki-aside__slider for="wiki-aside-shown" { img .wiki-icon src="/static/svg/double-arrow.svg" width="24" height="24"; } - section .link-tree { - h2 .link-tree__heading { - a .link-tree__heading-text href="#top" { "Content" } - } - nav #table-of-contents .link-tree__nav { - ul .link-tree__nav-list { - @for (title, id) in outline.0 { - li .link-tree__nav-list-item { - a .link-tree__nav-list-text.link href=(format!("#{id}")) { - (title) - } - } - } - } - } - } + (tree(outline)) } article .wiki-article /*class:list={classlist)*/ { diff --git a/src/html/wiki.rs b/src/html/wiki.rs index b3dbee0..1511732 100644 --- a/src/html/wiki.rs +++ b/src/html/wiki.rs @@ -1,15 +1,111 @@ +use std::collections::HashMap; + use hypertext::{html_elements, maud_move, GlobalAttributes, Renderable}; + use crate::md::Wiki; use crate::html::page; use crate::text::md::Outline; +use crate::Sack; + +use super::Link; -pub fn wiki<'data, 'html, T>( +#[derive(Debug)] +struct TreeNode { + pub name: String, + pub children: HashMap, +} + +impl TreeNode { + fn new(name: &str) -> Self { + TreeNode { + name: name.to_string(), + children: HashMap::new(), + } + } + + fn add_link(&mut self, link: &Link) { + let mut current_node = self; + for component in link.path.split('/').filter(|s| !s.is_empty()) { + current_node = current_node.children.entry(component.to_string()) + .or_insert(TreeNode::new(component)); + } + } +} + +fn tree(sack: &Sack) -> impl Renderable { + let mut tree = TreeNode::new("wiki"); + for link in sack.get_links_2("wiki/**/*.html") { + tree.add_link(&link); + }; + + maud_move!( + h2 .link-tree__heading { + // {pages.chain(x => x.prefix) + // .map(pathify) + // .mapOrDefault(href => + // {heading}, + // {heading} + // )} + } + nav .link-tree__nav { + // {pages.map(pages => ).extract()} + (level(&tree)) + } + ) +} + +fn level(tree: &TreeNode) -> impl Renderable + '_ { + for (key, next) in tree.children.iter() { + println!("{key}"); + level(next); + }; + maud_move!( + ul .link-tree__nav-list { + @for (key, next) in tree.children.iter() { + li .link-tree__nav-list-item { + span .link-tree__nav-list-text { (key) } + @if next.children.len() > 0 { + (level(next)) + } + } + } + } + ) +} + +// {tree.children +// .map(m => Object.values(m)) +// .filter(xs => xs.length > 0) +// .map(pages => +// +// ).extract()} + + +pub fn wiki<'data, 'html, 'sack, T>( fm: &'data Wiki, content: T, outline: Outline, + sack: &'sack Sack, ) -> impl Renderable + 'html where + 'sack: 'html, 'data: 'html, T: Renderable + 'data { @@ -25,7 +121,11 @@ pub fn wiki<'data, 'html, T>( img .wiki-icon src="/static/svg/double-arrow.svg" width="24" height="24"; } // Navigation tree - // + section .link-tree { + div { + (tree(sack)) + } + } } article .wiki-article /*class:list={classlist)*/ { diff --git a/src/main.rs b/src/main.rs index 271113e..29745bd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, path::Path}; use std::fs; use chrono::Datelike; use grass; -use html::LinkableData; +use html::{Link, LinkDate, Linkable}; use hypertext::{Raw, Renderable}; use once_cell::sync::Lazy; use text::md::Outline; @@ -46,49 +46,78 @@ static REPO: Lazy = Lazy::new(|| { }); /// This struct allows for querying the website hierarchy -struct Everything<'a> { +#[derive(Debug)] +struct Sack<'a> { assets: &'a [&'a gen::Asset], } -impl Everything<'_> { - fn get_linkable(&self, path: &str) -> Vec { +impl Sack<'_> { + fn get_links(&self, path: &str) -> Vec { let pattern = glob::Pattern::new(path).unwrap(); self.assets.iter() .filter(|f| pattern.matches_path(&f.out)) - .filter_map(|f| f.link.clone()) + .filter_map(|f| match &f.link { + Some(Linkable::Date(link)) => Some(link.clone()), + _ => None, + }) + .collect() + } + + fn get_links_2(&self, path: &str) -> Vec { + let pattern = glob::Pattern::new(path).unwrap(); + self.assets.iter() + .filter(|f| pattern.matches_path(&f.out)) + .filter_map(|f| match &f.link { + Some(Linkable::Link(link)) => Some(link.clone()), + _ => None, + }) .collect() } } trait Transformable { - fn transform<'f, 'm, 'html, T>(&'f self, content: T, outline: Outline) -> impl Renderable + 'html + fn transform<'f, 'm, 'html, 'sack, T>( + &'f self, + content: T, + outline: Outline, + sack: &'sack Sack, + ) -> impl Renderable + 'html where 'f: 'html, 'm: 'html, + 'sack: 'html, T: Renderable + 'm; - fn as_link(&self, path: String) -> Option; + fn as_link(&self, path: String) -> Option; fn render(data: &str) -> (Outline, String); } impl Transformable for md::Post { - fn transform<'f, 'm, 'html, T>(&'f self, content: T, outline: Outline) -> impl Renderable + 'html + fn transform<'f, 'm, 'html, 'sack, T>( + &'f self, + content: T, + outline: Outline, + sack: &'sack Sack, + ) -> impl Renderable + 'html where 'f: 'html, 'm: 'html, + 'sack: 'html, T: Renderable + 'm { html::post(self, content, outline) } - fn as_link(&self, path: String) -> Option { - Some(html::LinkableData { - path: path.strip_suffix("index.html").unwrap().to_owned(), - name: self.title.to_owned(), + fn as_link(&self, path: String) -> Option { + Some(Linkable::Date(LinkDate { + link: Link { + path: path.strip_suffix("index.html").unwrap().to_owned(), + name: self.title.to_owned(), + desc: self.desc.to_owned(), + }, date: self.date.to_owned(), - desc: self.desc.to_owned(), - }) + })) } fn render(data: &str) -> (Outline, String) { @@ -97,21 +126,29 @@ impl Transformable for md::Post { } impl Transformable for md::Slide { - fn transform<'f, 'm, 'html, T>(&'f self, content: T, _: Outline) -> impl Renderable + 'html + fn transform<'f, 'm, 'html, 'sack, T>( + &'f self, + content: T, + _: Outline, + sack: &'sack Sack, + ) -> impl Renderable + 'html where 'f: 'html, 'm: 'html, + 'sack: 'html, T: Renderable + 'm { html::show(self, content) } - fn as_link(&self, path: String) -> Option { - Some(html::LinkableData { - path: path.strip_suffix("index.html").unwrap().to_owned(), - name: self.title.to_owned(), + fn as_link(&self, path: String) -> Option { + Some(Linkable::Date(LinkDate { + link: Link { + path: path.strip_suffix("index.html").unwrap().to_owned(), + name: self.title.to_owned(), + desc: self.desc.to_owned(), + }, date: self.date.to_owned(), - desc: self.desc.to_owned(), - }) + })) } fn render(data: &str) -> (Outline, String) { @@ -128,16 +165,26 @@ impl Transformable for md::Slide { } impl Transformable for md::Wiki { - fn transform<'f, 'm, 'html, T>(&'f self, content: T, outline: Outline) -> impl Renderable + 'html + fn transform<'f, 'm, 'html, 'sack, T>( + &'f self, + content: T, + outline: Outline, + sack: &'sack Sack, + ) -> impl Renderable + 'html where 'f: 'html, 'm: 'html, + 'sack: 'html, T: Renderable + 'm { - html::wiki(self, content, outline) + html::wiki(self, content, outline, sack) } - fn as_link(&self, _: String) -> Option { - None + fn as_link(&self, path: String) -> Option { + Some(Linkable::Link(Link { + path: path.strip_suffix("index.html").unwrap().to_owned(), + name: self.title.to_owned(), + desc: None, + })) } fn render(data: &str) -> (Outline, String) { @@ -146,7 +193,7 @@ impl Transformable for md::Wiki { } -fn to_list(list: Vec) -> String { +fn to_list(list: Vec) -> String { let mut groups = HashMap::>::new(); for page in list { @@ -183,9 +230,9 @@ fn transform(meta: gen::Source) -> gen::Asset let (fm, md) = md::preflight::(&data); let link = T::as_link(&fm, Path::new("/").join(&loc).to_str().unwrap().to_owned()); - let call = move |_: &Everything| { + let call = move |everything: &Sack| { let (outline, html) = T::render(&md); - T::transform(&fm, Raw(html), outline).render().into() + T::transform(&fm, Raw(html), outline, everything).render().into() }; gen::Asset { @@ -248,7 +295,7 @@ fn main() { gen::Asset { kind: gen::AssetKind::Html(Box::new(|_| { let data = std::fs::read_to_string("content/index.md").unwrap(); - let (outline, html) = text::md::parse(&data); + let (_, html) = text::md::parse(&data); html::home(Raw(html)).render().to_owned().into() })), out: "index.html".into(), @@ -261,10 +308,10 @@ fn main() { } }.into(), gen::Virtual("posts/index.html".into(), Box::new(|all| - to_list(all.get_linkable("posts/**/*.html")) + to_list(all.get_links("posts/**/*.html")) )).into(), gen::Virtual("slides/index.html".into(), Box::new(|all| - to_list(all.get_linkable("slides/**/*.html")) + to_list(all.get_links("slides/**/*.html")) )).into(), ], gen::gather("content/about.md", &["md"].into())