diff --git a/crates/org-cli/Cargo.toml b/crates/org-cli/Cargo.toml
index 100563f..fa75b80 100644
--- a/crates/org-cli/Cargo.toml
+++ b/crates/org-cli/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "org-rust"
-version = "0.1.16"
+version = "0.1.17"
description = "CLI tool for converting Org-Mode documents to other formats"
keywords = ["org-mode", "parser"]
categories = ["command-line-utilities"]
@@ -18,8 +18,8 @@ rust-version.workspace = true
clap = { version = "4.3.11", features = ["derive"] }
lazy_format = "2.0.0"
regex = "1.9.5"
-org-exporter = { version = "0.1.6", path = "../org-exporter", package = "org-rust-exporter" }
-org-parser = { version = "0.1.4", path = "../org-parser", package = "org-rust-parser" }
+org-exporter = { version = "0.1.8", path = "../org-exporter", package = "org-rust-exporter" }
+org-parser = { version = "0.1.5", path = "../org-parser", package = "org-rust-parser" }
serde = { version = "1.0.196", features=["derive"]}
toml = "0.8.8"
anyhow = "1.0.82"
@@ -30,8 +30,8 @@ clap = { version = "4.3.11", features=["derive"]}
clap_complete = "4.3.2"
clap_mangen = "0.2.14"
serde = { version = "1.0.196", features=["derive"]}
-org-exporter = { version = "0.1.2", path = "../org-exporter", package = "org-rust-exporter" }
-org-parser = { version = "0.1.2", path = "../org-parser", package = "org-rust-parser" }
+org-exporter = { version = "0.1.8", path = "../org-exporter", package = "org-rust-exporter" }
+org-parser = { version = "0.1.5", path = "../org-parser", package = "org-rust-parser" }
# [[bin]]
diff --git a/crates/org-cli/src/cli.rs b/crates/org-cli/src/cli.rs
index f2341d1..47ef818 100644
--- a/crates/org-cli/src/cli.rs
+++ b/crates/org-cli/src/cli.rs
@@ -51,7 +51,7 @@ impl Backend {
parsed: &org_parser::Parser,
buf: &mut String,
conf: ConfigOptions
- ) -> Result<(), core::fmt::Error> {
+ ) -> Result<(), Vec> {
match self {
Backend::Html => org_exporter::Html::export_tree(parsed, buf, conf),
Backend::Org => org_exporter::Org::export_tree(parsed, buf, conf),
diff --git a/crates/org-cli/src/main.rs b/crates/org-cli/src/main.rs
index df62ca2..d318a52 100644
--- a/crates/org-cli/src/main.rs
+++ b/crates/org-cli/src/main.rs
@@ -161,7 +161,14 @@ fn run() -> anyhow::Result<()> {
}
let conf = ConfigOptions::new(Some(file_path.to_path_buf()));
- backend.export(&parser_output, &mut exported_content, conf)?;
+ if let Err(err_vec) = backend.export(&parser_output, &mut exported_content, conf) {
+ let mut build_str = String::new();
+ for e in err_vec {
+ build_str.push_str(&e.to_string());
+ build_str.push_str("\n");
+ }
+ Err(CliError::new().with_cause(&build_str))?
+ }
// handle a template (if needed)
if let Some(template_path) = parser_output.keywords.get("template_path") {
diff --git a/crates/org-cli/src/template.rs b/crates/org-cli/src/template.rs
index 43fd025..9836513 100644
--- a/crates/org-cli/src/template.rs
+++ b/crates/org-cli/src/template.rs
@@ -243,10 +243,10 @@ impl<'a, 'template> Template<'a, 'template> {
}
}
-#[cfg(test)]
-mod tests {
- use super::*;
+// #[cfg(test)]
+// mod tests {
+// use super::*;
- #[test]
- fn bad_template() {}
-}
+// #[test]
+// fn bad_template() {}
+// }
diff --git a/crates/org-cli/src/utils.rs b/crates/org-cli/src/utils.rs
index e5a2042..21c5336 100644
--- a/crates/org-cli/src/utils.rs
+++ b/crates/org-cli/src/utils.rs
@@ -8,7 +8,7 @@ use crate::types::CliError;
// a fs::canonicalize that doesnt care for existince. used for error handling
// yanked straight from:
// https://github.com/rust-lang/cargo/blob/fede83ccf973457de319ba6fa0e36ead454d2e20/src/cargo/util/paths.rs#L61
-pub fn normalize_path(path: &Path) -> PathBuf {
+pub fn _normalize_path(path: &Path) -> PathBuf {
let mut components = path.components().peekable();
let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
components.next();
diff --git a/crates/org-exporter/Cargo.toml b/crates/org-exporter/Cargo.toml
index 18fa06b..d51a246 100644
--- a/crates/org-exporter/Cargo.toml
+++ b/crates/org-exporter/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "org-rust-exporter"
-version = "0.1.7"
+version = "0.1.8"
description = "exporter for org mode documents parsed with `org-rust-parser`"
homepage.workspace = true
@@ -14,8 +14,9 @@ rust-version.workspace = true
[dependencies]
latex2mathml = "0.2.3"
memchr = "2.5.0"
-org-parser = { version = "0.1.3", path = "../org-parser", package = "org-rust-parser" }
+org-parser = { version = "0.1.5", path = "../org-parser", package = "org-rust-parser" }
phf = {version = "0.11.1", features = ["macros"]}
+thiserror = "1.0.63"
[dev-dependencies]
pretty_assertions = "1.3.0"
diff --git a/crates/org-exporter/src/html.rs b/crates/org-exporter/src/html.rs
index dd5125a..894b7a1 100644
--- a/crates/org-exporter/src/html.rs
+++ b/crates/org-exporter/src/html.rs
@@ -5,7 +5,7 @@
use core::fmt;
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
-use std::fmt::{Result, Write};
+use std::fmt::Write;
use latex2mathml::{latex_to_mathml, DisplayStyle};
use memchr::memchr3_iter;
@@ -15,10 +15,16 @@ use org_parser::{parse_macro_call, parse_org, Expr, Node, NodeID, Parser};
use crate::include::include_handle;
use crate::org_macros::macro_handle;
-use crate::types::{ConfigOptions, Exporter, ExporterInner};
+use crate::types::{ConfigOptions, Exporter, ExporterInner, LogicErrorKind};
use crate::utils::{process_toc, Options, TocItem};
+use crate::ExportError;
use phf::phf_set;
+macro_rules! w {
+ ($dst:expr, $($arg:tt)*) => {
+ $dst.write_fmt(format_args!($($arg)*)).expect("writing to buffer during export failed")
+ };
+}
// file types we can wrap an `img` around
static IMAGE_TYPES: phf::Set<&str> = phf_set! {
"jpeg",
@@ -69,6 +75,7 @@ pub struct Html<'buf> {
footnotes: Vec,
footnote_ids: HashMap,
conf: ConfigOptions,
+ errors: Vec,
}
/// Wrapper around strings that need to be properly HTML escaped.
@@ -83,7 +90,7 @@ impl<'a, S: AsRef> fmt::Display for HtmlEscape {
// we can iterate over bytes since it's not possible for
// an ascii character to appear in the codepoint of another larger char
// if we see an ascii, then it's guaranteed to be valid
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut prev_pos = 0;
// there are other characters we could escape, but memchr caps out at 3
// the really important one is `<`, and then also probably &
@@ -113,7 +120,7 @@ impl<'a, S: AsRef> fmt::Display for HtmlEscape {
}
impl<'buf> Exporter<'buf> for Html<'buf> {
- fn export(input: &str, conf: ConfigOptions) -> core::result::Result {
+ fn export(input: &str, conf: ConfigOptions) -> core::result::Result> {
let mut buf = String::new();
Html::export_buf(input, &mut buf, conf)?;
Ok(buf)
@@ -123,7 +130,7 @@ impl<'buf> Exporter<'buf> for Html<'buf> {
input: &'inp str,
buf: &'buf mut T,
conf: ConfigOptions,
- ) -> Result {
+ ) -> core::result::Result<(), Vec> {
let parsed: Parser<'_> = parse_org(input);
Html::export_tree(&parsed, buf, conf)
}
@@ -132,35 +139,50 @@ impl<'buf> Exporter<'buf> for Html<'buf> {
parsed: &Parser,
buf: &'buf mut T,
conf: ConfigOptions,
- ) -> fmt::Result {
+ ) -> core::result::Result<(), Vec> {
let mut obj = Html {
buf,
nox: HashSet::new(),
footnotes: Vec::new(),
footnote_ids: HashMap::new(),
conf,
+ errors: Vec::new(),
};
if let Ok(opts) = Options::handle_opts(parsed) {
if let Ok(tocs) = process_toc(parsed, &opts) {
- write!(
- obj,
- r#"
+ handle_toc(parsed, &mut obj, &tocs);
+ }
+ }
+ obj.export_rec(&parsed.pool.root_id(), &parsed);
+ obj.exp_footnotes(&parsed);
+
+ if obj.errors().is_empty() {
+ Ok(())
+ } else {
+ Err(obj.errors)
+ }
+ }
+}
+
+fn handle_toc<'a, T: fmt::Write + ExporterInner<'a>>(
+ parsed: &Parser,
+ writer: &mut T,
+ tocs: &Vec,
+) {
+ w!(
+ writer,
+ r#"
Table Of Contents
"#
- )?;
- write!(obj, "
")?;
- for toc in tocs {
- toc_rec(&parsed, &mut obj, &toc, 1)?;
- }
- write!(obj, " ")?;
- write!(obj, r#"
"#)?;
- }
- }
- obj.export_rec(&parsed.pool.root_id(), &parsed)?;
- obj.exp_footnotes(&parsed)
+ );
+ w!(writer, "");
+ for toc in tocs {
+ toc_rec(&parsed, writer, toc, 1);
}
+ w!(writer, " ");
+ w!(writer, r#" "#);
}
fn toc_rec<'a, T: fmt::Write + ExporterInner<'a>>(
@@ -168,27 +190,27 @@ fn toc_rec<'a, T: fmt::Write + ExporterInner<'a>>(
writer: &mut T,
parent: &TocItem,
curr_level: u8,
-) -> Result {
- write!(writer, "")?;
+) {
+ w!(writer, " ");
if curr_level < parent.level {
- write!(writer, "")?;
- toc_rec(&parser, writer, parent, curr_level + 1)?;
- write!(writer, " ")?;
+ w!(writer, "");
+ toc_rec(&parser, writer, parent, curr_level + 1);
+ w!(writer, " ");
} else {
- write!(writer, r#""#, parent.target)?;
+ w!(writer, r#" "#, parent.target);
for id in parent.name {
- writer.export_rec(id, parser)?;
+ writer.export_rec(id, parser);
}
- write!(writer, " ")?;
+ w!(writer, "");
if !parent.children.is_empty() {
- write!(writer, "")?;
+ w!(writer, "");
for child in &parent.children {
- toc_rec(&parser, writer, child, curr_level + 1)?;
+ toc_rec(&parser, writer, child, curr_level + 1);
}
- write!(writer, " ")?;
+ w!(writer, " ");
}
}
- write!(writer, " ")
+ w!(writer, "");
}
impl<'buf> ExporterInner<'buf> for Html<'buf> {
@@ -196,7 +218,7 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
input: &'inp str,
buf: &'buf mut T,
conf: ConfigOptions,
- ) -> Result {
+ ) -> core::result::Result<(), Vec> {
let parsed = parse_macro_call(input);
let mut obj = Html {
buf,
@@ -204,41 +226,47 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
footnotes: Vec::new(),
footnote_ids: HashMap::new(),
conf,
+ errors: Vec::new(),
};
- obj.export_rec(&parsed.pool.root_id(), &parsed)
+ obj.export_rec(&parsed.pool.root_id(), &parsed);
+ if obj.errors().is_empty() {
+ Ok(())
+ } else {
+ Err(obj.errors)
+ }
}
- fn export_rec(&mut self, node_id: &NodeID, parser: &Parser) -> Result {
+ fn export_rec(&mut self, node_id: &NodeID, parser: &Parser) {
// avoid parsing this node
if self.nox.contains(node_id) {
- return Ok(());
+ return;
}
let node = &parser.pool[*node_id];
match &node.obj {
Expr::Root(inner) => {
for id in inner {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
}
Expr::Heading(inner) => {
let heading_number: u8 = inner.heading_level.into();
- write!(self, "")?;
+ w!(self, "");
if let Some(title) = &inner.title {
for id in &title.1 {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
}
- writeln!(self, " ")?;
+ w!(self, " \n");
if let Some(children) = &inner.children {
for id in children {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
}
}
@@ -250,31 +278,31 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
contents,
} => {
if parameters.get("exports").is_some_and(|&x| x == "none") {
- return Ok(());
+ return;
}
- write!(self, "")?;
+ w!(self, "
\n");
for id in contents {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- writeln!(self, "
")?;
+ w!(self, "
\n");
}
Block::Quote {
parameters,
contents,
} => {
if parameters.get("exports").is_some_and(|&x| x == "none") {
- return Ok(());
+ return;
}
- write!(self, "")?;
+ w!(self, "\n");
for id in contents {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- writeln!(self, " ")?;
+ w!(self, " \n");
}
Block::Special {
parameters,
@@ -282,26 +310,26 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
name,
} => {
if parameters.get("exports").is_some_and(|&x| x == "none") {
- return Ok(());
+ return;
}
// html5 names are directly converted into tags
if HTML5_TYPES.contains(name) {
- write!(self, "<{name}")?;
- self.prop(node)?;
- writeln!(self, ">")?;
+ w!(self, "<{name}");
+ self.prop(node);
+ w!(self, ">\n");
for id in contents {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- write!(self, "{name}>")?;
+ w!(self, "{name}>");
} else {
- write!(self, "")?;
+ w!(self, "
\n");
for id in contents {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- writeln!(self, "
")?;
+ w!(self, "
\n");
}
}
@@ -311,21 +339,21 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
contents,
} => {
if parameters.get("exports").is_some_and(|&x| x == "none") {
- return Ok(());
+ return;
}
- writeln!(self, "")?;
+ w!(self, "\n");
}
Block::Example {
parameters,
contents,
} => {
if parameters.get("exports").is_some_and(|&x| x == "none") {
- return Ok(());
+ return;
}
- write!(self, "\n{} ", HtmlEscape(contents))?;
+ w!(self, "\n{} \n", HtmlEscape(contents));
}
Block::Export {
backend,
@@ -333,10 +361,10 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
contents,
} => {
if parameters.get("exports").is_some_and(|&x| x == "none") {
- return Ok(());
+ return;
}
if backend.is_some_and(|x| x == Html::backend_name()) {
- writeln!(self, "{contents}")?;
+ w!(self, "{contents}\n");
}
}
Block::Src {
@@ -345,29 +373,29 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
contents,
} => {
if parameters.get("exports").is_some_and(|&x| x == "none") {
- return Ok(());
+ return;
}
- write!(self, "")?;
- write!(self, "");
+ w!(self, "\n{}
", HtmlEscape(contents))?;
+ self.prop(node);
+ w!(self, ">\n{}\n", HtmlEscape(contents));
}
Block::Verse {
parameters,
contents,
} => {
if parameters.get("exports").is_some_and(|&x| x == "none") {
- return Ok(());
+ return;
}
// FIXME: apparently verse blocks contain objects...
- write!(self, "\n{}
", HtmlEscape(contents))?;
+ w!(self, "\n{}
\n", HtmlEscape(contents));
}
}
}
@@ -398,15 +426,15 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
}
PathReg::File(a) => format!("{a}"),
};
- write!(self, r#""#, HtmlEscape(&path_link))?;
+ w!(self, r#" "#, HtmlEscape(&path_link));
if let Some(children) = &inner.description {
for id in children {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
} else {
- write!(self, "{}", HtmlEscape(inner.path.to_str(parser.source)))?;
+ w!(self, "{}", HtmlEscape(inner.path.to_str(parser.source)));
}
- write!(self, " ")?;
+ w!(self, "");
}
Expr::Paragraph(inner) => {
@@ -427,14 +455,14 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
let ending_tag = link_source.split('.').last();
if let Some(extension) = ending_tag {
if IMAGE_TYPES.contains(extension) {
- write!(self, "\n \n =
@@ -443,158 +471,174 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
} else {
link_source.into()
};
- write!(self, "{}", HtmlEscape(alt_text))?;
+ w!(self, "{}", HtmlEscape(alt_text));
}
- write!(self, "\">\n ")?;
- return Ok(());
+ w!(self, "\">\n");
+ return;
}
}
}
}
- write!(self, "")?;
+ w!(self, "
");
for id in &inner.0 {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- writeln!(self, "
")?;
+ w!(self, "
\n");
}
Expr::Italic(inner) => {
- write!(self, "")?;
+ w!(self, "");
for id in &inner.0 {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- write!(self, " ")?;
+ w!(self, " ");
}
Expr::Bold(inner) => {
- write!(self, "")?;
+ w!(self, "");
for id in &inner.0 {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- write!(self, " ")?;
+ w!(self, " ");
}
Expr::StrikeThrough(inner) => {
- write!(self, "")?;
+ w!(self, "");
for id in &inner.0 {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- write!(self, "")?;
+ w!(self, "");
}
Expr::Underline(inner) => {
- write!(self, "")?;
+ w!(self, "");
for id in &inner.0 {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- write!(self, " ")?;
- // write!(self, "")?;
+ w!(self, " ");
+ // w!(self, "")?;
// for id in &inner.0 {
- // self.export_rec(id, parser)?;
+ // self.export_rec(id, parser);
// }
- // write!(self, " ")?;
+ // w!(self, "")?;
}
Expr::BlankLine => {
- // write!(self, "\n")?;
+ // w!(self, "\n")?;
}
Expr::SoftBreak => {
- write!(self, " ")?;
+ w!(self, " ");
}
Expr::LineBreak => {
- writeln!(self, "\n ")?;
+ w!(self, "\n \n");
}
Expr::HorizontalRule => {
- writeln!(self, "\n ")?;
+ w!(self, "\n \n");
}
Expr::Plain(inner) => {
- write!(self, "{}", HtmlEscape(inner))?;
+ w!(self, "{}", HtmlEscape(inner));
}
Expr::Verbatim(inner) => {
- write!(self, "{}
", HtmlEscape(inner.0))?;
+ w!(self, "{}
", HtmlEscape(inner.0));
}
Expr::Code(inner) => {
- write!(self, "{}
", HtmlEscape(inner.0))?;
+ w!(self, "{}
", HtmlEscape(inner.0));
}
Expr::Comment(inner) => {
- write!(self, "", inner.0)?;
+ w!(self, "", inner.0);
}
Expr::InlineSrc(inner) => {
- write!(
+ w!(
self,
"{}
",
inner.lang,
HtmlEscape(inner.body)
- )?;
+ );
// if let Some(args) = inner.headers {
- // write!(self, "[{args}]")?;
+ // w!(self, "[{args}]")?;
// }
- // write!(self, "{{{}}}", inner.body)?;
+ // w!(self, "{{{}}}", inner.body)?;
}
Expr::Keyword(inner) => {
if inner.key.to_ascii_lowercase() == "include" {
- // FIXME: proper error handling
- write!(self, r#"")?;
- include_handle(inner.val, self).unwrap();
- write!(self, "
")?;
+ w!(self, r#"");
+
+ if let Err(e) = include_handle(inner.val, self) {
+ self.errors().push(ExportError::LogicError {
+ span: node.start..node.end,
+ source: LogicErrorKind::Include(e),
+ });
+ return;
+ }
+
+ // .map_err(|e| ExportError::LogicError {
+ // span: node.start..node.end,
+ // source: LogicErrorKind::Include(e),
+ // })?;
+ w!(self, "
");
}
}
Expr::LatexEnv(inner) => {
- let ret = latex_to_mathml(
- &format!(
- r"\begin{{{0}}}
+ let formatted = &format!(
+ r"\begin{{{0}}}
{1}
\end{{{0}}}
",
- inner.name, inner.contents
- ),
- DisplayStyle::Block,
- )
- .unwrap();
- writeln!(self, "{ret}")?;
+ inner.name, inner.contents
+ );
+ let ret = latex_to_mathml(&formatted, DisplayStyle::Block);
+ // TODO/FIXME: this should be an error
+ w!(
+ self,
+ "{}\n",
+ if let Ok(val) = &ret { val } else { formatted }
+ );
}
Expr::LatexFragment(inner) => match inner {
LatexFragment::Command { name, contents } => {
let mut pot_cont = String::new();
- write!(pot_cont, r#"{name}"#)?;
+ w!(pot_cont, r#"{name}"#);
if let Some(command_cont) = contents {
- write!(pot_cont, "{{{command_cont}}}")?;
+ w!(pot_cont, "{{{command_cont}}}");
}
- write!(
+ // TODO/FIXME: this should be an error
+ w!(
self,
"{}",
&latex_to_mathml(&pot_cont, DisplayStyle::Inline).unwrap(),
- )?;
+ );
}
LatexFragment::Display(inner) => {
- writeln!(
+ // TODO/FIXME: this should be an error
+ w!(
self,
- "{}",
+ "{}\n",
&latex_to_mathml(inner, DisplayStyle::Block).unwrap()
- )?;
+ );
}
LatexFragment::Inline(inner) => {
- write!(
+ // TODO/FIXME: this should be an error
+ w!(
self,
"{}",
&latex_to_mathml(inner, DisplayStyle::Inline).unwrap()
- )?;
+ );
}
},
Expr::Item(inner) => {
if let Some(tag) = inner.tag {
- write!(self, "{} ", HtmlEscape(tag))?;
- write!(self, "")?;
+ w!(self, " {} ", HtmlEscape(tag));
+ w!(self, "");
for id in &inner.children {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- write!(self, " ")?;
+ w!(self, "");
} else {
- write!(self, " ExporterInner<'buf> for Html<'buf> {
CheckBox::Intermediate => "trans",
CheckBox::Off => "off",
CheckBox::On => "on",
- })?;
+ });
}
- write!(self, ">")?;
+ w!(self, ">");
for id in &inner.children {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- writeln!(self, " ")?;
+ w!(self, "\n");
}
}
Expr::PlainList(inner) => {
- let tag = match inner.kind {
- ListKind::Unordered => "ul",
+ let (tag, desc) = match inner.kind {
+ ListKind::Unordered => ("ul", ""),
ListKind::Ordered(counter_kind) => match counter_kind {
org_parser::element::CounterKind::Letter(c) => {
if c.is_ascii_uppercase() {
- r#"ol type="A""#
+ ("ol", r#" type="A""#)
} else {
- r#"ol type="a""#
+ ("ol", r#" type="a""#)
}
}
- org_parser::element::CounterKind::Number(_) => r#"ol type="1""#,
+ org_parser::element::CounterKind::Number(_) => ("ol", r#" type="1""#),
},
- ListKind::Descriptive => "dd",
+ ListKind::Descriptive => ("dd", ""),
};
- write!(self, "<{tag}")?;
- self.prop(node)?;
- writeln!(self, ">")?;
+ w!(self, "<{tag}{desc}");
+ self.prop(node);
+ w!(self, ">\n");
for id in &inner.children {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- writeln!(self, "{tag}>")?;
+ w!(self, "{tag}>\n");
}
Expr::PlainLink(inner) => {
- write!(
+ w!(
self,
"{0}:{1} ",
- inner.protocol, inner.path
- )?;
+ inner.protocol,
+ inner.path
+ );
}
Expr::Entity(inner) => {
- write!(self, "{}", inner.mapped_item)?;
+ w!(self, "{}", inner.mapped_item);
}
Expr::Table(inner) => {
- write!(self, "")?;
+ w!(self, "\n");
for id in &inner.children {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- writeln!(self, "
")?;
+ w!(self, "
\n");
}
Expr::TableRow(inner) => {
match inner {
TableRow::Rule => { /*skip*/ }
TableRow::Standard(stands) => {
- writeln!(self, "")?;
+ w!(self, " \n");
for id in stands.iter() {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- writeln!(self, " ")?;
+ w!(self, "\n");
}
}
}
Expr::TableCell(inner) => {
- write!(self, "")?;
+ w!(self, " ");
for id in &inner.0 {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
- writeln!(self, " ")?;
+ w!(self, "\n");
}
Expr::Emoji(inner) => {
- write!(self, "{}", inner.mapped_item)?;
+ w!(self, "{}", inner.mapped_item);
}
Expr::Superscript(inner) => {
- write!(self, "")?;
+ w!(self, "");
match &inner.0 {
PlainOrRec::Plain(inner) => {
- write!(self, "{inner}")?;
+ w!(self, "{inner}");
}
PlainOrRec::Rec(inner) => {
for id in inner {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
}
}
- write!(self, " ")?;
+ w!(self, " ");
}
Expr::Subscript(inner) => {
- write!(self, "")?;
+ w!(self, "");
match &inner.0 {
PlainOrRec::Plain(inner) => {
- write!(self, "{inner}")?;
+ w!(self, "{inner}");
}
PlainOrRec::Rec(inner) => {
for id in inner {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
}
}
- write!(self, " ")?;
+ w!(self, " ");
}
Expr::Target(inner) => {
- write!(self, "")?;
- write!(
+ w!(self, "");
+ w!(
self,
"{} ",
parser.pool[*node_id].id_target.as_ref().unwrap(), // must exist
HtmlEscape(inner.0)
- )?;
+ );
}
Expr::Macro(macro_call) => {
- if let Ok(macro_contents) = macro_handle(parser, macro_call, self.config_opts()) {
- match macro_contents {
- Cow::Owned(p) => {
- Html::export_macro_buf(&p, self, self.config_opts().clone())?;
- }
- Cow::Borrowed(r) => {
- write!(self, "{}", HtmlEscape(r))?;
+ let macro_contents = match macro_handle(parser, macro_call, self.config_opts()) {
+ Ok(contents) => contents,
+ Err(e) => {
+ self.errors().push(ExportError::LogicError {
+ span: node.start..node.end,
+ source: LogicErrorKind::Macro(e),
+ });
+ return;
+ }
+ };
+
+ match macro_contents {
+ Cow::Owned(p) => {
+ if let Err(mut err_vec) =
+ Html::export_macro_buf(&p, self, self.config_opts().clone())
+ {
+ self.errors().append(&mut err_vec);
+ // TODO alert for errors handled within macro
}
}
+ Cow::Borrowed(r) => {
+ w!(self, "{}", HtmlEscape(r));
+ }
}
}
Expr::Drawer(inner) => {
for id in &inner.children {
- self.export_rec(id, parser)?;
+ self.export_rec(id, parser);
}
}
Expr::ExportSnippet(inner) => {
if inner.backend == Html::backend_name() {
- write!(self, "{}", inner.contents)?;
+ w!(self, "{}", inner.contents);
}
}
Expr::Affiliated(inner) => match inner {
Affiliated::Name(_id) => {}
Affiliated::Caption(id, contents) => {
if let Some(caption_id) = id {
- writeln!(self, "")?;
- self.export_rec(caption_id, parser)?;
- writeln!(self, "")?;
- self.export_rec(contents, parser)?;
- writeln!(self, " ")?;
- writeln!(self, " ")?;
+ w!(self, "\n");
+ self.export_rec(caption_id, parser);
+ w!(self, "\n");
+ self.export_rec(contents, parser);
+ w!(self, " \n");
+ w!(self, " \n");
self.nox.insert(*caption_id);
}
}
@@ -792,17 +851,16 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
format!("{index}")
};
- write!(
+ w!(
self,
r##"
"##,
- fn_id, index,
- )?;
+ fn_id,
+ index,
+ );
}
}
-
- Ok(())
}
fn backend_name() -> &'static str {
@@ -812,38 +870,39 @@ impl<'buf> ExporterInner<'buf> for Html<'buf> {
fn config_opts(&self) -> &ConfigOptions {
&self.conf
}
+ fn errors(&mut self) -> &mut Vec {
+ &mut self.errors
+ }
}
// Writers for generic attributes
impl<'buf> Html<'buf> {
/// Adds a property
- fn prop(&mut self, node: &Node) -> Result {
+ fn prop(&mut self, node: &Node) {
// if the target needs an id
if let Some(tag_contents) = node.id_target.as_ref() {
- write!(self, r#" id="{tag_contents}""#)?;
+ w!(self, r#" id="{tag_contents}""#);
}
// attach any keys that need to be placed
if let Some(attrs) = node.attrs.get(Html::backend_name()) {
for (key, val) in attrs {
- self.attr(key, val)?;
+ self.attr(key, val);
}
}
-
- Ok(())
}
- fn class(&mut self, name: &str) -> Result {
- write!(self, r#" class="{name}""#)
+ fn class(&mut self, name: &str) {
+ w!(self, r#" class="{name}""#);
}
- fn attr(&mut self, key: &str, val: &str) -> Result {
- write!(self, r#" {}="{}""#, key, HtmlEscape(val))
+ fn attr(&mut self, key: &str, val: &str) {
+ w!(self, r#" {}="{}""#, key, HtmlEscape(val));
}
- fn exp_footnotes(&mut self, parser: &Parser) -> Result {
+ fn exp_footnotes(&mut self, parser: &Parser) {
if self.footnotes.is_empty() {
- return Ok(());
+ return;
}
// get last heading, and check if its title is Footnotes,
@@ -859,7 +918,7 @@ impl<'buf> Html<'buf> {
false
});
- writeln!(
+ w!(
self,
r#"
\n")
+ w!(self, "\n \n");
}
}
impl fmt::Write for Html<'_> {
- fn write_str(&mut self, s: &str) -> Result {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
self.buf.write_str(s)
}
}
@@ -925,60 +993,51 @@ mod tests {
use super::*;
use pretty_assertions::assert_eq;
- fn html_export(input: &str) -> core::result::Result {
- Html::export(input, ConfigOptions::default())
+ fn html_export(input: &str) -> String {
+ Html::export(input, ConfigOptions::default()).unwrap()
}
#[test]
- fn combined_macros() -> fmt::Result {
+ fn combined_macros() {
let a = html_export(
r"#+macro: poem hiii $1 $2 text
{{{poem(cool,three)}}}
",
- )?;
+ );
assert_eq!(
a,
- r"
-hiii cool three text
-
+ r"hiii cool three text
"
);
-
- Ok(())
}
#[test]
- fn keyword_macro() -> Result {
+ fn keyword_macro() {
let a = html_export(
r"
#+title: hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
{{{keyword(title)}}}
",
- )?;
+ );
assert_eq!(
a,
- r"
-hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
-
+ r"hiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
",
);
- Ok(())
}
#[test]
- fn line_break() -> Result {
+ fn line_break() {
let a = html_export(
r" abc\\
",
- )?;
+ );
assert_eq!(
a,
- r"
-abc
+ r"
abc
-
",
);
@@ -986,34 +1045,31 @@ abc
let n = html_export(
r" abc\\ q
",
- )?;
+ );
assert_eq!(
n,
- r"
-abc\\ q
-
+ r"abc\\ q
",
);
- Ok(())
}
#[test]
- fn horizontal_rule() -> Result {
+ fn horizontal_rule() {
let a = html_export(
r"-----
",
- )?;
+ );
let b = html_export(
r" -----
",
- )?;
+ );
let c = html_export(
r" -------------------------
",
- )?;
+ );
assert_eq!(a, b);
assert_eq!(b, c);
@@ -1022,21 +1078,17 @@ abc\\ q
let nb = html_export(
r" ----
",
- )?;
+ );
assert_eq!(
nb,
- r"
-----
-
+ r"----
",
);
-
- Ok(())
}
#[test]
- fn correct_cache() -> Result {
+ fn correct_cache() {
let a = html_export(
r"
- one
@@ -1046,65 +1098,56 @@ abc\\ q
abc &+ 10\\
\end{align}
",
- )?;
+ );
println!("{a}");
-
- Ok(())
}
#[test]
- fn html_unicode() -> Result {
+ fn html_unicode() {
let a = html_export(
r"a é😳
",
- )?;
+ );
assert_eq!(
a,
- r"
-a é😳
-
+ r"a é😳
"
);
-
- Ok(())
}
#[test]
- fn list_counter_set() -> Result {
+ fn list_counter_set() {
let a = html_export(
r"
1. [@4] wordsss??
",
- )?;
+ );
assert_eq!(
a,
- r#"
-
-wordsss??
-
+ r#"
+wordsss??
"#,
);
- Ok(())
}
#[test]
- fn anon_footnote() -> Result {
+ fn anon_footnote() {
let a = html_export(
r"
-hi [fn:next:coolio]
+hi [fn:next:coolio] yeah [fn:next]
",
- )?;
+ );
// just codifying what the output is here, not supposed to be set in stone
assert_eq!(
a,
- r##"
-hi
+ r##"hi
-
-
+ yeah
+
+