Compare commits
2 commits
d6e6dbd660
...
35408f03b1
Author | SHA1 | Date | |
---|---|---|---|
35408f03b1 | |||
2211c44ee0 |
8 changed files with 339 additions and 170 deletions
|
@ -1,3 +1,4 @@
|
||||||
pub mod compiler;
|
pub mod compiler;
|
||||||
pub mod navigation;
|
pub mod navigation;
|
||||||
|
pub mod process;
|
||||||
pub mod postprocess;
|
pub mod postprocess;
|
||||||
|
|
|
@ -19,7 +19,7 @@ impl PostProcess {
|
||||||
/// Applies postprocessing to a [`CompiledDocument`]
|
/// Applies postprocessing to a [`CompiledDocument`]
|
||||||
pub fn apply(
|
pub fn apply(
|
||||||
&self,
|
&self,
|
||||||
target: Target,
|
_target: Target,
|
||||||
list: &Vec<(RefCell<CompiledDocument>, Option<PostProcess>)>,
|
list: &Vec<(RefCell<CompiledDocument>, Option<PostProcess>)>,
|
||||||
doc: &RefCell<CompiledDocument>,
|
doc: &RefCell<CompiledDocument>,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
|
@ -35,7 +35,7 @@ impl PostProcess {
|
||||||
if let Some(found) = doc.borrow().references.get(name) {
|
if let Some(found) = doc.borrow().references.get(name) {
|
||||||
// Check for duplicates
|
// Check for duplicates
|
||||||
if let Some((_, previous_doc)) = &found_ref {
|
if let Some((_, previous_doc)) = &found_ref {
|
||||||
return Err(format!("Cannot use an unspecific reference for reference named: `{found}`. Found in document `{}` but also in `{}`. Specify the source of the reference to resolve the conflict.", previous_doc.borrow().input, doc.borrow().input));
|
return Err(format!("Cannot use an unspecific reference for reference named: `{name}`. Found in document `{}` but also in `{}`. Specify the source of the reference to resolve the conflict.", previous_doc.borrow().input, doc.borrow().input));
|
||||||
}
|
}
|
||||||
|
|
||||||
found_ref = Some((found.clone(), &doc));
|
found_ref = Some((found.clone(), &doc));
|
||||||
|
@ -71,7 +71,7 @@ impl PostProcess {
|
||||||
"Unable to get the output. Aborting postprocessing."
|
"Unable to get the output. Aborting postprocessing."
|
||||||
))?;
|
))?;
|
||||||
let insert_content = format!("{found_path}#{found_ref}");
|
let insert_content = format!("{found_path}#{found_ref}");
|
||||||
content.insert_str(pos - offset, insert_content.as_str());
|
content.insert_str(pos + offset, insert_content.as_str());
|
||||||
offset += insert_content.len();
|
offset += insert_content.len();
|
||||||
} else {
|
} else {
|
||||||
return Err(format!("Cannot find reference `{cross_ref}` from document `{}`. Aborting postprocessing.", doc.borrow().input));
|
return Err(format!("Cannot find reference `{cross_ref}` from document `{}`. Aborting postprocessing.", doc.borrow().input));
|
||||||
|
|
195
src/compiler/process.rs
Normal file
195
src/compiler/process.rs
Normal file
|
@ -0,0 +1,195 @@
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::time::UNIX_EPOCH;
|
||||||
|
|
||||||
|
use rusqlite::Connection;
|
||||||
|
|
||||||
|
use crate::document::document::Document;
|
||||||
|
use crate::parser::langparser::LangParser;
|
||||||
|
use crate::parser::parser::Parser;
|
||||||
|
use crate::parser::parser::ParserState;
|
||||||
|
use crate::parser::source::Source;
|
||||||
|
use crate::parser::source::SourceFile;
|
||||||
|
|
||||||
|
use super::compiler::CompiledDocument;
|
||||||
|
use super::compiler::Compiler;
|
||||||
|
use super::compiler::Target;
|
||||||
|
use super::postprocess::PostProcess;
|
||||||
|
|
||||||
|
/// Parses a source file into a document
|
||||||
|
fn parse(
|
||||||
|
parser: &LangParser,
|
||||||
|
source: Rc<dyn Source>,
|
||||||
|
debug_opts: &Vec<String>,
|
||||||
|
) -> Result<Box<dyn Document<'static>>, String> {
|
||||||
|
// Parse
|
||||||
|
//let source = SourceFile::new(input.to_string(), None).unwrap();
|
||||||
|
let (doc, _) = parser.parse(ParserState::new(parser, None), source.clone(), None);
|
||||||
|
|
||||||
|
if debug_opts.contains(&"ast".to_string()) {
|
||||||
|
println!("-- BEGIN AST DEBUGGING --");
|
||||||
|
doc.content()
|
||||||
|
.borrow()
|
||||||
|
.iter()
|
||||||
|
.for_each(|elem| println!("{elem:#?}"));
|
||||||
|
println!("-- END AST DEBUGGING --");
|
||||||
|
}
|
||||||
|
if debug_opts.contains(&"ref".to_string()) {
|
||||||
|
println!("-- BEGIN REFERENCES DEBUGGING --");
|
||||||
|
let sc = doc.scope().borrow();
|
||||||
|
sc.referenceable.iter().for_each(|(name, reference)| {
|
||||||
|
println!(" - {name}: `{:#?}`", doc.get_from_reference(reference));
|
||||||
|
});
|
||||||
|
println!("-- END REFERENCES DEBUGGING --");
|
||||||
|
}
|
||||||
|
if debug_opts.contains(&"var".to_string()) {
|
||||||
|
println!("-- BEGIN VARIABLES DEBUGGING --");
|
||||||
|
let sc = doc.scope().borrow();
|
||||||
|
sc.variables.iter().for_each(|(_name, var)| {
|
||||||
|
println!(" - `{:#?}`", var);
|
||||||
|
});
|
||||||
|
println!("-- END VARIABLES DEBUGGING --");
|
||||||
|
}
|
||||||
|
|
||||||
|
if parser.has_error() {
|
||||||
|
return Err("Parsing failed due to errors while parsing".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(doc)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Takes a list of paths and processes it into a list of compiled documents
|
||||||
|
pub fn process(
|
||||||
|
target: Target,
|
||||||
|
files: Vec<PathBuf>,
|
||||||
|
db_path: &Option<String>,
|
||||||
|
force_rebuild: bool,
|
||||||
|
debug_opts: &Vec<String>,
|
||||||
|
) -> Result<Vec<(RefCell<CompiledDocument>, Option<PostProcess>)>, String> {
|
||||||
|
let mut compiled = vec![];
|
||||||
|
|
||||||
|
let current_dir = std::env::current_dir()
|
||||||
|
.map_err(|err| format!("Unable to get the current working directory: {err}"))?;
|
||||||
|
|
||||||
|
let con = db_path
|
||||||
|
.as_ref()
|
||||||
|
.map_or(Connection::open_in_memory(), |path| Connection::open(path))
|
||||||
|
.map_err(|err| format!("Unable to open connection to the database: {err}"))?;
|
||||||
|
CompiledDocument::init_cache(&con)
|
||||||
|
.map_err(|err| format!("Failed to initialize cached document table: {err}"))?;
|
||||||
|
|
||||||
|
let parser = LangParser::default();
|
||||||
|
for file in files {
|
||||||
|
let meta = std::fs::metadata(&file)
|
||||||
|
.map_err(|err| format!("Failed to get metadata for `{file:#?}`: {err}"))?;
|
||||||
|
|
||||||
|
let modified = meta
|
||||||
|
.modified()
|
||||||
|
.map_err(|err| format!("Unable to query modification time for `{file:#?}`: {err}"))?;
|
||||||
|
|
||||||
|
// Move to file's directory
|
||||||
|
let file_parent_path = file
|
||||||
|
.parent()
|
||||||
|
.ok_or(format!("Failed to get parent path for `{file:#?}`"))?;
|
||||||
|
std::env::set_current_dir(file_parent_path)
|
||||||
|
.map_err(|err| format!("Failed to move to path `{file_parent_path:#?}`: {err}"))?;
|
||||||
|
|
||||||
|
let parse_and_compile = || -> Result<(CompiledDocument, Option<PostProcess>), String> {
|
||||||
|
// Parse
|
||||||
|
let source = SourceFile::new(file.to_str().unwrap().to_string(), None).unwrap();
|
||||||
|
println!("Parsing {}...", source.name());
|
||||||
|
let doc = parse(&parser, Rc::new(source), debug_opts)?;
|
||||||
|
|
||||||
|
// Compile
|
||||||
|
let compiler = Compiler::new(target, db_path.clone());
|
||||||
|
let (mut compiled, postprocess) = compiler.compile(&*doc);
|
||||||
|
|
||||||
|
compiled.mtime = modified.duration_since(UNIX_EPOCH).unwrap().as_secs();
|
||||||
|
|
||||||
|
Ok((compiled, Some(postprocess)))
|
||||||
|
};
|
||||||
|
|
||||||
|
let (cdoc, post) = if force_rebuild {
|
||||||
|
parse_and_compile()?
|
||||||
|
} else {
|
||||||
|
match CompiledDocument::from_cache(&con, file.to_str().unwrap()) {
|
||||||
|
Some(compiled) => {
|
||||||
|
if compiled.mtime < modified.duration_since(UNIX_EPOCH).unwrap().as_secs() {
|
||||||
|
parse_and_compile()?
|
||||||
|
} else {
|
||||||
|
(compiled, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => parse_and_compile()?,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
compiled.push((RefCell::new(cdoc), post));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (doc, postprocess) in &compiled {
|
||||||
|
if postprocess.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post processing
|
||||||
|
let body = postprocess
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.apply(target, &compiled, &doc)?;
|
||||||
|
doc.borrow_mut().body = body;
|
||||||
|
|
||||||
|
// Insert into cache
|
||||||
|
doc.borrow().insert_cache(&con).map_err(|err| {
|
||||||
|
format!(
|
||||||
|
"Failed to insert compiled document from `{}` into cache: {err}",
|
||||||
|
doc.borrow().input
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::env::set_current_dir(current_dir)
|
||||||
|
.map_err(|err| format!("Failed to set current directory: {err}"))?;
|
||||||
|
|
||||||
|
Ok(compiled)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Processes sources from in-memory strings
|
||||||
|
/// This function is indented for testing
|
||||||
|
pub fn process_from_memory(target: Target, sources: Vec<String>) -> Result<Vec<(RefCell<CompiledDocument>, Option<PostProcess>)>, String> {
|
||||||
|
let mut compiled = vec![];
|
||||||
|
|
||||||
|
let parser = LangParser::default();
|
||||||
|
for (idx, content) in sources.iter().enumerate() {
|
||||||
|
let parse_and_compile = || -> Result<(CompiledDocument, Option<PostProcess>), String> {
|
||||||
|
// Parse
|
||||||
|
let source = SourceFile::with_content(format!("{idx}"), content.clone(), None);
|
||||||
|
let doc = parse(&parser, Rc::new(source), &vec![])?;
|
||||||
|
|
||||||
|
// Compile
|
||||||
|
let compiler = Compiler::new(target, None);
|
||||||
|
let (compiled, postprocess) = compiler.compile(&*doc);
|
||||||
|
|
||||||
|
Ok((compiled, Some(postprocess)))
|
||||||
|
};
|
||||||
|
|
||||||
|
let (cdoc, post) = parse_and_compile()?;
|
||||||
|
compiled.push((RefCell::new(cdoc), post));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (doc, postprocess) in &compiled {
|
||||||
|
if postprocess.is_none() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post processing
|
||||||
|
let body = postprocess
|
||||||
|
.as_ref()
|
||||||
|
.unwrap()
|
||||||
|
.apply(target, &compiled, &doc)?;
|
||||||
|
doc.borrow_mut().body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(compiled)
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ pub enum ElemReference {
|
||||||
Nested(usize, usize),
|
Nested(usize, usize),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub enum CrossReference {
|
pub enum CrossReference {
|
||||||
/// When the referenced document is unspecified
|
/// When the referenced document is unspecified
|
||||||
Unspecific(String),
|
Unspecific(String),
|
||||||
|
|
|
@ -45,7 +45,7 @@ pub mod tests {
|
||||||
use crate::parser::langparser::LangParser;
|
use crate::parser::langparser::LangParser;
|
||||||
use crate::parser::parser::Parser;
|
use crate::parser::parser::Parser;
|
||||||
use crate::parser::source::SourceFile;
|
use crate::parser::source::SourceFile;
|
||||||
use crate::ParserState;
|
use crate::parser::parser::ParserState;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn validate_refname_tests() {
|
fn validate_refname_tests() {
|
||||||
|
|
|
@ -91,13 +91,15 @@ impl Element for ExternalReference {
|
||||||
match compiler.target() {
|
match compiler.target() {
|
||||||
Target::HTML => {
|
Target::HTML => {
|
||||||
let mut result = "<a href=\"".to_string();
|
let mut result = "<a href=\"".to_string();
|
||||||
let refname = self.caption.as_ref().unwrap_or(match &self.reference {
|
|
||||||
CrossReference::Unspecific(name) => &name,
|
|
||||||
CrossReference::Specific(_, name) => &name,
|
|
||||||
});
|
|
||||||
|
|
||||||
compiler.insert_crossreference(cursor + result.len(), self.reference.clone());
|
compiler.insert_crossreference(cursor + result.len(), self.reference.clone());
|
||||||
result += format!("\">{}</a>", Compiler::sanitize(Target::HTML, refname)).as_str();
|
|
||||||
|
if let Some(caption) = &self.caption {
|
||||||
|
result +=
|
||||||
|
format!("\">{}</a>", Compiler::sanitize(Target::HTML, caption)).as_str();
|
||||||
|
} else {
|
||||||
|
result += format!("\">{}</a>", self.reference).as_str();
|
||||||
|
}
|
||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
_ => todo!(""),
|
_ => todo!(""),
|
||||||
|
@ -268,10 +270,6 @@ impl RegexRule for ReferenceRule {
|
||||||
.and_then(|(_, s)| Some(s));
|
.and_then(|(_, s)| Some(s));
|
||||||
|
|
||||||
if let Some(refdoc) = refdoc {
|
if let Some(refdoc) = refdoc {
|
||||||
if caption.is_none() {
|
|
||||||
return reports;
|
|
||||||
}
|
|
||||||
|
|
||||||
if refdoc.is_empty() {
|
if refdoc.is_empty() {
|
||||||
state.push(
|
state.push(
|
||||||
document,
|
document,
|
||||||
|
@ -305,3 +303,95 @@ impl RegexRule for ReferenceRule {
|
||||||
reports
|
reports
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::compiler::process::process_from_memory;
|
||||||
|
use crate::elements::paragraph::Paragraph;
|
||||||
|
use crate::elements::section::Section;
|
||||||
|
use crate::parser::langparser::LangParser;
|
||||||
|
use crate::parser::parser::Parser;
|
||||||
|
use crate::parser::source::SourceFile;
|
||||||
|
use crate::validate_document;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn parse_internal() {
|
||||||
|
let source = Rc::new(SourceFile::with_content(
|
||||||
|
"".to_string(),
|
||||||
|
r#"
|
||||||
|
#{ref} Referenceable section
|
||||||
|
|
||||||
|
§{ref}[caption=Section]
|
||||||
|
§{ref}[caption=Another]
|
||||||
|
"#
|
||||||
|
.to_string(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
let parser = LangParser::default();
|
||||||
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
|
validate_document!(doc.content().borrow(), 0,
|
||||||
|
Section;
|
||||||
|
Paragraph {
|
||||||
|
InternalReference { refname == "ref", caption == Some("Section".to_string()) };
|
||||||
|
InternalReference { refname == "ref", caption == Some("Another".to_string()) };
|
||||||
|
};
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn parse_external() {
|
||||||
|
let source = Rc::new(SourceFile::with_content(
|
||||||
|
"".to_string(),
|
||||||
|
r#"
|
||||||
|
§{DocA#ref}[caption=Section]
|
||||||
|
§{DocB#ref}
|
||||||
|
§{#ref}[caption='ref' from any document]
|
||||||
|
"#
|
||||||
|
.to_string(),
|
||||||
|
None,
|
||||||
|
));
|
||||||
|
let parser = LangParser::default();
|
||||||
|
let (doc, _) = parser.parse(ParserState::new(&parser, None), source, None);
|
||||||
|
|
||||||
|
validate_document!(doc.content().borrow(), 0,
|
||||||
|
Paragraph {
|
||||||
|
ExternalReference { reference == CrossReference::Specific("DocA".into(), "ref".into()), caption == Some("Section".to_string()) };
|
||||||
|
ExternalReference { reference == CrossReference::Specific("DocB".into(), "ref".into()), caption == None::<String> };
|
||||||
|
ExternalReference { reference == CrossReference::Unspecific("ref".into()), caption == Some("'ref' from any document".to_string()) };
|
||||||
|
};
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
pub fn test_external()
|
||||||
|
{
|
||||||
|
let result = process_from_memory(Target::HTML, vec![
|
||||||
|
r#"
|
||||||
|
@html.page_title = 0
|
||||||
|
@compiler.output = a.html
|
||||||
|
|
||||||
|
#{ref} Referenceable section
|
||||||
|
"#.into(),
|
||||||
|
r#"
|
||||||
|
@html.page_title = 1
|
||||||
|
@compiler.output = b.html
|
||||||
|
|
||||||
|
§{#ref}
|
||||||
|
§{a#ref}
|
||||||
|
#{ref2} Another Referenceable section
|
||||||
|
"#.into(),
|
||||||
|
r#"
|
||||||
|
@html.page_title = 2
|
||||||
|
|
||||||
|
§{#ref}[caption=from 0]
|
||||||
|
§{#ref2}[caption=from 1]
|
||||||
|
"#.into(),
|
||||||
|
]).unwrap();
|
||||||
|
|
||||||
|
assert!(result[1].0.borrow().body.starts_with("<div class=\"content\"><p><a href=\"a.html#Referenceable_section\">#ref</a><a href=\"a.html#Referenceable_section\">a#ref</a></p>"));
|
||||||
|
assert!(result[2].0.borrow().body.starts_with("<div class=\"content\"><p><a href=\"a.html#Referenceable_section\">from 0</a><a href=\"b.html#Another_Referenceable_section\">from 1</a></p>"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
153
src/main.rs
153
src/main.rs
|
@ -5,29 +5,16 @@ mod elements;
|
||||||
mod lua;
|
mod lua;
|
||||||
mod parser;
|
mod parser;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io::BufWriter;
|
use std::io::BufWriter;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::process::ExitCode;
|
use std::process::ExitCode;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::time::UNIX_EPOCH;
|
|
||||||
|
|
||||||
use compiler::compiler::CompiledDocument;
|
|
||||||
use compiler::compiler::Compiler;
|
|
||||||
use compiler::compiler::Target;
|
use compiler::compiler::Target;
|
||||||
use compiler::navigation::create_navigation;
|
use compiler::navigation::create_navigation;
|
||||||
use compiler::postprocess::PostProcess;
|
|
||||||
use document::document::Document;
|
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
use parser::langparser::LangParser;
|
|
||||||
use parser::parser::Parser;
|
|
||||||
use parser::parser::ParserState;
|
|
||||||
use rusqlite::Connection;
|
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::parser::source::SourceFile;
|
|
||||||
extern crate getopts;
|
extern crate getopts;
|
||||||
|
|
||||||
fn print_usage(program: &str, opts: Options) {
|
fn print_usage(program: &str, opts: Options) {
|
||||||
|
@ -49,142 +36,6 @@ NML version: 0.4\n"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse(
|
|
||||||
parser: &LangParser,
|
|
||||||
input: &str,
|
|
||||||
debug_opts: &Vec<String>,
|
|
||||||
) -> Result<Box<dyn Document<'static>>, String> {
|
|
||||||
println!("Parsing {input}...");
|
|
||||||
|
|
||||||
// Parse
|
|
||||||
let source = SourceFile::new(input.to_string(), None).unwrap();
|
|
||||||
let (doc, _) = parser.parse(ParserState::new(parser, None), Rc::new(source), None);
|
|
||||||
|
|
||||||
if debug_opts.contains(&"ast".to_string()) {
|
|
||||||
println!("-- BEGIN AST DEBUGGING --");
|
|
||||||
doc.content()
|
|
||||||
.borrow()
|
|
||||||
.iter()
|
|
||||||
.for_each(|elem| println!("{elem:#?}"));
|
|
||||||
println!("-- END AST DEBUGGING --");
|
|
||||||
}
|
|
||||||
if debug_opts.contains(&"ref".to_string()) {
|
|
||||||
println!("-- BEGIN REFERENCES DEBUGGING --");
|
|
||||||
let sc = doc.scope().borrow();
|
|
||||||
sc.referenceable.iter().for_each(|(name, reference)| {
|
|
||||||
println!(" - {name}: `{:#?}`", doc.get_from_reference(reference));
|
|
||||||
});
|
|
||||||
println!("-- END REFERENCES DEBUGGING --");
|
|
||||||
}
|
|
||||||
if debug_opts.contains(&"var".to_string()) {
|
|
||||||
println!("-- BEGIN VARIABLES DEBUGGING --");
|
|
||||||
let sc = doc.scope().borrow();
|
|
||||||
sc.variables.iter().for_each(|(_name, var)| {
|
|
||||||
println!(" - `{:#?}`", var);
|
|
||||||
});
|
|
||||||
println!("-- END VARIABLES DEBUGGING --");
|
|
||||||
}
|
|
||||||
|
|
||||||
if parser.has_error() {
|
|
||||||
return Err("Parsing failed due to errors while parsing".to_string());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(doc)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn process(
|
|
||||||
target: Target,
|
|
||||||
files: Vec<PathBuf>,
|
|
||||||
db_path: &Option<String>,
|
|
||||||
force_rebuild: bool,
|
|
||||||
debug_opts: &Vec<String>,
|
|
||||||
) -> Result<Vec<(RefCell<CompiledDocument>, Option<PostProcess>)>, String> {
|
|
||||||
let mut compiled = vec![];
|
|
||||||
|
|
||||||
let current_dir = std::env::current_dir()
|
|
||||||
.map_err(|err| format!("Unable to get the current working directory: {err}"))?;
|
|
||||||
|
|
||||||
let con = db_path
|
|
||||||
.as_ref()
|
|
||||||
.map_or(Connection::open_in_memory(), |path| Connection::open(path))
|
|
||||||
.map_err(|err| format!("Unable to open connection to the database: {err}"))?;
|
|
||||||
CompiledDocument::init_cache(&con)
|
|
||||||
.map_err(|err| format!("Failed to initialize cached document table: {err}"))?;
|
|
||||||
|
|
||||||
let parser = LangParser::default();
|
|
||||||
for file in files {
|
|
||||||
let meta = std::fs::metadata(&file)
|
|
||||||
.map_err(|err| format!("Failed to get metadata for `{file:#?}`: {err}"))?;
|
|
||||||
|
|
||||||
let modified = meta
|
|
||||||
.modified()
|
|
||||||
.map_err(|err| format!("Unable to query modification time for `{file:#?}`: {err}"))?;
|
|
||||||
|
|
||||||
// Move to file's directory
|
|
||||||
let file_parent_path = file
|
|
||||||
.parent()
|
|
||||||
.ok_or(format!("Failed to get parent path for `{file:#?}`"))?;
|
|
||||||
std::env::set_current_dir(file_parent_path)
|
|
||||||
.map_err(|err| format!("Failed to move to path `{file_parent_path:#?}`: {err}"))?;
|
|
||||||
|
|
||||||
let parse_and_compile = || -> Result<(CompiledDocument, Option<PostProcess>), String> {
|
|
||||||
// Parse
|
|
||||||
let doc = parse(&parser, file.to_str().unwrap(), debug_opts)?;
|
|
||||||
|
|
||||||
// Compile
|
|
||||||
let compiler = Compiler::new(target, db_path.clone());
|
|
||||||
let (mut compiled, postprocess) = compiler.compile(&*doc);
|
|
||||||
|
|
||||||
compiled.mtime = modified.duration_since(UNIX_EPOCH).unwrap().as_secs();
|
|
||||||
|
|
||||||
Ok((compiled, Some(postprocess)))
|
|
||||||
};
|
|
||||||
|
|
||||||
let (cdoc, post) = if force_rebuild {
|
|
||||||
parse_and_compile()?
|
|
||||||
} else {
|
|
||||||
match CompiledDocument::from_cache(&con, file.to_str().unwrap()) {
|
|
||||||
Some(compiled) => {
|
|
||||||
if compiled.mtime < modified.duration_since(UNIX_EPOCH).unwrap().as_secs() {
|
|
||||||
parse_and_compile()?
|
|
||||||
} else {
|
|
||||||
(compiled, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => parse_and_compile()?,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
compiled.push((RefCell::new(cdoc), post));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (doc, postprocess) in &compiled {
|
|
||||||
if postprocess.is_none() {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Post processing
|
|
||||||
let body = postprocess
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.apply(target, &compiled, &doc)?;
|
|
||||||
doc.borrow_mut().body = body;
|
|
||||||
|
|
||||||
// Insert into cache
|
|
||||||
doc.borrow().insert_cache(&con).map_err(|err| {
|
|
||||||
format!(
|
|
||||||
"Failed to insert compiled document from `{}` into cache: {err}",
|
|
||||||
doc.borrow().input
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::env::set_current_dir(current_dir)
|
|
||||||
.map_err(|err| format!("Failed to set current directory: {err}"))?;
|
|
||||||
|
|
||||||
Ok(compiled)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() -> ExitCode {
|
fn main() -> ExitCode {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
let program = args[0].clone();
|
let program = args[0].clone();
|
||||||
|
@ -362,7 +213,9 @@ fn main() -> ExitCode {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse, compile using the cache
|
// Parse, compile using the cache
|
||||||
let processed = match process(Target::HTML, files, &db_path, force_rebuild, &debug_opts) {
|
let processed =
|
||||||
|
match compiler::process::process(Target::HTML, files, &db_path, force_rebuild, &debug_opts)
|
||||||
|
{
|
||||||
Ok(processed) => processed,
|
Ok(processed) => processed,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("{e}");
|
eprintln!("{e}");
|
||||||
|
|
30
style.css
30
style.css
|
@ -137,6 +137,11 @@ a.inline-code {
|
||||||
content: "–";
|
content: "–";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sections */
|
||||||
|
a.section-link {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* Code blocks */
|
/* Code blocks */
|
||||||
div.code-block-title {
|
div.code-block-title {
|
||||||
background-color: #20202a;
|
background-color: #20202a;
|
||||||
|
@ -241,3 +246,28 @@ a:hover.medium-ref img {
|
||||||
|
|
||||||
box-shadow: 0px 0px 6px 2px rgba(0, 0, 0, 0.75);
|
box-shadow: 0px 0px 6px 2px rgba(0, 0, 0, 0.75);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Blockquote */
|
||||||
|
blockquote {
|
||||||
|
margin-left: 0.2em;
|
||||||
|
padding-left: 0.6em;
|
||||||
|
|
||||||
|
border-left: 4px solid #0ff08b;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote p::before {
|
||||||
|
content: '\201C';
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote p::after {
|
||||||
|
content: '\201D';
|
||||||
|
}
|
||||||
|
|
||||||
|
.blockquote-author:before {
|
||||||
|
content: '—';
|
||||||
|
}
|
||||||
|
|
||||||
|
.blockquote-author {
|
||||||
|
margin-left: 0.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue