use core::convert::Infallible; use std::{ collections::HashMap, str::FromStr, fs, path::{Path, PathBuf}, ffi::OsStr }; pub struct Lexicon { pub word: String, pub related: Vec } impl FromStr for Lexicon { type Err = Infallible; fn from_str(s: &str) -> Result { let parts = s.split(" "); let tag = parts.clone().take_while(|&s| s != "|").nth(0).unwrap_or("").to_string(); let rest = parts.skip_while(|&s| s != "|").skip(1).map(|s| s.to_string()).collect::>(); Ok(Lexicon { word: tag, related: rest }) } } fn backlink<'a, 'b>(v: &'a Vec, tag: &'b str) -> Option<&'a Lexicon> { v.iter().find(|l| { l.related.iter().find(|&s| s == tag).is_some() }) } fn find<'a, 'b>(v: &'a Vec, tag: &'b str) -> Option<&'a Lexicon> { v.iter().find( |l| l.word == tag ) } fn links<'a, 'b>(v: &'a Vec, tag: &'b str) -> Vec<&'a Lexicon> { let l = match find(v, tag) { Some(l) => l, None => return Vec::new() }; l.related.iter().map(|g| find(v, g)).filter(|g| g.is_some()).map(|g| g.unwrap()).collect::>() } fn create_links<'a, 'b>(v: &'a Vec, tag: &'b str) -> Vec<&'a Lexicon> { let mut ret = Vec::<&'a Lexicon>::new(); let mut b = tag; while { let lex = find(v, b).unwrap(); ret.push(lex); if let Some(prev) = backlink(v, b) { b = prev.word.as_str(); true } else { false } } {} ret } fn create_nav<'a, 'b>(v: &'a Vec<&'b Lexicon>) -> String { let mut html = Vec::::new(); let v = v.iter().rev().take(3).collect::>(); let iter = v.iter(); let count = v.len(); for (i, level) in iter.enumerate() { html.push("
    ".into()); let next_word = if i < count - 1 { &v[i + 1].word } else { "" }; for (j, related) in level.related.iter().enumerate() { let class = if related == next_word { "nav-intended" } else { "nav-not-intended" }; html.push(format!("
  • {}
  • ", related, class, related)); } html.push("
".into()); } html.join("") } fn process(lexicon: &Vec, template: &String, src: &Path) -> String { let dest = template.clone(); let stem = src.file_stem().and_then(OsStr::to_str).unwrap(); let ext = src.extension().and_then(OsStr::to_str).unwrap(); let src = fs::read_to_string(src).unwrap(); let m = create_links(lexicon, stem); let nav = create_nav(&m); let dest = dest.replace("{{ nav }}", &nav); let dest = dest.replace("{{ article }}", &src); dest } fn main() { let lexicon = fs::read_to_string("lexicon").expect("Missing lexicon"); let lexicon = lexicon.split("\n").map(|s| Lexicon::from_str(s).unwrap()).collect::>(); let template = fs::read_to_string("index.html").expect("Missing index.html"); let paths = fs::read_dir("source").unwrap(); for path in paths { let mut dest = PathBuf::from("site"); // Get the source of the path let src = path.unwrap().path(); let name = src.file_name().and_then(OsStr::to_str).unwrap(); let stem = src.file_stem().and_then(OsStr::to_str).unwrap(); let site = process(&lexicon, &template, &src); // Build dest dest.push(stem); fs::write(&dest, &site); } }