Cross-references
This commit is contained in:
parent
d8fd2feefb
commit
d6e6dbd660
23 changed files with 433 additions and 136 deletions
|
@ -35,9 +35,9 @@ cargo build --release --bin nml
|
|||
- [x] LaTeX rendering
|
||||
- [x] Graphviz rendering
|
||||
- [x] Media
|
||||
- [ ] References
|
||||
- [ ] Navigation
|
||||
- [ ] Cross-Document references
|
||||
- [x] References
|
||||
- [x] Navigation
|
||||
- [x] Cross-Document references
|
||||
- [ ] Complete Lua api
|
||||
- [ ] Documentation
|
||||
- [ ] Table
|
||||
|
|
|
@ -6,10 +6,13 @@ use std::rc::Rc;
|
|||
|
||||
use rusqlite::Connection;
|
||||
|
||||
use crate::document::document::CrossReference;
|
||||
use crate::document::document::Document;
|
||||
use crate::document::document::ElemReference;
|
||||
use crate::document::variable::Variable;
|
||||
|
||||
use super::postprocess::PostProcess;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Target {
|
||||
HTML,
|
||||
|
@ -21,8 +24,9 @@ pub struct Compiler {
|
|||
target: Target,
|
||||
cache: Option<RefCell<Connection>>,
|
||||
reference_count: RefCell<HashMap<String, HashMap<String, usize>>>,
|
||||
// TODO: External references, i.e resolved later
|
||||
sections_counter: RefCell<Vec<usize>>,
|
||||
|
||||
unresolved_references: RefCell<Vec<(usize, CrossReference)>>,
|
||||
}
|
||||
|
||||
impl Compiler {
|
||||
|
@ -39,6 +43,7 @@ impl Compiler {
|
|||
cache: cache.map(|con| RefCell::new(con)),
|
||||
reference_count: RefCell::new(HashMap::new()),
|
||||
sections_counter: RefCell::new(vec![]),
|
||||
unresolved_references: RefCell::new(vec![]),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +120,13 @@ impl Compiler {
|
|||
}
|
||||
}
|
||||
|
||||
/// Inserts a new crossreference
|
||||
pub fn insert_crossreference(&self, pos: usize, reference: CrossReference) {
|
||||
self.unresolved_references
|
||||
.borrow_mut()
|
||||
.push((pos, reference));
|
||||
}
|
||||
|
||||
pub fn target(&self) -> Target { self.target }
|
||||
|
||||
pub fn cache(&self) -> Option<RefMut<'_, Connection>> {
|
||||
|
@ -179,7 +191,7 @@ impl Compiler {
|
|||
result
|
||||
}
|
||||
|
||||
pub fn compile(&self, document: &dyn Document) -> CompiledDocument {
|
||||
pub fn compile(&self, document: &dyn Document) -> (CompiledDocument, PostProcess) {
|
||||
let borrow = document.content().borrow();
|
||||
|
||||
// Header
|
||||
|
@ -190,7 +202,7 @@ impl Compiler {
|
|||
for i in 0..borrow.len() {
|
||||
let elem = &borrow[i];
|
||||
|
||||
match elem.compile(self, document) {
|
||||
match elem.compile(self, document, body.len()) {
|
||||
Ok(result) => body.push_str(result.as_str()),
|
||||
Err(err) => println!("Unable to compile element: {err}\n{elem:#?}"),
|
||||
}
|
||||
|
@ -209,14 +221,35 @@ impl Compiler {
|
|||
.map(|(key, var)| (key.clone(), var.to_string()))
|
||||
.collect::<HashMap<String, String>>();
|
||||
|
||||
CompiledDocument {
|
||||
// References
|
||||
let references = document
|
||||
.scope()
|
||||
.borrow_mut()
|
||||
.referenceable
|
||||
.iter()
|
||||
.map(|(key, reference)| {
|
||||
let elem = document.get_from_reference(reference).unwrap();
|
||||
let refid = self.reference_id(document, *reference);
|
||||
|
||||
(key.clone(), elem.refid(self, refid))
|
||||
})
|
||||
.collect::<HashMap<String, String>>();
|
||||
|
||||
let postprocess = PostProcess {
|
||||
resolve_references: self.unresolved_references.replace(vec![]),
|
||||
};
|
||||
|
||||
let cdoc = CompiledDocument {
|
||||
input: document.source().name().clone(),
|
||||
mtime: 0,
|
||||
variables,
|
||||
references,
|
||||
header,
|
||||
body,
|
||||
footer,
|
||||
}
|
||||
};
|
||||
|
||||
(cdoc, postprocess)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -227,12 +260,14 @@ pub struct CompiledDocument {
|
|||
/// Modification time (i.e seconds since last epoch)
|
||||
pub mtime: u64,
|
||||
|
||||
// TODO: Also store exported references
|
||||
// so they can be referenced from elsewhere
|
||||
// This will also require rebuilding in case some exported references have changed...
|
||||
/// Variables exported to string, so they can be querried later
|
||||
/// All the variables defined in the document
|
||||
/// with values mapped by [`Variable::to_string()`]
|
||||
pub variables: HashMap<String, String>,
|
||||
|
||||
/// All the referenceable elements in the document
|
||||
/// with values mapped by [`ReferenceableElement::refid()`]
|
||||
pub references: HashMap<String, String>,
|
||||
|
||||
/// Compiled document's header
|
||||
pub header: String,
|
||||
/// Compiled document's body
|
||||
|
@ -245,10 +280,11 @@ impl CompiledDocument {
|
|||
pub fn get_variable(&self, name: &str) -> Option<&String> { self.variables.get(name) }
|
||||
|
||||
fn sql_table() -> &'static str {
|
||||
"CREATE TABLE IF NOT EXISTS compiled_documents (
|
||||
"CREATE TABLE IF NOT EXISTS compiled_documents(
|
||||
input TEXT PRIMARY KEY,
|
||||
mtime INTEGER NOT NULL,
|
||||
variables TEXT NOT NULL,
|
||||
internal_references TEXT NOT NULL,
|
||||
header TEXT NOT NULL,
|
||||
body TEXT NOT NULL,
|
||||
footer TEXT NOT NULL
|
||||
|
@ -258,7 +294,7 @@ impl CompiledDocument {
|
|||
fn sql_get_query() -> &'static str { "SELECT * FROM compiled_documents WHERE input = (?1)" }
|
||||
|
||||
fn sql_insert_query() -> &'static str {
|
||||
"INSERT OR REPLACE INTO compiled_documents (input, mtime, variables, header, body, footer) VALUES (?1, ?2, ?3, ?4, ?5, ?6)"
|
||||
"INSERT OR REPLACE INTO compiled_documents (input, mtime, variables, internal_references, header, body, footer) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"
|
||||
}
|
||||
|
||||
pub fn init_cache(con: &Connection) -> Result<usize, rusqlite::Error> {
|
||||
|
@ -271,9 +307,10 @@ impl CompiledDocument {
|
|||
input: input.to_string(),
|
||||
mtime: row.get_unwrap::<_, u64>(1),
|
||||
variables: serde_json::from_str(row.get_unwrap::<_, String>(2).as_str()).unwrap(),
|
||||
header: row.get_unwrap::<_, String>(3),
|
||||
body: row.get_unwrap::<_, String>(4),
|
||||
footer: row.get_unwrap::<_, String>(5),
|
||||
references: serde_json::from_str(row.get_unwrap::<_, String>(3).as_str()).unwrap(),
|
||||
header: row.get_unwrap::<_, String>(4),
|
||||
body: row.get_unwrap::<_, String>(5),
|
||||
footer: row.get_unwrap::<_, String>(6),
|
||||
})
|
||||
})
|
||||
.ok()
|
||||
|
@ -287,6 +324,7 @@ impl CompiledDocument {
|
|||
&self.input,
|
||||
&self.mtime,
|
||||
serde_json::to_string(&self.variables).unwrap(),
|
||||
serde_json::to_string(&self.references).unwrap(),
|
||||
&self.header,
|
||||
&self.body,
|
||||
&self.footer,
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub mod compiler;
|
||||
pub mod navigation;
|
||||
pub mod postprocess;
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::compiler::compiler::Compiler;
|
||||
|
||||
use super::compiler::CompiledDocument;
|
||||
use super::compiler::Target;
|
||||
use super::postprocess::PostProcess;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct NavEntry {
|
||||
|
@ -13,10 +15,14 @@ pub struct NavEntry {
|
|||
|
||||
impl NavEntry {
|
||||
// FIXME: Sanitize
|
||||
pub fn compile(&self, target: Target, doc: &CompiledDocument) -> String {
|
||||
pub fn compile(&self, target: Target, doc: &RefCell<CompiledDocument>) -> String {
|
||||
let doc_borrow = doc.borrow();
|
||||
let categories = vec![
|
||||
doc.get_variable("nav.category").map_or("", |s| s.as_str()),
|
||||
doc.get_variable("nav.subcategory")
|
||||
doc_borrow
|
||||
.get_variable("nav.category")
|
||||
.map_or("", |s| s.as_str()),
|
||||
doc_borrow
|
||||
.get_variable("nav.subcategory")
|
||||
.map_or("", |s| s.as_str()),
|
||||
];
|
||||
|
||||
|
@ -101,7 +107,9 @@ impl NavEntry {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, String> {
|
||||
pub fn create_navigation(
|
||||
docs: &Vec<(RefCell<CompiledDocument>, Option<PostProcess>)>,
|
||||
) -> Result<NavEntry, String> {
|
||||
let mut nav = NavEntry {
|
||||
entries: vec![],
|
||||
children: HashMap::new(),
|
||||
|
@ -110,19 +118,20 @@ pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, Strin
|
|||
// All paths (for duplicate checking)
|
||||
let mut all_paths = HashMap::new();
|
||||
|
||||
for doc in docs {
|
||||
let cat = doc.get_variable("nav.category");
|
||||
let subcat = doc.get_variable("nav.subcategory");
|
||||
let title = doc
|
||||
for (doc, _) in docs {
|
||||
let doc_borrow = doc.borrow();
|
||||
let cat = doc_borrow.get_variable("nav.category");
|
||||
let subcat = doc_borrow.get_variable("nav.subcategory");
|
||||
let title = doc_borrow
|
||||
.get_variable("nav.title")
|
||||
.or(doc.get_variable("doc.title"));
|
||||
let previous = doc.get_variable("nav.previous").map(|s| s.clone());
|
||||
let path = doc.get_variable("compiler.output");
|
||||
.or(doc_borrow.get_variable("doc.title"));
|
||||
let previous = doc_borrow.get_variable("nav.previous").map(|s| s.clone());
|
||||
let path = doc_borrow.get_variable("compiler.output");
|
||||
|
||||
let (title, path) = match (title, path) {
|
||||
(Some(title), Some(path)) => (title, path),
|
||||
_ => {
|
||||
eprintln!("Skipping navigation generation for `{}`, must have a defined `@nav.title` and `@compiler.output`", doc.input);
|
||||
eprintln!("Skipping navigation generation for `{}`, must have a defined `@nav.title` and `@compiler.output`", doc_borrow.input);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
@ -134,7 +143,7 @@ pub fn create_navigation(docs: &Vec<CompiledDocument>) -> Result<NavEntry, Strin
|
|||
None => {
|
||||
eprintln!(
|
||||
"Skipping `{}`: No `@nav.category`, but `@nav.subcategory` is set",
|
||||
doc.input
|
||||
doc_borrow.input
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
|
83
src/compiler/postprocess.rs
Normal file
83
src/compiler/postprocess.rs
Normal file
|
@ -0,0 +1,83 @@
|
|||
use std::cell::RefCell;
|
||||
|
||||
use crate::document::document::CrossReference;
|
||||
|
||||
use super::compiler::CompiledDocument;
|
||||
use super::compiler::Target;
|
||||
|
||||
/// Represents the list of tasks that have to be ran after the document has been compiled and the
|
||||
/// compiled document list has been built. Every task is stored with a raw byte position in the
|
||||
/// compiled document's body. The position represents the original position and thus should be
|
||||
/// offset accordingly to other postprocessing tasks.
|
||||
pub struct PostProcess {
|
||||
/// List of references to resolve i.e insert the resolved refname at a certain byte position
|
||||
/// in the document's body
|
||||
pub resolve_references: Vec<(usize, CrossReference)>,
|
||||
}
|
||||
|
||||
impl PostProcess {
|
||||
/// Applies postprocessing to a [`CompiledDocument`]
|
||||
pub fn apply(
|
||||
&self,
|
||||
target: Target,
|
||||
list: &Vec<(RefCell<CompiledDocument>, Option<PostProcess>)>,
|
||||
doc: &RefCell<CompiledDocument>,
|
||||
) -> Result<String, String> {
|
||||
let mut content = doc.borrow().body.clone();
|
||||
|
||||
let mut offset = 0;
|
||||
for (pos, cross_ref) in &self.resolve_references {
|
||||
// Cross-references
|
||||
let mut found_ref: Option<(String, &RefCell<CompiledDocument>)> = None;
|
||||
match cross_ref {
|
||||
CrossReference::Unspecific(name) => {
|
||||
for (doc, _) in list {
|
||||
if let Some(found) = doc.borrow().references.get(name) {
|
||||
// Check for duplicates
|
||||
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));
|
||||
}
|
||||
|
||||
found_ref = Some((found.clone(), &doc));
|
||||
}
|
||||
}
|
||||
}
|
||||
CrossReference::Specific(doc_name, name) => {
|
||||
let ref_doc = list.iter().find(|(doc, _)| {
|
||||
let doc_borrow = doc.borrow();
|
||||
if let Some(outname) = doc_borrow.variables.get("compiler.output") {
|
||||
// Strip extension
|
||||
let split_at = outname.rfind('.').unwrap_or(outname.len());
|
||||
return doc_name == outname.split_at(split_at).0;
|
||||
}
|
||||
|
||||
false
|
||||
});
|
||||
if ref_doc.is_none() {
|
||||
return Err(format!(
|
||||
"Cannot find document `{doc_name}` for reference `{name}` in `{}`",
|
||||
doc.borrow().input
|
||||
));
|
||||
}
|
||||
|
||||
if let Some(found) = ref_doc.unwrap().0.borrow().references.get(name) {
|
||||
found_ref = Some((found.clone(), &ref_doc.unwrap().0));
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some((found_ref, found_doc)) = &found_ref {
|
||||
let found_borrow = found_doc.borrow();
|
||||
let found_path = found_borrow.get_variable("compiler.output").ok_or(format!(
|
||||
"Unable to get the output. Aborting postprocessing."
|
||||
))?;
|
||||
let insert_content = format!("{found_path}#{found_ref}");
|
||||
content.insert_str(pos - offset, insert_content.as_str());
|
||||
offset += insert_content.len();
|
||||
} else {
|
||||
return Err(format!("Cannot find reference `{cross_ref}` from document `{}`. Aborting postprocessing.", doc.borrow().input));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(content)
|
||||
}
|
||||
}
|
|
@ -4,13 +4,17 @@ use std::cell::RefMut;
|
|||
use std::collections::hash_map::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::parser::source::Source;
|
||||
|
||||
use super::element::Element;
|
||||
use super::element::ReferenceableElement;
|
||||
use super::variable::Variable;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// For references inside the current document
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
pub enum ElemReference {
|
||||
Direct(usize),
|
||||
|
||||
|
@ -18,10 +22,30 @@ pub enum ElemReference {
|
|||
Nested(usize, usize),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum CrossReference {
|
||||
/// When the referenced document is unspecified
|
||||
Unspecific(String),
|
||||
|
||||
/// When the referenced document is specified
|
||||
Specific(String, String),
|
||||
}
|
||||
|
||||
impl core::fmt::Display for CrossReference {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self
|
||||
{
|
||||
CrossReference::Unspecific(name) => write!(f, "#{name}"),
|
||||
CrossReference::Specific(doc_name, name) => write!(f, "{doc_name}#{name}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Scope {
|
||||
/// List of all referenceable elements in current scope.
|
||||
/// All elements in this should return a non empty
|
||||
/// All elements in this should return a non empty element
|
||||
/// when [`Element::as_referenceable`] is called
|
||||
pub referenceable: HashMap<String, ElemReference>,
|
||||
pub variables: HashMap<String, Rc<dyn Variable>>,
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use std::str::FromStr;
|
||||
|
||||
use crate::compiler::compiler::Compiler;
|
||||
use crate::elements::reference::Reference;
|
||||
use crate::elements::reference::InternalReference;
|
||||
use crate::parser::source::Token;
|
||||
use downcast_rs::impl_downcast;
|
||||
use downcast_rs::Downcast;
|
||||
|
@ -50,7 +50,7 @@ pub trait Element: Downcast + core::fmt::Debug {
|
|||
fn as_container(&self) -> Option<&dyn ContainerElement> { None }
|
||||
|
||||
/// Compiles element
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String>;
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document, cursor: usize) -> Result<String, String>;
|
||||
}
|
||||
impl_downcast!(Element);
|
||||
|
||||
|
@ -66,9 +66,13 @@ pub trait ReferenceableElement: Element {
|
|||
&self,
|
||||
compiler: &Compiler,
|
||||
document: &dyn Document,
|
||||
reference: &Reference,
|
||||
reference: &InternalReference,
|
||||
refid: usize,
|
||||
) -> Result<String, String>;
|
||||
|
||||
/// Gets the refid for a compiler. The refid is some key that can be used from an external
|
||||
/// document to reference this element.
|
||||
fn refid(&self, compiler: &Compiler, refid: usize) -> String;
|
||||
}
|
||||
|
||||
pub trait ContainerElement: Element {
|
||||
|
@ -89,7 +93,7 @@ impl Element for DocumentEnd {
|
|||
|
||||
fn element_name(&self) -> &'static str { "Document End" }
|
||||
|
||||
fn compile(&self, _compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, _compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
Ok(String::new())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ impl Element for Blockquote {
|
|||
|
||||
fn element_name(&self) -> &'static str { "Blockquote" }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document, cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
HTML => {
|
||||
let mut result = r#"<div class="blockquote-content">"#.to_string();
|
||||
|
@ -124,7 +124,7 @@ impl Element for Blockquote {
|
|||
|
||||
result += "<p>";
|
||||
for elem in &self.content {
|
||||
result += elem.compile(compiler, document)?.as_str();
|
||||
result += elem.compile(compiler, document, cursor+result.len())?.as_str();
|
||||
}
|
||||
result += "</p></blockquote>";
|
||||
if self.style.author_pos == After {
|
||||
|
|
|
@ -263,7 +263,7 @@ impl Element for Code {
|
|||
|
||||
fn element_name(&self) -> &'static str { "Code Block" }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
static CACHE_INIT: Once = Once::new();
|
||||
|
|
|
@ -25,7 +25,7 @@ impl Element for Comment {
|
|||
fn location(&self) -> &Token { &self.location }
|
||||
fn kind(&self) -> ElemKind { ElemKind::Invisible }
|
||||
fn element_name(&self) -> &'static str { "Comment" }
|
||||
fn compile(&self, _compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, _compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
Ok("".to_string())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,7 +111,7 @@ impl Element for Graphviz {
|
|||
|
||||
fn element_name(&self) -> &'static str { "Graphviz" }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
static CACHE_INIT: Once = Once::new();
|
||||
|
|
|
@ -232,7 +232,7 @@ impl Element for Layout {
|
|||
fn location(&self) -> &Token { &self.location }
|
||||
fn kind(&self) -> ElemKind { ElemKind::Block }
|
||||
fn element_name(&self) -> &'static str { "Layout" }
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
self.layout
|
||||
.compile(self.token, self.id, &self.properties, compiler, document)
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ impl Element for Link {
|
|||
fn location(&self) -> &Token { &self.location }
|
||||
fn kind(&self) -> ElemKind { ElemKind::Inline }
|
||||
fn element_name(&self) -> &'static str { "Link" }
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document, cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
let mut result = format!(
|
||||
|
@ -46,7 +46,7 @@ impl Element for Link {
|
|||
);
|
||||
|
||||
for elem in &self.display {
|
||||
result += elem.compile(compiler, document)?.as_str();
|
||||
result += elem.compile(compiler, document, cursor+result.len())?.as_str();
|
||||
}
|
||||
|
||||
result += "</a>";
|
||||
|
|
|
@ -48,7 +48,7 @@ impl Element for ListMarker {
|
|||
|
||||
fn element_name(&self) -> &'static str { "List Marker" }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => match (self.kind, self.numbered) {
|
||||
(MarkerKind::Close, true) => Ok("</ol>".to_string()),
|
||||
|
@ -76,12 +76,12 @@ impl Element for ListEntry {
|
|||
|
||||
fn element_name(&self) -> &'static str { "List Entry" }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document, cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
let mut result = "<li>".to_string();
|
||||
for elem in &self.content {
|
||||
result += elem.compile(compiler, document)?.as_str();
|
||||
result += elem.compile(compiler, document, cursor+result.len())?.as_str();
|
||||
}
|
||||
result += "</li>";
|
||||
Ok(result)
|
||||
|
|
|
@ -35,7 +35,7 @@ use crate::parser::util::PropertyMapError;
|
|||
use crate::parser::util::PropertyParser;
|
||||
|
||||
use super::paragraph::Paragraph;
|
||||
use super::reference::Reference;
|
||||
use super::reference::InternalReference;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum MediaType {
|
||||
|
@ -72,17 +72,14 @@ impl Element for Media {
|
|||
|
||||
fn as_container(&self) -> Option<&dyn ContainerElement> { Some(self) }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document, cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
let mut result = String::new();
|
||||
|
||||
result.push_str("<div class=\"media\">");
|
||||
for medium in &self.media {
|
||||
match medium.compile(compiler, document) {
|
||||
Ok(r) => result.push_str(r.as_str()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
result += medium.compile(compiler, document, cursor+result.len())?.as_str();
|
||||
}
|
||||
result.push_str("</div>");
|
||||
|
||||
|
@ -135,16 +132,20 @@ impl Element for Medium {
|
|||
|
||||
fn as_referenceable(&self) -> Option<&dyn ReferenceableElement> { Some(self) }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document, cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
let mut result = String::new();
|
||||
|
||||
// Reference
|
||||
let elemref = document.get_reference(self.reference.as_str()).unwrap();
|
||||
let refcount = compiler.reference_id(document, elemref);
|
||||
|
||||
let width = self
|
||||
.width
|
||||
.as_ref()
|
||||
.map_or(String::new(), |w| format!(r#" style="width:{w};""#));
|
||||
result.push_str(format!(r#"<div class="medium"{width}>"#).as_str());
|
||||
result.push_str(format!(r#"<div id="{}" class="medium"{width}>"#, self.refid(compiler, refcount)).as_str());
|
||||
result += match self.media_type {
|
||||
MediaType::IMAGE => format!(r#"<a href="{0}"><img src="{0}"></a>"#, self.uri),
|
||||
MediaType::VIDEO => format!(
|
||||
|
@ -168,17 +169,11 @@ impl Element for Medium {
|
|||
})
|
||||
.unwrap_or(String::new());
|
||||
|
||||
// Reference
|
||||
let elemref = document.get_reference(self.reference.as_str()).unwrap();
|
||||
let refcount = compiler.reference_id(document, elemref);
|
||||
result.push_str(
|
||||
format!(r#"<p class="medium-refname">({refcount}){caption}</p>"#).as_str(),
|
||||
);
|
||||
if let Some(paragraph) = self.description.as_ref() {
|
||||
match paragraph.compile(compiler, document) {
|
||||
Ok(res) => result.push_str(res.as_str()),
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
result += paragraph.compile(compiler, document, cursor+result.len())?.as_str();
|
||||
}
|
||||
result.push_str("</div>");
|
||||
|
||||
|
@ -198,7 +193,7 @@ impl ReferenceableElement for Medium {
|
|||
&self,
|
||||
compiler: &Compiler,
|
||||
_document: &dyn Document,
|
||||
reference: &Reference,
|
||||
reference: &InternalReference,
|
||||
refid: usize,
|
||||
) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
|
@ -219,6 +214,10 @@ impl ReferenceableElement for Medium {
|
|||
_ => todo!(""),
|
||||
}
|
||||
}
|
||||
|
||||
fn refid(&self, _compiler: &Compiler, refid: usize) -> String {
|
||||
format!("medium-{refid}")
|
||||
}
|
||||
}
|
||||
|
||||
#[auto_registry::auto_registry(registry = "rules", path = "crate::elements::media")]
|
||||
|
|
|
@ -48,7 +48,7 @@ impl Element for Paragraph {
|
|||
|
||||
fn element_name(&self) -> &'static str { "Paragraph" }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document, cursor: usize) -> Result<String, String> {
|
||||
if self.content.is_empty() {
|
||||
return Ok(String::new());
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ impl Element for Paragraph {
|
|||
result.push_str("<p>");
|
||||
|
||||
for elems in &self.content {
|
||||
result += elems.compile(compiler, document)?.as_str();
|
||||
result += elems.compile(compiler, document, cursor+result.len())?.as_str();
|
||||
}
|
||||
|
||||
result.push_str("</p>");
|
||||
|
|
|
@ -39,7 +39,7 @@ impl Element for Raw {
|
|||
|
||||
fn element_name(&self) -> &'static str { "Raw" }
|
||||
|
||||
fn compile(&self, _compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, _compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
Ok(self.content.clone())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ use regex::Regex;
|
|||
|
||||
use crate::compiler::compiler::Compiler;
|
||||
use crate::compiler::compiler::Target;
|
||||
use crate::document::document::CrossReference;
|
||||
use crate::document::document::Document;
|
||||
use crate::document::element::ElemKind;
|
||||
use crate::document::element::Element;
|
||||
|
@ -27,24 +28,29 @@ use crate::parser::util::PropertyMap;
|
|||
use crate::parser::util::PropertyParser;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Reference {
|
||||
pub struct InternalReference {
|
||||
pub(self) location: Token,
|
||||
pub(self) refname: String,
|
||||
pub(self) caption: Option<String>,
|
||||
}
|
||||
|
||||
impl Reference {
|
||||
impl InternalReference {
|
||||
pub fn caption(&self) -> Option<&String> { self.caption.as_ref() }
|
||||
}
|
||||
|
||||
impl Element for Reference {
|
||||
impl Element for InternalReference {
|
||||
fn location(&self) -> &Token { &self.location }
|
||||
|
||||
fn kind(&self) -> ElemKind { ElemKind::Inline }
|
||||
|
||||
fn element_name(&self) -> &'static str { "Reference" }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(
|
||||
&self,
|
||||
compiler: &Compiler,
|
||||
document: &dyn Document,
|
||||
_cursor: usize,
|
||||
) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
let elemref = document.get_reference(self.refname.as_str()).unwrap();
|
||||
|
@ -62,6 +68,43 @@ impl Element for Reference {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExternalReference {
|
||||
pub(self) location: Token,
|
||||
pub(self) reference: CrossReference,
|
||||
pub(self) caption: Option<String>,
|
||||
}
|
||||
|
||||
impl Element for ExternalReference {
|
||||
fn location(&self) -> &Token { &self.location }
|
||||
|
||||
fn kind(&self) -> ElemKind { ElemKind::Inline }
|
||||
|
||||
fn element_name(&self) -> &'static str { "Reference" }
|
||||
|
||||
fn compile(
|
||||
&self,
|
||||
compiler: &Compiler,
|
||||
_document: &dyn Document,
|
||||
cursor: usize,
|
||||
) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
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());
|
||||
result += format!("\">{}</a>", Compiler::sanitize(Target::HTML, refname)).as_str();
|
||||
Ok(result)
|
||||
}
|
||||
_ => todo!(""),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[auto_registry::auto_registry(registry = "rules", path = "crate::elements::reference")]
|
||||
pub struct ReferenceRule {
|
||||
re: [Regex; 1],
|
||||
|
@ -142,41 +185,71 @@ impl RegexRule for ReferenceRule {
|
|||
) -> Vec<Report<'_, (Rc<dyn Source>, Range<usize>)>> {
|
||||
let mut reports = vec![];
|
||||
|
||||
let refname = match (
|
||||
matches.get(1).unwrap(),
|
||||
validate_refname(document, matches.get(1).unwrap().as_str(), false),
|
||||
) {
|
||||
(m, Ok(refname)) => {
|
||||
if document.get_reference(refname).is_none() {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), m.start())
|
||||
.with_message("Uknown Reference Refname")
|
||||
.with_label(
|
||||
Label::new((token.source().clone(), m.range())).with_message(
|
||||
format!(
|
||||
"Could not find element with reference: `{}`",
|
||||
refname.fg(state.parser.colors().info)
|
||||
),
|
||||
),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
return reports;
|
||||
let (refdoc, refname) = if let Some(refname_match) = matches.get(1) {
|
||||
if let Some(sep) = refname_match.as_str().find('#')
|
||||
// External reference
|
||||
{
|
||||
let refdoc = refname_match.as_str().split_at(sep).0;
|
||||
match validate_refname(document, refname_match.as_str().split_at(sep + 1).1, false)
|
||||
{
|
||||
Err(err) => {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), refname_match.start())
|
||||
.with_message("Invalid Reference Refname")
|
||||
.with_label(
|
||||
Label::new((token.source().clone(), refname_match.range()))
|
||||
.with_message(err),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
return reports;
|
||||
}
|
||||
Ok(refname) => (Some(refdoc.to_string()), refname.to_string()),
|
||||
}
|
||||
} else
|
||||
// Internal reference
|
||||
{
|
||||
match validate_refname(document, refname_match.as_str(), false) {
|
||||
Err(err) => {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), refname_match.start())
|
||||
.with_message("Invalid Reference Refname")
|
||||
.with_label(
|
||||
Label::new((token.source().clone(), refname_match.range()))
|
||||
.with_message(err),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
return reports;
|
||||
}
|
||||
Ok(refname) => {
|
||||
if document.get_reference(refname).is_none() {
|
||||
reports.push(
|
||||
Report::build(
|
||||
ReportKind::Error,
|
||||
token.source(),
|
||||
refname_match.start(),
|
||||
)
|
||||
.with_message("Uknown Reference Refname")
|
||||
.with_label(
|
||||
Label::new((token.source().clone(), refname_match.range()))
|
||||
.with_message(format!(
|
||||
"Could not find element with reference: `{}`",
|
||||
refname.fg(state.parser.colors().info)
|
||||
)),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
return reports;
|
||||
}
|
||||
(None, refname.to_string())
|
||||
}
|
||||
}
|
||||
refname.to_string()
|
||||
}
|
||||
(m, Err(err)) => {
|
||||
reports.push(
|
||||
Report::build(ReportKind::Error, token.source(), m.start())
|
||||
.with_message("Invalid Reference Refname")
|
||||
.with_label(
|
||||
Label::new((token.source().clone(), m.range())).with_message(err),
|
||||
)
|
||||
.finish(),
|
||||
);
|
||||
return reports;
|
||||
}
|
||||
} else {
|
||||
panic!("Unknown error")
|
||||
};
|
||||
|
||||
// Properties
|
||||
let properties = match self.parse_properties(state.parser.colors(), &token, &matches.get(3))
|
||||
{
|
||||
|
@ -194,14 +267,40 @@ impl RegexRule for ReferenceRule {
|
|||
.ok()
|
||||
.and_then(|(_, s)| Some(s));
|
||||
|
||||
state.push(
|
||||
document,
|
||||
Box::new(Reference {
|
||||
location: token,
|
||||
refname,
|
||||
caption,
|
||||
}),
|
||||
);
|
||||
if let Some(refdoc) = refdoc {
|
||||
if caption.is_none() {
|
||||
return reports;
|
||||
}
|
||||
|
||||
if refdoc.is_empty() {
|
||||
state.push(
|
||||
document,
|
||||
Box::new(ExternalReference {
|
||||
location: token,
|
||||
reference: CrossReference::Unspecific(refname),
|
||||
caption,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
state.push(
|
||||
document,
|
||||
Box::new(ExternalReference {
|
||||
location: token,
|
||||
reference: CrossReference::Specific(refdoc, refname),
|
||||
caption,
|
||||
}),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
state.push(
|
||||
document,
|
||||
Box::new(InternalReference {
|
||||
location: token,
|
||||
refname,
|
||||
caption,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
reports
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ use std::ops::Range;
|
|||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::reference::InternalReference;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Section {
|
||||
pub(self) location: Token,
|
||||
|
@ -43,7 +45,7 @@ impl Element for Section {
|
|||
fn location(&self) -> &Token { &self.location }
|
||||
fn kind(&self) -> ElemKind { ElemKind::Block }
|
||||
fn element_name(&self) -> &'static str { "Section" }
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
// Section numbering
|
||||
|
@ -112,7 +114,7 @@ impl ReferenceableElement for Section {
|
|||
&self,
|
||||
compiler: &Compiler,
|
||||
_document: &dyn Document,
|
||||
reference: &super::reference::Reference,
|
||||
reference: &InternalReference,
|
||||
_refid: usize,
|
||||
) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
|
@ -133,6 +135,10 @@ impl ReferenceableElement for Section {
|
|||
_ => todo!(""),
|
||||
}
|
||||
}
|
||||
|
||||
fn refid(&self, compiler: &Compiler, _refid: usize) -> String {
|
||||
Compiler::refname(compiler.target(), self.title.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[auto_registry::auto_registry(registry = "rules", path = "crate::elements::section")]
|
||||
|
|
|
@ -46,7 +46,7 @@ impl Element for Style {
|
|||
fn location(&self) -> &Token { &self.location }
|
||||
fn kind(&self) -> ElemKind { ElemKind::Inline }
|
||||
fn element_name(&self) -> &'static str { "Style" }
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
Ok([
|
||||
|
|
|
@ -149,7 +149,7 @@ impl Element for Tex {
|
|||
|
||||
fn element_name(&self) -> &'static str { "LaTeX" }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
match compiler.target() {
|
||||
Target::HTML => {
|
||||
static CACHE_INIT: Once = Once::new();
|
||||
|
|
|
@ -37,7 +37,7 @@ impl Element for Text {
|
|||
fn kind(&self) -> ElemKind { ElemKind::Inline }
|
||||
fn element_name(&self) -> &'static str { "Text" }
|
||||
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document) -> Result<String, String> {
|
||||
fn compile(&self, compiler: &Compiler, _document: &dyn Document, _cursor: usize) -> Result<String, String> {
|
||||
Ok(Compiler::sanitize(compiler.target(), self.content.as_str()))
|
||||
}
|
||||
}
|
||||
|
|
76
src/main.rs
76
src/main.rs
|
@ -5,6 +5,7 @@ mod elements;
|
|||
mod lua;
|
||||
mod parser;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::env;
|
||||
use std::io::BufWriter;
|
||||
use std::io::Write;
|
||||
|
@ -17,6 +18,7 @@ use compiler::compiler::CompiledDocument;
|
|||
use compiler::compiler::Compiler;
|
||||
use compiler::compiler::Target;
|
||||
use compiler::navigation::create_navigation;
|
||||
use compiler::postprocess::PostProcess;
|
||||
use document::document::Document;
|
||||
use getopts::Options;
|
||||
use parser::langparser::LangParser;
|
||||
|
@ -66,7 +68,6 @@ fn parse(
|
|||
.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();
|
||||
|
@ -85,7 +86,7 @@ fn parse(
|
|||
}
|
||||
|
||||
if parser.has_error() {
|
||||
return Err("Parsing failed aborted due to errors while parsing".to_string());
|
||||
return Err("Parsing failed due to errors while parsing".to_string());
|
||||
}
|
||||
|
||||
Ok(doc)
|
||||
|
@ -97,7 +98,7 @@ fn process(
|
|||
db_path: &Option<String>,
|
||||
force_rebuild: bool,
|
||||
debug_opts: &Vec<String>,
|
||||
) -> Result<Vec<CompiledDocument>, String> {
|
||||
) -> Result<Vec<(RefCell<CompiledDocument>, Option<PostProcess>)>, String> {
|
||||
let mut compiled = vec![];
|
||||
|
||||
let current_dir = std::env::current_dir()
|
||||
|
@ -126,24 +127,20 @@ fn process(
|
|||
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, String> {
|
||||
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 = compiler.compile(&*doc);
|
||||
let (mut compiled, postprocess) = compiler.compile(&*doc);
|
||||
|
||||
// Insert into cache
|
||||
compiled.mtime = modified.duration_since(UNIX_EPOCH).unwrap().as_secs();
|
||||
compiled.insert_cache(&con).map_err(|err| {
|
||||
format!("Failed to insert compiled document from `{file:#?}` into cache: {err}")
|
||||
})?;
|
||||
|
||||
Ok(compiled)
|
||||
Ok((compiled, Some(postprocess)))
|
||||
};
|
||||
|
||||
let cdoc = if force_rebuild {
|
||||
let (cdoc, post) = if force_rebuild {
|
||||
parse_and_compile()?
|
||||
} else {
|
||||
match CompiledDocument::from_cache(&con, file.to_str().unwrap()) {
|
||||
|
@ -151,14 +148,35 @@ fn process(
|
|||
if compiled.mtime < modified.duration_since(UNIX_EPOCH).unwrap().as_secs() {
|
||||
parse_and_compile()?
|
||||
} else {
|
||||
compiled
|
||||
(compiled, None)
|
||||
}
|
||||
}
|
||||
None => parse_and_compile()?,
|
||||
}
|
||||
};
|
||||
|
||||
compiled.push(cdoc);
|
||||
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)
|
||||
|
@ -344,8 +362,8 @@ fn main() -> ExitCode {
|
|||
}
|
||||
|
||||
// Parse, compile using the cache
|
||||
let compiled = match process(Target::HTML, files, &db_path, force_rebuild, &debug_opts) {
|
||||
Ok(compiled) => compiled,
|
||||
let processed = match process(Target::HTML, files, &db_path, force_rebuild, &debug_opts) {
|
||||
Ok(processed) => processed,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
return ExitCode::FAILURE;
|
||||
|
@ -356,7 +374,7 @@ fn main() -> ExitCode {
|
|||
// Batch mode
|
||||
{
|
||||
// Build navigation
|
||||
let navigation = match create_navigation(&compiled) {
|
||||
let navigation = match create_navigation(&processed) {
|
||||
Ok(nav) => nav,
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
|
@ -365,14 +383,15 @@ fn main() -> ExitCode {
|
|||
};
|
||||
|
||||
// Output
|
||||
for doc in compiled {
|
||||
for (doc, _) in &processed {
|
||||
let out_path = match doc
|
||||
.borrow()
|
||||
.get_variable("compiler.output")
|
||||
.or(input_meta.is_file().then_some(&output))
|
||||
{
|
||||
Some(path) => path.clone(),
|
||||
None => {
|
||||
eprintln!("Unable to get output file for `{}`", doc.input);
|
||||
eprintln!("Unable to get output file for `{}`", doc.borrow().input);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
@ -382,18 +401,33 @@ fn main() -> ExitCode {
|
|||
|
||||
let mut writer = BufWriter::new(file);
|
||||
|
||||
write!(writer, "{}{}{}{}", doc.header, nav, doc.body, doc.footer).unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"{}{}{}{}",
|
||||
doc.borrow().header,
|
||||
nav,
|
||||
doc.borrow().body,
|
||||
doc.borrow().footer
|
||||
)
|
||||
.unwrap();
|
||||
writer.flush().unwrap();
|
||||
}
|
||||
} else
|
||||
// Single file
|
||||
{
|
||||
for doc in compiled {
|
||||
for (doc, _) in &processed {
|
||||
let file = std::fs::File::create(output.clone()).unwrap();
|
||||
|
||||
let mut writer = BufWriter::new(file);
|
||||
|
||||
write!(writer, "{}{}{}", doc.header, doc.body, doc.footer).unwrap();
|
||||
write!(
|
||||
writer,
|
||||
"{}{}{}",
|
||||
doc.borrow().header,
|
||||
doc.borrow().body,
|
||||
doc.borrow().footer
|
||||
)
|
||||
.unwrap();
|
||||
writer.flush().unwrap();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue