basic wiki tree

This commit is contained in:
Maciej Jur 2024-04-21 23:02:30 +02:00
parent b66f168c42
commit 3e0112c206
Signed by: kamov
GPG key ID: 191CBFF5F72ECAFD
6 changed files with 246 additions and 68 deletions

View file

@ -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<dyn Fn(&Everything) -> String>),
Html(Box<dyn Fn(&Sack) -> 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<LinkableData>,
pub link: Option<Linkable>,
pub meta: super::Source,
}
pub struct Virtual(pub PathBuf, pub Box<dyn Fn(&Everything) -> String>);
pub struct Virtual(pub PathBuf, pub Box<dyn Fn(&Sack) -> String>);
impl Virtual {
pub fn new<P, F>(path: P, call: F) -> Self
where
P: AsRef<Path>,
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);

View file

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

View file

@ -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<Utc>,
pub desc: Option<String>,
}
#[derive(Debug, Clone)]
pub struct LinkDate {
pub link: Link,
pub date: DateTime<Utc>,
}
#[derive(Debug, Clone)]
pub enum Linkable {
Link(Link),
Date(LinkDate),
}
pub fn list<'data, 'list>(
title: &'data str,
groups: &'data [(i32, Vec<LinkableData>)]
groups: &'data [(i32, Vec<LinkDate>)]
) -> 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)
}

View file

@ -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)*/ {

View file

@ -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<String, TreeNode>,
}
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 =>
// <a class="link-tree__heading-text" href={href}>{heading}</a>,
// <span class="link-tree__heading-text">{heading}</span>
// )}
}
nav .link-tree__nav {
// {pages.map(pages => <PagesList {...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 =>
// <ul class="link-tree__nav-list">
// {pages
// .sort(compare)
// .map(page => ({...page, current: checkCurrent(page.slug) }))
// .map(page =>
// <li class="link-tree__nav-list-item">
// {page.slug
// .chain(slug => prefix.map(prefix => pathify(prefix, slug)))
// .map(href => (page.current)
// ? <button id="current-page-button" class="link-tree__nav-list-text current">{page.title}</button>
// : <a class="link-tree__nav-list-text link" href={href}>{page.title}</a>
// )
// .orDefault(<span class="link-tree__nav-list-text">{page.title}</span>)}
// <Astro.self tree={page} slug={slug} prefix={prefix} />
// </li>
// )}
// </ul>
// ).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
// <Tree heading="Personal Wiki" pages={pages} headings={headings} />
section .link-tree {
div {
(tree(sack))
}
}
}
article .wiki-article /*class:list={classlist)*/ {

View file

@ -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<BuildInfo> = 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<LinkableData> {
impl Sack<'_> {
fn get_links(&self, path: &str) -> Vec<LinkDate> {
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<Link> {
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<html::LinkableData>;
fn as_link(&self, path: String) -> Option<Linkable>;
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<html::LinkableData> {
Some(html::LinkableData {
path: path.strip_suffix("index.html").unwrap().to_owned(),
name: self.title.to_owned(),
fn as_link(&self, path: String) -> Option<Linkable> {
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<html::LinkableData> {
Some(html::LinkableData {
path: path.strip_suffix("index.html").unwrap().to_owned(),
name: self.title.to_owned(),
fn as_link(&self, path: String) -> Option<Linkable> {
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<html::LinkableData> {
None
fn as_link(&self, path: String) -> Option<Linkable> {
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<LinkableData>) -> String {
fn to_list(list: Vec<LinkDate>) -> String {
let mut groups = HashMap::<i32, Vec<_>>::new();
for page in list {
@ -183,9 +230,9 @@ fn transform<T>(meta: gen::Source) -> gen::Asset
let (fm, md) = md::preflight::<T>(&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())