Experimental navigation
This commit is contained in:
parent
c62039dfdf
commit
ac0c4050eb
4 changed files with 226 additions and 42 deletions
|
@ -9,6 +9,9 @@ use crate::document::document::Document;
|
||||||
use crate::document::document::ElemReference;
|
use crate::document::document::ElemReference;
|
||||||
use crate::document::variable::Variable;
|
use crate::document::variable::Variable;
|
||||||
|
|
||||||
|
use super::navigation::NavEntry;
|
||||||
|
use super::navigation::Navigation;
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
pub enum Target {
|
pub enum Target {
|
||||||
HTML,
|
HTML,
|
||||||
|
@ -18,8 +21,8 @@ pub enum Target {
|
||||||
pub struct Compiler {
|
pub struct Compiler {
|
||||||
target: Target,
|
target: Target,
|
||||||
cache: Option<RefCell<Connection>>,
|
cache: Option<RefCell<Connection>>,
|
||||||
// TODO:
|
|
||||||
reference_count: RefCell<HashMap<String, HashMap<String, usize>>>,
|
reference_count: RefCell<HashMap<String, HashMap<String, usize>>>,
|
||||||
|
// TODO: External references, i.e resolved later
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Compiler {
|
impl Compiler {
|
||||||
|
@ -130,37 +133,71 @@ impl Compiler {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn navigation(&self, navigation: &Navigation, document: &dyn Document) -> String
|
||||||
|
{
|
||||||
|
let mut result = String::new();
|
||||||
|
match self.target()
|
||||||
|
{
|
||||||
|
Target::HTML => {
|
||||||
|
result += r#"<ul id="navbar">"#;
|
||||||
|
|
||||||
|
fn process(result: &mut String, name: &String, ent: &NavEntry, depth: usize)
|
||||||
|
{
|
||||||
|
let ent_path = ent.path.as_ref()
|
||||||
|
.map_or("#".to_string(),|path| path.clone());
|
||||||
|
result.push_str(format!(r#"<li><a href="{ent_path}">{name}</a></li>"#).as_str());
|
||||||
|
|
||||||
|
if let Some(children) = ent.children.as_ref()
|
||||||
|
{
|
||||||
|
result.push_str("<ul>");
|
||||||
|
for (name, ent) in children
|
||||||
|
{
|
||||||
|
process(result, name, ent, depth+1);
|
||||||
|
}
|
||||||
|
result.push_str("</ul>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (name, ent) in &navigation.entries
|
||||||
|
{
|
||||||
|
process(&mut result, name, ent, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
result += r#"</ul>"#;
|
||||||
|
},
|
||||||
|
_ => todo!("")
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
pub fn footer(&self, _document: &dyn Document) -> String {
|
pub fn footer(&self, _document: &dyn Document) -> String {
|
||||||
let mut result = String::new();
|
let mut result = String::new();
|
||||||
match self.target() {
|
match self.target() {
|
||||||
Target::HTML => {
|
Target::HTML => {
|
||||||
result += "</body></html>";
|
result += "</body></html>";
|
||||||
}
|
}
|
||||||
Target::LATEX => {}
|
Target::LATEX => todo!("")
|
||||||
}
|
}
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile(&self, document: &dyn Document) -> String {
|
pub fn compile(&self, navigation: &Navigation, document: &dyn Document) -> String {
|
||||||
let mut out = String::new();
|
let mut out = String::new();
|
||||||
let borrow = document.content().borrow();
|
let borrow = document.content().borrow();
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
out += self.header(document).as_str();
|
out += self.header(document).as_str();
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
out += self.navigation(navigation, document).as_str();
|
||||||
|
|
||||||
// Body
|
// Body
|
||||||
for i in 0..borrow.len() {
|
for i in 0..borrow.len() {
|
||||||
let elem = &borrow[i];
|
let elem = &borrow[i];
|
||||||
//let prev = match i
|
|
||||||
//{
|
|
||||||
// 0 => None,
|
|
||||||
// _ => borrow.get(i-1),
|
|
||||||
//};
|
|
||||||
//let next = borrow.get(i+1);
|
|
||||||
|
|
||||||
match elem.compile(self, document) {
|
match elem.compile(self, document) {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
//println!("Elem: {}\nCompiled to: {result}", elem.to_string());
|
|
||||||
out.push_str(result.as_str())
|
out.push_str(result.as_str())
|
||||||
}
|
}
|
||||||
Err(err) => println!("Unable to compile element: {err}\n{}", elem.to_string()),
|
Err(err) => println!("Unable to compile element: {err}\n{}", elem.to_string()),
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
|
pub mod navigation;
|
||||||
|
|
103
src/compiler/navigation.rs
Normal file
103
src/compiler/navigation.rs
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use crate::document::document::Document;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct NavEntry {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) path: Option<String>,
|
||||||
|
pub(crate) children: Option<HashMap<String, NavEntry>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Navigation {
|
||||||
|
pub(crate) entries: HashMap<String, NavEntry>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_navigation(docs: &Vec<Box<dyn Document>>) -> Result<Navigation, String> {
|
||||||
|
let mut nav = Navigation {
|
||||||
|
entries: HashMap::new(),
|
||||||
|
};
|
||||||
|
|
||||||
|
for doc in docs {
|
||||||
|
let cat = doc.get_variable("nav.category");
|
||||||
|
let subcat = doc.get_variable("nav.subcategory");
|
||||||
|
let title = doc
|
||||||
|
.get_variable("nav.title")
|
||||||
|
.or(doc.get_variable("doc.title"));
|
||||||
|
let path = doc.get_variable("compiler.output");
|
||||||
|
|
||||||
|
let (cat, title, path) = match (cat, title, path) {
|
||||||
|
(Some(cat), Some(title), Some(path)) => (cat, title, path),
|
||||||
|
_ => {
|
||||||
|
println!(
|
||||||
|
"Skipping navigation generation for `{}`",
|
||||||
|
doc.source().name()
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(subcat) = subcat {
|
||||||
|
// Get parent entry
|
||||||
|
let cat_name = cat.to_string();
|
||||||
|
let mut pent = match nav.entries.get_mut(cat_name.as_str()) {
|
||||||
|
Some(pent) => pent,
|
||||||
|
None => {
|
||||||
|
// Create parent entry
|
||||||
|
nav.entries.insert(
|
||||||
|
cat_name.clone(),
|
||||||
|
NavEntry {
|
||||||
|
name: cat_name.clone(),
|
||||||
|
path: None,
|
||||||
|
children: Some(HashMap::new()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
nav.entries.get_mut(cat_name.as_str()).unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Insert into parent
|
||||||
|
let subcat_name = subcat.to_string();
|
||||||
|
if let Some(previous) = pent.children.as_mut().unwrap().insert(
|
||||||
|
subcat_name.clone(),
|
||||||
|
NavEntry {
|
||||||
|
name: subcat_name.clone(),
|
||||||
|
path: Some(path.to_string()),
|
||||||
|
children: None,
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
return Err(format!(
|
||||||
|
"Duplicate subcategory:\n{subcat:#?}\nclashes with:\n{previous:#?}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Get entry
|
||||||
|
let cat_name = cat.to_string();
|
||||||
|
let mut ent = match nav.entries.get_mut(cat_name.as_str()) {
|
||||||
|
Some(ent) => ent,
|
||||||
|
None => {
|
||||||
|
// Create parent entry
|
||||||
|
nav.entries.insert(
|
||||||
|
cat_name.clone(),
|
||||||
|
NavEntry {
|
||||||
|
name: cat_name.clone(),
|
||||||
|
path: None,
|
||||||
|
children: Some(HashMap::new()),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
nav.entries.get_mut(cat_name.as_str()).unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(path) = ent.path.as_ref() {
|
||||||
|
return Err(format!(
|
||||||
|
"Duplicate category:\n{subcat:#?}\nwith previous path:\n{path}"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
ent.path = Some(path.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(nav)
|
||||||
|
}
|
105
src/main.rs
105
src/main.rs
|
@ -11,6 +11,10 @@ use std::process::ExitCode;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use compiler::compiler::Compiler;
|
use compiler::compiler::Compiler;
|
||||||
|
use compiler::compiler::Target;
|
||||||
|
use compiler::navigation::create_navigation;
|
||||||
|
use compiler::navigation::Navigation;
|
||||||
|
use document::document::Document;
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
use parser::langparser::LangParser;
|
use parser::langparser::LangParser;
|
||||||
use parser::parser::Parser;
|
use parser::parser::Parser;
|
||||||
|
@ -38,15 +42,41 @@ NML version: 0.4\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process(
|
fn compile(
|
||||||
parser: &LangParser,
|
target: Target,
|
||||||
db_path: &Option<String>,
|
doc: &Box<dyn Document>,
|
||||||
input: &String,
|
|
||||||
output: &String,
|
output: &String,
|
||||||
debug_opts: &Vec<String>,
|
db_path: &Option<String>,
|
||||||
|
navigation: &Navigation,
|
||||||
multi_mode: bool,
|
multi_mode: bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
println!("Processing {input}...");
|
let compiler = Compiler::new(target, db_path.clone());
|
||||||
|
|
||||||
|
// Get output from file
|
||||||
|
if multi_mode {
|
||||||
|
let out_file = match doc.get_variable("compiler.output") {
|
||||||
|
None => {
|
||||||
|
eprintln!("Missing required variable `compiler.output` for multifile mode");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Some(var) => output.clone() + "/" + var.to_string().as_str(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let out = compiler.compile(navigation, doc.as_ref());
|
||||||
|
std::fs::write(out_file, out).is_ok()
|
||||||
|
} else {
|
||||||
|
let out = compiler.compile(navigation, doc.as_ref());
|
||||||
|
std::fs::write(output, out).is_ok()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(
|
||||||
|
input: &String,
|
||||||
|
debug_opts: &Vec<String>,
|
||||||
|
) -> Result<Box<dyn Document<'static>>, String> {
|
||||||
|
println!("Parsing {input}...");
|
||||||
|
let parser = LangParser::default();
|
||||||
|
|
||||||
// Parse
|
// Parse
|
||||||
let source = SourceFile::new(input.to_string(), None).unwrap();
|
let source = SourceFile::new(input.to_string(), None).unwrap();
|
||||||
let doc = parser.parse(Rc::new(source), None);
|
let doc = parser.parse(Rc::new(source), None);
|
||||||
|
@ -78,28 +108,10 @@ fn process(
|
||||||
}
|
}
|
||||||
|
|
||||||
if parser.has_error() {
|
if parser.has_error() {
|
||||||
println!("Compilation aborted due to errors while parsing");
|
return Err("Parsing failed aborted due to errors while parsing".to_string())
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let compiler = Compiler::new(compiler::compiler::Target::HTML, db_path.clone());
|
Ok(doc)
|
||||||
|
|
||||||
// Get output from file
|
|
||||||
if multi_mode {
|
|
||||||
let out_file = match doc.get_variable("compiler.output") {
|
|
||||||
None => {
|
|
||||||
eprintln!("Missing required variable `compiler.output` for multifile mode");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Some(var) => output.clone() + "/" + var.to_string().as_str(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let out = compiler.compile(doc.as_ref());
|
|
||||||
std::fs::write(out_file, out).is_ok()
|
|
||||||
} else {
|
|
||||||
let out = compiler.compile(doc.as_ref());
|
|
||||||
std::fs::write(output, out).is_ok()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
|
@ -164,7 +176,8 @@ fn main() -> ExitCode {
|
||||||
|
|
||||||
let debug_opts = matches.opt_strs("z");
|
let debug_opts = matches.opt_strs("z");
|
||||||
let db_path = matches.opt_str("d");
|
let db_path = matches.opt_str("d");
|
||||||
let parser = LangParser::default();
|
|
||||||
|
let mut docs = vec![];
|
||||||
|
|
||||||
if input_meta.is_dir() {
|
if input_meta.is_dir() {
|
||||||
if db_path.is_none() {
|
if db_path.is_none() {
|
||||||
|
@ -208,14 +221,44 @@ fn main() -> ExitCode {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if !process(&parser, &db_path, &path, &output, &debug_opts, true) {
|
match parse(&path, &debug_opts)
|
||||||
eprintln!("Processing aborted");
|
{
|
||||||
|
Ok(doc) => docs.push(doc),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if !process(&parser, &db_path, &input, &output, &debug_opts, false) {
|
match parse(&input, &debug_opts)
|
||||||
eprintln!("Processing aborted");
|
{
|
||||||
|
Ok(doc) => docs.push(doc),
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build navigation
|
||||||
|
let navigation = match create_navigation(&docs)
|
||||||
|
{
|
||||||
|
Ok(nav) => nav,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{e}");
|
||||||
|
return ExitCode::FAILURE;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
println!("{navigation:#?}");
|
||||||
|
|
||||||
|
let multi_mode = input_meta.is_dir();
|
||||||
|
for doc in docs
|
||||||
|
{
|
||||||
|
if !compile(Target::HTML, &doc, &output, &db_path, &navigation, multi_mode)
|
||||||
|
{
|
||||||
|
eprintln!("Compilation failed, processing aborted");
|
||||||
return ExitCode::FAILURE;
|
return ExitCode::FAILURE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue