diff --git a/Cargo.lock b/Cargo.lock index 7377814..733bcb9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,6 +361,7 @@ dependencies = [ "syntect", "tempfile", "textwrap", + "upon", "uuid", "walkdir 2.3.3", "yaml-rust", @@ -1642,6 +1643,17 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" +[[package]] +name = "upon" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a9260fe394dfd8ab204a8eab40f88eb9a331bb852147d24fc0aff6b30daa02" +dependencies = [ + "serde", + "unicode-ident", + "unicode-width", +] + [[package]] name = "utf8-ranges" version = "1.0.5" diff --git a/Cargo.toml b/Cargo.toml index 8aa7f40..d6bd2c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,6 +51,7 @@ html-escape = "0.2" mime_guess = "2" comrak = "0.18" yaml-rust = "0.4" +upon = "0.7" mustache = "0.9" uuid = { version = "1", features = ["v4"] } walkdir = "2" diff --git a/src/lib/book.rs b/src/lib/book.rs index a62e7d5..e2cd308 100644 --- a/src/lib/book.rs +++ b/src/lib/book.rs @@ -22,11 +22,11 @@ use crate::chapter::Chapter; use crate::cleaner::{Cleaner, CleanerParams, Default, French, Off}; use crate::epub::Epub; use crate::error::{Error, Result, Source}; -use crate::html_dir::{HtmlDir, ProofHtmlDir}; +use crate::html_dir::HtmlDir; use crate::html_if::HtmlIf; -use crate::html_single::{HtmlSingle, ProofHtmlSingle}; +use crate::html_single::HtmlSingle; use crate::lang; -use crate::latex::{Latex, Pdf, ProofLatex, ProofPdf}; +use crate::latex::{Latex, Pdf}; use crate::misc; use crate::number::Number; use crate::parser::Features; @@ -38,14 +38,14 @@ use crate::token::Token; use std::borrow::Cow; use std::cmp::Ordering; -use std::collections::HashMap; +use std::collections::{HashMap, BTreeMap}; use std::fmt; use std::fs::File; use std::io::{Read, Write}; use std::iter::IntoIterator; use std::path::{Path, PathBuf}; -use mustache::{MapBuilder, Template}; +use mustache::{Template}; use numerals::roman::Roman; use rayon::prelude::*; use yaml_rust::{Yaml, YamlLoader}; @@ -120,7 +120,7 @@ impl fmt::Display for HeaderData { /// // Render the book as html to stdout /// book.render_format_to("html", &mut std::io::stdout()).unwrap(); /// ``` -pub struct Book { +pub struct Book<'a> { /// Internal structure. You should not accesss this directly except if /// you are writing a new renderer. pub chapters: Vec, @@ -147,11 +147,14 @@ pub struct Book { #[doc(hidden)] pub bars: Bars, + + /// Store the templates registry + pub registry: upon::Engine<'a>, } -impl Book { +impl<'a> Book<'a> { /// Creates a new, empty `Book` - pub fn new() -> Book { + pub fn new() -> Book<'a> { let mut book = Book { source: Source::empty(), chapters: vec![], @@ -163,47 +166,26 @@ impl Book { formats: HashMap::new(), features: Features::new(), bars: Bars::new(), + registry: upon::Engine::new(), }; book.add_format( "html", lformat!("HTML (standalone page)"), Box::new(HtmlSingle {}), ) - .add_format( - "proofread.html", - lformat!("HTML (standalone page/proofreading)"), - Box::new(ProofHtmlSingle {}), - ) .add_format( "html.dir", lformat!("HTML (multiple pages)"), Box::new(HtmlDir {}), ) - .add_format( - "proofread.html.dir", - lformat!("HTML (multiple pages/proofreading)"), - Box::new(ProofHtmlDir {}), - ) .add_format("tex", lformat!("LaTeX"), Box::new(Latex {})) - .add_format( - "proofread.tex", - lformat!("LaTeX (proofreading)"), - Box::new(ProofLatex {}), - ) .add_format("pdf", lformat!("PDF"), Box::new(Pdf {})) - .add_format( - "proofread.pdf", - lformat!("PDF (proofreading)"), - Box::new(ProofPdf {}), - ) .add_format("epub", lformat!("EPUB"), Box::new(Epub {})) .add_format( "html.if", lformat!("HTML (interactive fiction)"), Box::new(HtmlIf {}), ); - #[cfg(feature = "odt")] - book.add_format("odt", lformat!("ODT"), Box::new(Odt {})); book } @@ -265,9 +247,9 @@ impl Book { /// assert_eq!(book.options.get_str("author").unwrap(), "Foo"); /// assert_eq!(book.options.get_str("title").unwrap(), "Bar"); /// ``` - pub fn set_options<'a, I>(&mut self, options: I) -> &mut Book + pub fn set_options<'b, I>(&mut self, options: I) -> &mut Self where - I: IntoIterator, + I: IntoIterator, { // set options for (key, value) in options { @@ -302,7 +284,7 @@ impl Book { /// let mut book = Book::new(); /// let result = book.load_file("some.book"); /// ``` - pub fn load_file>(&mut self, path: P) -> Result<&mut Book> { + pub fn load_file>(&mut self, path: P) -> Result<()> { let filename = format!("{}", path.as_ref().display()); self.source = Source::new(filename.as_str()); self.options.source = Source::new(filename.as_str()); @@ -316,9 +298,8 @@ impl Book { self.options.root = self.root.clone(); } - let result = self.read_config(&f); - match result { - Ok(book) => Ok(book), + match self.read_config(&f) { + Ok(_) => Ok(()), Err(err) => { if err.is_config_parser() && path.as_ref().ends_with(".md") { let err = Error::default( @@ -353,7 +334,7 @@ impl Book { /// let mut book = Book::new(); /// book.load_markdown_file("foo.md"); // not unwraping since foo.md doesn't exist /// ``` - pub fn load_markdown_file>(&mut self, path: P) -> Result<&mut Self> { + pub fn load_markdown_file>(&mut self, path: P) -> Result<()> { let filename = format!("{}", path.as_ref().display()); self.source = Source::new(filename.as_str()); @@ -372,7 +353,7 @@ impl Book { // Update grammar checker according to options self.add_chapter(Number::Hidden, &relative_path.to_string_lossy(), false)?; - Ok(self) + Ok(()) } /// Reads a single markdown config from a `Read`able object. @@ -397,18 +378,18 @@ impl Book { /// book.read_markdown_config(content.as_bytes()).unwrap(); /// assert_eq!(book.options.get_str("title").unwrap(), "Bar"); /// ``` - pub fn read_markdown_config(&mut self, source: R) -> Result<&mut Self> { + pub fn read_markdown_config(&mut self, source: R) -> Result<()> { self.options.set("tex.class", "article").unwrap(); self.options.set("input.yaml_blocks", "true").unwrap(); // Update grammar checker according to options self.add_chapter_from_source(Number::Hidden, source, false)?; - Ok(self) + Ok(()) } /// Sets options from a YAML block - fn set_options_from_yaml(&mut self, yaml: &str) -> Result<&mut Book> { + fn set_options_from_yaml(&mut self, yaml: &str) -> Result<&mut Self> { self.options.source = self.source.clone(); match YamlLoader::load_from_str(yaml) { Err(err) => { @@ -471,8 +452,8 @@ impl Book { /// let mut book = Book::new(); /// book.read_config(content.as_bytes()); // no unwraping as `intro.md` and `chapter_01.md` don't exist /// ``` - pub fn read_config(&mut self, mut source: R) -> Result<&mut Book> { - fn get_filename<'a>(source: &Source, s: &'a str) -> Result<&'a str> { + pub fn read_config(&mut self, mut source: R) -> Result<()> { + fn get_filename<'b>(source: &Source, s: &'b str) -> Result<&'b str> { let words: Vec<&str> = (s[1..]).split_whitespace().collect(); if words.len() > 1 { return Err(Error::config_parser( @@ -691,7 +672,7 @@ impl Book { self.source.unset_line(); self.set_chapter_template()?; - Ok(self) + Ok(()) } /// Determine whether proofreading is activated or not @@ -1179,12 +1160,16 @@ impl Book { /// Sets the chapter_template once and for all fn set_chapter_template(&mut self) -> Result<()> { - let template = compile_str( - self.options.get_str("rendering.chapter.template").unwrap(), - &self.source, - "rendering.chapter.template", - )?; - self.chapter_template = Some(template); + self.registry.add_template("rendering.chapter.template", + self.options.get_str("rendering.chapter.template").unwrap().to_owned()) + .map_err(|e| Error::template( + &self.source, + lformat!( + "could not compile '{template}': {error}", + template = "rendering.chapter.template", + error = e + )) + )?; Ok(()) } @@ -1236,7 +1221,7 @@ impl Book { }; let mut data = self.get_metadata(&mut f)?; if !title.is_empty() { - data = data.insert_bool(format!("has_{header_type}_title"), true); + data.insert(format!("has_{header_type}_title"), true.into()); } let number = self.get_header_number(header, n)?; let header_name = self @@ -1245,43 +1230,24 @@ impl Book { .map(|s| s.to_owned()) .unwrap_or_else(|_| lang::get_str(self.options.get_str("lang").unwrap(), header_type)); - data = data - .insert_str(format!("{header_type}_title"), title.clone()) - .insert_str(header_type, header_name.clone()) - .insert_str("number", number.clone()); - let data = data.build(); - let mut res: Vec = vec![]; + data.insert(format!("{header_type}_title"), title.clone().into()); + data.insert(header_type.into(), header_name.clone().into()); + data.insert("number".into(), number.clone().into()); let opt_template = match header { Header::Part => &self.part_template, Header::Chapter => &self.chapter_template, }; - - if let Some(ref template) = *opt_template { - template.render_data(&mut res, &data)?; - } else { - let template = compile_str( - self.options - .get_str(&format!("rendering.{header_type}.template")) - .unwrap(), - &self.source, - &format!("rendering.{header_type}.template"), - )?; - template.render_data(&mut res, &data)?; - } - - match String::from_utf8(res) { - Err(_) => panic!( - "{}", - lformat!("header generated by mustache was not valid utf-8") - ), - Ok(res) => Ok(HeaderData { - text: res, - number, - header: header_name, - title, - }), - } + let res = self.registry.get_template(&format!("rendering.{header_type}.template")) + .unwrap() + .render(&data) + .to_string()?; + Ok(HeaderData { + text: res, + number, + header: header_name, + title, + }) } /// Returns the string corresponding to a number, title, and the numbering template for chapter @@ -1302,24 +1268,22 @@ impl Book { self.get_header(Header::Part, n, title, f) } - /// Returns a `MapBuilder` (used by `Mustache` for templating), to be used (and completed) + /// Returns a `Map of Key/Value` (used by `Upon` for templating), to be used (and completed) /// by renderers. It fills it with the metadata options. /// /// It also uses the lang/xx.yaml file corresponding to the language and fills /// `loc_xxx` fiels with it that corresponds to translated versions. /// /// This method treats the metadata as Markdown and thus calls `f` to render it. + /// This is why we can’t really cache this as it will depend on the renderer. #[doc(hidden)] - pub fn get_metadata(&self, mut f: F) -> Result + pub fn get_metadata(&self, mut f: F) -> Result> where F: FnMut(&str) -> Result, { - let mut mapbuilder = MapBuilder::new(); - mapbuilder = mapbuilder.insert_str("crowbook_version", env!("CARGO_PKG_VERSION")); - mapbuilder = mapbuilder.insert_bool( - format!("lang_{}", self.options.get_str("lang").unwrap()), - true, - ); + let mut m: BTreeMap = BTreeMap::new(); + m.insert("crowbook_version".into(), env!("CARGO_PKG_VERSION").into()); + m.insert(format!("lang_{}", self.options.get_str("lang").unwrap()), true.into()); // Add metadata to mapbuilder for key in self.options.get_metadata() { @@ -1335,12 +1299,12 @@ impl Book { match content { Ok(content) => { if !content.is_empty() { - mapbuilder = mapbuilder.insert_str(format!("{key}_raw"), raw); - mapbuilder = mapbuilder.insert_str(key.clone(), content); + m.insert(format!("{key}_raw"), raw.into()); + m.insert(key.clone(), content.into()); - mapbuilder = mapbuilder.insert_bool(format!("has_{key}"), true); + m.insert(format!("has_{key}"), true.into()); } else { - mapbuilder = mapbuilder.insert_bool(format!("has_{key}"), false); + m.insert(format!("has_{key}"), false.into()); } } Err(err) => { @@ -1356,7 +1320,7 @@ impl Book { } } } else { - mapbuilder = mapbuilder.insert_bool(format!("has_{key}"), false); + m.insert(format!("has_{key}"), false.into()); } } @@ -1365,11 +1329,32 @@ impl Book { for (key, value) in hash { let key = format!("loc_{}", key.as_str().unwrap()); let value = value.as_str().unwrap(); - mapbuilder = mapbuilder.insert_str(key, value); + m.insert(key, value.into()); } - Ok(mapbuilder) + Ok(m) } + /// Calls upon::engine::compile, does NOT registre the complete + pub fn compile_str<'s, O>(&self, template: &'s str, source: O, template_name: &str) -> Result> + where + O: Into, + { + let input: String = template.to_owned(); + let result = self.registry.compile(template); + match result { + Ok(result) => Ok(result), + Err(err) => Err(Error::template( + source, + lformat!( + "could not compile '{template}': {error}", + template = template_name, + error = err + ), + )), + } + } + + /// Remove YAML blocks from a string and try to parse them to set options /// /// YAML blocks start with @@ -1495,28 +1480,9 @@ impl Book { } } -impl std::default::Default for Book { +impl std::default::Default for Book<'_> { fn default() -> Self { Self::new() } } -/// Calls mustache::compile_str but catches panics and returns a result -pub fn compile_str(template: &str, source: O, template_name: &str) -> Result -where - O: Into, -{ - let input: String = template.to_owned(); - let result = mustache::compile_str(&input); - match result { - Ok(result) => Ok(result), - Err(err) => Err(Error::template( - source, - lformat!( - "could not compile '{template}': {error}", - template = template_name, - error = err - ), - )), - } -} diff --git a/src/lib/book_bars.rs b/src/lib/book_bars.rs index 2b501c7..7528852 100644 --- a/src/lib/book_bars.rs +++ b/src/lib/book_bars.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2022 Élisabeth HENRY. +// Copyright (C) 2017-2023 Élisabeth HENRY. // // This file is part of Crowbook. // @@ -63,7 +63,7 @@ impl Default for Bars { /// Return the style of a bar -impl Book { +impl Book<'_> { /// Adds a progress bar where where info should be written. /// /// See [indicatif doc](https://docs.rs/indicatif) for more information. @@ -270,7 +270,7 @@ impl Book { } } -impl Drop for Book { +impl Drop for Book<'_> { fn drop(&mut self) { if let Some(ref bar) = self.bars.secondbar { bar.finish_and_clear(); diff --git a/src/lib/book_bars_stubs.rs b/src/lib/book_bars_stubs.rs index 0de7c73..155f8e9 100644 --- a/src/lib/book_bars_stubs.rs +++ b/src/lib/book_bars_stubs.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2017 Élisabeth HENRY. +// Copyright (C) 2017-2023 Élisabeth HENRY. // // This file is part of Crowbook. // @@ -28,7 +28,7 @@ impl Bars { } } -impl Book { +impl Book<'_> { pub fn private_add_progress_bar(&mut self, _: bool) {} /// Sets a finished message to the progress bar, if it is set diff --git a/src/lib/bookoptions.rs b/src/lib/bookoptions.rs index 769be38..59b2ff5 100644 --- a/src/lib/bookoptions.rs +++ b/src/lib/bookoptions.rs @@ -559,8 +559,7 @@ impl BookOptions { })?; let mut book = Book::new(); book.load_file(file)?; - let options = mem::replace(&mut book.options, BookOptions::new()); - self.merge(options)?; + self.merge(&book.options)?; Ok(None) } else { Ok(self.options.insert(key, BookOption::Path(value))) @@ -910,7 +909,7 @@ impl BookOptions { /// If option is already set in self, don't add it, unless it was the default. /// Option is not inserted either if new value is equal to default. #[doc(hidden)] - pub fn merge(&mut self, other: BookOptions) -> Result<()> { + pub fn merge(&mut self, other: &BookOptions) -> Result<()> { for (key, value) in &other.options { // Check if option was already set, and if it was to default or to something else if self.defaults.contains_key(key) { diff --git a/src/lib/epub.rs b/src/lib/epub.rs index eb2a9cd..713ba3a 100644 --- a/src/lib/epub.rs +++ b/src/lib/epub.rs @@ -16,7 +16,7 @@ // along with Crowbook. If not, see . use crate::book::Header; -use crate::book::{compile_str, Book}; +use crate::book::Book; use crate::book_renderer::BookRenderer; use crate::error::{Error, Result, Source}; use crate::html::HtmlRenderer; @@ -34,7 +34,7 @@ use epub_builder::{ EpubBuilder, EpubContent, EpubVersion, ReferenceType, ZipCommand, ZipCommandOrLibrary, ZipLibrary, }; -use mustache::Template; +use upon::Template; use std::borrow::Cow; use std::convert::{AsMut, AsRef}; @@ -194,8 +194,9 @@ impl<'a> EpubRenderer<'a> { } // Write chapters - let template_chapter = compile_str( - self.html.book.get_template("epub.chapter.xhtml")?.as_ref(), + let template_chapter_src = self.html.book.get_template("epub.chapter.xhtml")?; + let template_chapter = self.html.book.compile_str( + template_chapter_src.as_ref(), &self.html.book.source, "epub.chapter.xhtml", )?; @@ -229,23 +230,21 @@ impl<'a> EpubRenderer<'a> { self.html.source = Source::empty(); // Render the CSS file and write it - let template_css = compile_str( - self.html.book.get_template("epub.css").unwrap().as_ref(), + let template_css_src = self.html.book.get_template("epub.css").unwrap(); + let template_css = self.html.book.compile_str( + template_css_src.as_ref(), &self.html.book.source, "epub.css", )?; let mut data = self .html .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .insert_bool(self.html.book.options.get_str("lang").unwrap(), true); + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + data.insert(self.html.book.options.get_str("lang").unwrap().into(), true.into()); if let Ok(epub_css_add) = self.html.book.options.get_str("epub.css.add") { - data = data.insert_str("additional_code", epub_css_add); + data.insert("additional_code".into(), epub_css_add.into()); } - let data = data.build(); - let mut res: Vec = vec![]; - template_css.render_data(&mut res, &data)?; - let css = String::from_utf8_lossy(&res); + let css = template_css.render(&data).to_string()?; maker.stylesheet(css.as_bytes()) .map_err(|err| Error::render(Source::empty(), format!("{}", err)))?; @@ -308,25 +307,17 @@ impl<'a> EpubRenderer<'a> { /// Render the titlepgae fn render_titlepage(&mut self) -> Result { - let template = compile_str( - self.html - .book - .get_template("epub.titlepage.xhtml")? - .as_ref(), + let template_src = self.html.book.get_template("epub.titlepage.xhtml")?; + let template = self.html.book.compile_str( + template_src.as_ref(), &self.html.book.source, "epub.titlepage.xhtml", )?; let data = self .html .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .build(); - let mut res: Vec = vec![]; - template.render_data(&mut res, &data)?; - match String::from_utf8(res) { - Err(_) => panic!("generated HTML in titlepage was not utf-8 valid"), - Ok(res) => Ok(res), - } + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + Ok(template.render(&data).to_string()?) } /// Render cover.xhtml @@ -341,32 +332,22 @@ impl<'a> EpubRenderer<'a> { )); } let epub3 = self.html.book.options.get_i32("epub.version").unwrap() == 3; - let template = compile_str( + let template = self.html.book.compile_str( if epub3 { epub3::COVER } else { COVER }, &self.html.book.source, "cover.xhtml", )?; - let data = self + let mut data = self .html .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .insert_str( - "cover", + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + data.insert( + "cover".into(), self.html .handler .map_image(&self.html.source, Cow::Owned(cover))? - .into_owned(), - ) - .build(); - let mut res: Vec = vec![]; - template.render_data(&mut res, &data)?; - match String::from_utf8(res) { - Err(_) => panic!( - "{}", - lformat!("generated HTML for cover.xhtml was not utf-8 valid") - ), - Ok(res) => Ok(res), - } + .into()); + Ok(template.render(&data).to_string()?) } else { panic!( "{}", @@ -421,21 +402,15 @@ impl<'a> EpubRenderer<'a> { } self.toc.push(self.chapter_title.clone()); - let data = self + let mut data = self .html .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .insert_str("content", content) - .insert_str("chapter_title_raw", self.chapter_title_raw.clone()) - .insert_str("chapter_title", std::mem::take(&mut self.chapter_title)) - .build(); - self.chapter_title = String::new(); - let mut res: Vec = vec![]; - template.render_data(&mut res, &data)?; - match String::from_utf8(res) { - Err(_) => panic!("{}", lformat!("generated HTML was not utf-8 valid")), - Ok(res) => Ok((res, std::mem::take(&mut self.chapter_title_raw))), - } + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + data.insert("content".into(), content.into()); + data.insert("chapter_title_raw".into(), self.chapter_title_raw.clone(). into()); + data.insert("chapter_title".into(), std::mem::take(&mut self.chapter_title).into()); + Ok((template.render(&data).to_string()?, + std::mem::take(&mut self.chapter_title_raw))) } /// Renders the header section of the book, finding the title of the chapter diff --git a/src/lib/error.rs b/src/lib/error.rs index 9d2184c..476ffc6 100644 --- a/src/lib/error.rs +++ b/src/lib/error.rs @@ -361,8 +361,8 @@ impl fmt::Display for Error { pub type Result = result::Result; /// Implement our Error from mustache::Error -impl From for Error { - fn from(err: mustache::Error) -> Error { +impl From for Error { + fn from(err: upon::Error) -> Error { Error::template(Source::empty(), format!("{err}")) } } diff --git a/src/lib/html.rs b/src/lib/html.rs index b152fe0..7076835 100644 --- a/src/lib/html.rs +++ b/src/lib/html.rs @@ -17,7 +17,7 @@ use crate::book::Header; use crate::book::HeaderData; -use crate::book::{compile_str, Book}; +use crate::book::Book; use crate::error::{Error, Result, Source}; use crate::lang; use crate::number::Number; @@ -36,8 +36,6 @@ use std::fmt::Write; use crowbook_text_processing::escape; use epub_builder::Toc; use epub_builder::TocElement; -use mustache::MapBuilder; -use mustache::Template; use numerals::roman::Roman; #[derive(Debug, PartialEq, Copy, Clone)] @@ -63,7 +61,7 @@ pub struct HtmlRenderer<'a> { filename: String, /// Book that must be rendered - pub book: &'a Book, + pub book: &'a Book<'a>, /// Proofread or not pub proofread: bool, @@ -109,8 +107,8 @@ pub struct HtmlRenderer<'a> { syntax: Option, - part_template_html: Template, - chapter_template_html: Template, + part_template_html: upon::Template<'a, 'a>, + chapter_template_html: upon::Template<'a, 'a>, } impl<'a> HtmlRenderer<'a> { @@ -161,12 +159,12 @@ impl<'a> HtmlRenderer<'a> { proofread: false, syntax, highlight, - part_template_html: compile_str( + part_template_html: book.compile_str( book.options.get_str("html.part.template").unwrap(), Source::empty(), "html.part.template", )?, - chapter_template_html: compile_str( + chapter_template_html: book.compile_str( book.options.get_str("html.chapter.template").unwrap(), Source::empty(), "html.chapter.template", @@ -278,17 +276,15 @@ impl<'a> HtmlRenderer<'a> { }; let has_number = !data.header.is_empty(); let has_title = !data.title.is_empty(); - let data = MapBuilder::new() - .insert_bool("has_number", has_number) - .insert_bool("has_title", has_title) - .insert_str("header", data.header) - .insert_str("number", data.number) - .insert_str("link", format!("{}", self.link_number)) - .insert_str("title", data.title) - .build(); - let mut res = vec![]; - template.render_data(&mut res, &data)?; - Ok(String::from_utf8(res)?) + let data = upon::value!{ + has_number: has_number, + has_title: has_title, + header: data.header, + number: data.number, + title: data.title, + link: format!("{}", self.link_number) + }; + Ok(template.render(&data).to_string()?) } } else { Ok(format!( @@ -667,12 +663,9 @@ impl<'a> HtmlRenderer<'a> { /// Consider the html as a template fn templatize(&mut self, s: &str) -> Result { - let mapbuilder = self.book.get_metadata(|s| Ok(s.to_owned()))?; - let data = mapbuilder.build(); - let template = compile_str(s, &self.book.source, "")?; - let mut res = vec![]; - template.render_data(&mut res, &data)?; - Ok(String::from_utf8_lossy(&res).into_owned()) + let data = self.book.get_metadata(|s| Ok(s.to_owned()))?; + let template = self.book.compile_str(s, &self.book.source, "")?; + Ok(template.render(&data).to_string()?) } /// Renders the toc name @@ -680,17 +673,14 @@ impl<'a> HtmlRenderer<'a> { pub fn get_toc_name(&mut self) -> Result { let data = self .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .build(); + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; let template = self .book .options .get_str("rendering.inline_toc.name") .unwrap(); - let template = compile_str(template, &self.book.source, "rendering.inline_toc.name")?; - let mut res = vec![]; - template.render_data(&mut res, &data)?; - Ok(String::from_utf8_lossy(&res).into_owned()) + let template = self.book.compile_str(template, &self.book.source, "rendering.inline_toc.name")?; + Ok(template.render(&data).to_string()?) } /// Render a section containing schema.org JSON-LD code diff --git a/src/lib/html_dir.rs b/src/lib/html_dir.rs index e7ba183..df9f568 100644 --- a/src/lib/html_dir.rs +++ b/src/lib/html_dir.rs @@ -15,7 +15,7 @@ // You should have received ba copy of the GNU Lesser General Public License // along with Crowbook. If not, see . -use crate::book::{compile_str, Book}; +use crate::book::Book; use crate::book_renderer::BookRenderer; use crate::error::{Error, Result, Source}; use crate::html::Highlight; @@ -273,8 +273,9 @@ impl<'a> HtmlDirRenderer<'a> { let toc = self.html.toc.render(false, false); // render all chapters - let template = compile_str( - self.html.book.get_template("html.dir.template")?.as_ref(), + let template_src = self.html.book.get_template("html.dir.template")?; + let template = self.html.book.compile_str( + template_src.as_ref(), &self.html.book.source, "html.dir.template", )?; @@ -308,39 +309,37 @@ impl<'a> HtmlDirRenderer<'a> { }; // Render each HTML document - let mut mapbuilder = self + let mut data = self .html .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .insert_str("content", content?) - .insert_str("chapter_title", titles[i].clone()) - .insert_str("json_data", self.html.get_json_ld()?) - .insert_str("chapter_title_raw", titles_raw[i].clone()) - .insert_str("toc", toc.clone()) - .insert_str("prev_chapter", prev_chapter) - .insert_str("next_chapter", next_chapter) - .insert_str("footer", HtmlRenderer::get_footer(self)?) - .insert_str("header", HtmlRenderer::get_header(self)?) - .insert_str("script", self.html.book.get_template("html.js").unwrap()) - .insert_bool(self.html.book.options.get_str("lang").unwrap(), true); + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + data.insert("content".into(), content?.into()); + data.insert("chapter_title".into(), titles[i].clone().into()); + data.insert("json_data".into(), self.html.get_json_ld()?.into()); + data.insert("chapter_title_raw".into(), titles_raw[i].clone().into()); + data.insert("toc".into(), toc.clone().into()); + data.insert("prev_chapter".into(), prev_chapter.into()); + data.insert("next_chapter".into(), next_chapter.into()); + data.insert("footer".into(), HtmlRenderer::get_footer(self)?.into()); + data.insert("header".into(), HtmlRenderer::get_header(self)?.into()); + data.insert("script".into(), self.html.book.get_template("html.js").unwrap().into()); + data.insert(self.html.book.options.get_str("lang").unwrap().into(), true.into()); if let Ok(favicon) = self.html.book.options.get_path("html.icon") { let favicon = self .html .handler .map_image(&self.html.book.source, favicon)?; - mapbuilder = mapbuilder.insert_str( - "favicon", - format!(""), + data.insert( + "favicon".into(), + format!("").into(), ); } if self.html.highlight == Highlight::Js { - mapbuilder = mapbuilder.insert_bool("highlight_code", true); + data.insert("highlight_code".into(), true.into()); } - let data = mapbuilder.build(); - let mut res = vec![]; - template.render_data(&mut res, &data)?; - self.write_file(&filenamer(i), &res)?; + let res = template.render(&data).to_string()?; + self.write_file(&filenamer(i), res.as_bytes())?; } let mut content = if let Ok(cover) = self.html.book.options.get_path("cover") { @@ -420,38 +419,37 @@ impl<'a> HtmlDirRenderer<'a> { )?; } // Render index.html and write it too - let mut mapbuilder = self + let mut data = self .html .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .insert_str("content", content) - .insert_str("header", HtmlRenderer::get_header(self)?) - .insert_str("footer", HtmlRenderer::get_footer(self)?) - .insert_str("toc", toc.clone()) - .insert_str("script", self.html.book.get_template("html.js").unwrap()) - .insert_bool(self.html.book.options.get_str("lang").unwrap(), true); + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + data.insert("content".into(), content.into()); + data.insert("header".into(), HtmlRenderer::get_header(self)?.into()); + data.insert("footer".into(), HtmlRenderer::get_footer(self)?.into()); + data.insert("toc".into(), toc.into()); + data.insert("script".into(), self.html.book.get_template("html.js").unwrap().into()); + data.insert(self.html.book.options.get_str("lang").unwrap().into(), true.into()); if let Ok(favicon) = self.html.book.options.get_path("html.icon") { let favicon = self .html .handler .map_image(&self.html.book.source, favicon)?; - mapbuilder = mapbuilder.insert_str( - "favicon", - format!(""), + data.insert( + "favicon".into(), + format!("").into(), ); } if self.html.highlight == Highlight::Js { - mapbuilder = mapbuilder.insert_bool("highlight_code", true); + data.insert("highlight_code".into(), true.into()); } - let data = mapbuilder.build(); - let template = compile_str( - self.html.book.get_template("html.dir.template")?.as_ref(), + let template_src = self.html.book.get_template("html.dir.template")?; + let template = self.html.book.compile_str( + template_src.as_ref(), &self.html.book.source, "html.dir.template", )?; - let mut res = vec![]; - template.render_data(&mut res, &data)?; - self.write_file("index.html", &res)?; + let res = template.render(&data).to_string()?; + self.write_file("index.html", res.as_bytes())?; Ok(()) } @@ -459,20 +457,19 @@ impl<'a> HtmlDirRenderer<'a> { // Render the CSS file and write it fn write_css(&self) -> Result<()> { // Render the CSS - let template_css = compile_str( - self.html.book.get_template("html.css")?.as_ref(), + let template_css_src = self.html.book.get_template("html.css")?; + let template_css = self.html.book.compile_str( + template_css_src.as_ref(), &self.html.book.source, "html.css", )?; let mut data = self.html.book.get_metadata(|s| Ok(s.to_owned()))?; - data = data.insert_str("colors", self.html.book.get_template("html.css.colors")?); + data.insert("colors".into(), self.html.book.get_template("html.css.colors")?.into()); if let Ok(html_css_add) = self.html.book.options.get_str("html.css.add") { - data = data.insert_str("additional_code", html_css_add); + data.insert("additional_code".into(), html_css_add.into()); } - let data = data.build(); - let mut res: Vec = vec![]; - template_css.render_data(&mut res, &data)?; - let css = String::from_utf8_lossy(&res); + + let css = template_css.render(&data).to_string()?; // Write it self.write_file("stylesheet.css", css.as_bytes()) diff --git a/src/lib/html_if.rs b/src/lib/html_if.rs index 2d749c4..ac35491 100644 --- a/src/lib/html_if.rs +++ b/src/lib/html_if.rs @@ -15,7 +15,7 @@ // You should have received ba copy of the GNU Lesser General Public License // along with Crowbook. If not, see . -use crate::book::{compile_str, Book}; +use crate::book::Book; use crate::book_renderer::BookRenderer; use crate::error::{Error, Result, Source}; use crate::html::Highlight; @@ -261,74 +261,71 @@ return crowbook_return_variable.replace(/<\\/ul>
    /g, '');\n", self.html.render_end_notes(&mut content, "section", ""); // Render the CSS - let template_css = compile_str( - self.html.book.get_template("html.css")?.as_ref(), + let template_css_src = self.html.book.get_template("html.css")?; + let template_css = self.html.book.compile_str( + template_css_src.as_ref(), &self.html.book.source, "html.css", )?; let mut data = self .html .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .insert_str("colors", self.html.book.get_template("html.css.colors")?); + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + data.insert("colors".into(), self.html.book.get_template("html.css.colors")?.into()); if let Ok(html_css_add) = self.html.book.options.get_str("html.css.add") { - data = data.insert_str("additional_code", html_css_add); + data.insert("additional_code".into(), html_css_add.into()); } - let data = data.build(); - let mut res: Vec = vec![]; - template_css.render_data(&mut res, &data)?; - let css = String::from_utf8_lossy(&res); + let css:String = template_css.render(&data).to_string()?; // Render the JS - let template_js = compile_str( - self.html.book.get_template("html.if.js")?.as_ref(), + let template_js_src = self.html.book.get_template("html.if.js")?; + let template_js = self.html.book.compile_str( + template_js_src.as_ref(), &self.html.book.source, "html.standalone.js", )?; - let data = self + let mut data = self .html .book - .get_metadata(|s| Ok(s.to_owned()))? - .insert_bool("one_chapter", true) - .insert_str("js_prelude", std::mem::take(&mut self.fn_defs)) - .insert_str( - "new_game", - self.html.book.get_template("html.if.new_game").unwrap(), - ) - .insert_str( - "common_script", - self.html.book.get_template("html.js").unwrap().as_ref(), - ) - .build(); - let mut res: Vec = vec![]; - template_js.render_data(&mut res, &data)?; - let js = String::from_utf8_lossy(&res); + .get_metadata(|s| Ok(s.to_owned()))?; + data.insert("one_chapter".into(), true.into()); + data.insert("js_prelude".into(), self.fn_defs.clone().into()); + data.insert( + "new_game".into(), + self.html.book.get_template("html.if.new_game").unwrap().into(), + ); + data.insert( + "common_script".into(), + self.html.book.get_template("html.js").unwrap().into(), + ); + let js = template_js.render(&data).to_string()?; + // Render the HTML document - let mut mapbuilder = self + let mut data = self .html .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .insert_str("content", content) - .insert_str("script", js) - .insert_bool(self.html.book.options.get_str("lang").unwrap(), true) - .insert_bool("one_chapter", true) - .insert_str("style", css.as_ref()) - .insert_str( - "print_style", - self.html.book.get_template("html.css.print").unwrap(), - ) - .insert_str("footer", HtmlRenderer::get_footer(self)?) - .insert_str("header", HtmlRenderer::get_header(self)?) - .insert_bool("has_toc", false); + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + data.insert("content".into(), content.into()); + data.insert("script".into(), js.into()); + data.insert(self.html.book.options.get_str("lang").unwrap().into(), true.into()); + data.insert("one_chapter".into(), true.into()); + data.insert("style".into(), css.into()); + data.insert( + "print_style".into(), + self.html.book.get_template("html.css.print").unwrap().into(), + ); + data.insert("footer".into(), HtmlRenderer::get_footer(self)?.into()); + data.insert("header".into(), HtmlRenderer::get_header(self)?.into()); + data.insert("has_toc".into(), false.into()); if let Ok(favicon) = self.html.book.options.get_path("html.icon") { let favicon = self .html .handler .map_image(&self.html.book.source, favicon)?; - mapbuilder = mapbuilder.insert_str( - "favicon", - format!(""), + data.insert( + "favicon".into(), + format!("").into(), ); } if self.html.highlight == Highlight::Js { @@ -338,26 +335,20 @@ return crowbook_return_variable.replace(/<\\/ul>
      /g, '');\n", .get_template("html.highlight.js")? .as_bytes()); let highlight_js = format!("data:text/javascript;base64,{highlight_js}"); - mapbuilder = mapbuilder - .insert_bool("highlight_code", true) - .insert_str( - "highlight_css", - self.html.book.get_template("html.highlight.css")?, - ) - .insert_str("highlight_js", highlight_js); + data.insert("highlight_code".into(), true.into()); + data.insert( + "highlight_css".into(), + self.html.book.get_template("html.highlight.css")?.into(), + ); + data.insert("highlight_js".into(), highlight_js.into()); } - let data = mapbuilder.build(); - let template = compile_str( - self.html - .book - .get_template("html.standalone.template")? - .as_ref(), + let template_src = self.html.book.get_template("html.standalone.template")?; + let template = self.html.book.compile_str( + template_src.as_ref(), &self.html.book.source, "html.standalone.template", )?; - let mut res = vec![]; - template.render_data(&mut res, &data)?; - Ok(String::from_utf8_lossy(&res).into_owned()) + Ok(template.render(&data).to_string()?) } } diff --git a/src/lib/html_single.rs b/src/lib/html_single.rs index b4dcc7d..4fed7bd 100644 --- a/src/lib/html_single.rs +++ b/src/lib/html_single.rs @@ -15,7 +15,7 @@ // You should have received ba copy of the GNU Lesser General Public License // along with Crowbook. If not, see . -use crate::book::{compile_str, Book}; +use crate::book::Book; use crate::book_renderer::BookRenderer; use crate::error::{Error, Result, Source}; use crate::html::Highlight; @@ -215,93 +215,91 @@ impl<'a> HtmlSingleRenderer<'a> { } // Render the CSS - let template_css = compile_str( - self.html.book.get_template("html.css")?.as_ref(), + let template_css_src = self.html.book.get_template("html.css")?; + let template_css = self.html.book.compile_str( + template_css_src.as_ref(), &self.html.book.source, "html.css", )?; let mut data = self .html .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .insert_str("colors", self.html.book.get_template("html.css.colors")?); + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + data.insert("colors".into(), self.html.book.get_template("html.css.colors")?.into()); if let Ok(html_css_add) = self.html.book.options.get_str("html.css.add") { - data = data.insert_str("additional_code", html_css_add); + data.insert("additional_code".into(), html_css_add.into()); } - let data = data.build(); - let mut res: Vec = vec![]; - template_css.render_data(&mut res, &data)?; - let css = String::from_utf8_lossy(&res); + let css = template_css.render(&data).to_string()?; // Render the JS - let template_js = compile_str( - self.html.book.get_template("html.standalone.js")?.as_ref(), + let template_js_src = self.html.book.get_template("html.standalone.js")?; + let template_js = self.html.book.compile_str( + template_js_src.as_ref(), &self.html.book.source, "html.standalone.js", )?; - let data = self + let mut data = self .html .book - .get_metadata(|s| Ok(s.to_owned()))? - .insert_str("book_svg", book_svg.clone()) - .insert_str("pages_svg", pages_svg.clone()) - .insert_bool( - "one_chapter", + .get_metadata(|s| Ok(s.to_owned()))?; + data.insert("book_svg".into(), book_svg.clone().into()); + data.insert("pages_svg".into(), pages_svg.clone().into()); + data.insert( + "one_chapter".into(), self.html .book .options .get_bool("html.standalone.one_chapter") - .unwrap(), - ) - .insert_str( - "common_script", - self.html.book.get_template("html.js").unwrap().as_ref(), - ) - .build(); - let mut res: Vec = vec![]; - template_js.render_data(&mut res, &data)?; - let js = String::from_utf8_lossy(&res); + .unwrap() + .into(), + ); + data.insert( + "common_script".into(), + self.html.book.get_template("html.js").unwrap().into(), + ); + let js = template_js.render(&data).to_string()?; // Render the HTML document - let mut mapbuilder = self + let mut data = self .html .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .insert_str("content", content) - .insert_str("script", js) - .insert_bool(self.html.book.options.get_str("lang").unwrap(), true) - .insert_bool( - "one_chapter", + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + data.insert("content".into(), content.into()); + data.insert("script".into(), js.into()); + data.insert(self.html.book.options.get_str("lang").unwrap().into(), true.into()); + data.insert( + "one_chapter".into(), self.html .book .options .get_bool("html.standalone.one_chapter") - .unwrap(), - ) - .insert_str("style", css.as_ref()) - .insert_str( - "print_style", - self.html.book.get_template("html.css.print").unwrap(), - ) - .insert_str("menu_svg", menu_svg) - .insert_str("book_svg", book_svg) - .insert_str("pages_svg", pages_svg) - .insert_str("json_data", self.html.get_json_ld()?) - .insert_str("footer", HtmlRenderer::get_footer(self)?) - .insert_str("header", HtmlRenderer::get_header(self)?); + .unwrap() + .into(), + ); + data.insert("style".into(), css.into()); + data.insert( + "print_style".into(), + self.html.book.get_template("html.css.print").unwrap().into(), + ); + data.insert("menu_svg".into(), menu_svg.clone().into()); + data.insert("book_svg".into(), book_svg.clone().into()); + data.insert("pages_svg".into(), pages_svg.clone().into()); + data.insert("json_data".into(), self.html.get_json_ld()?.into()); + data.insert("footer".into(), HtmlRenderer::get_footer(self)?.into()); + data.insert("header".into(), HtmlRenderer::get_header(self)?.into()); if let Ok(favicon) = self.html.book.options.get_path("html.icon") { let favicon = self .html .handler .map_image(&self.html.book.source, favicon)?; - mapbuilder = mapbuilder.insert_str( - "favicon", - format!(""), + data.insert( + "favicon".into(), + format!("").into(), ); } if !self.html.toc.is_empty() { - mapbuilder = mapbuilder.insert_bool("has_toc", true); - mapbuilder = mapbuilder.insert_str("toc", toc) + data.insert("has_toc".into(), true.into()); + data.insert("toc".into(), toc.into()); } if self.html.highlight == Highlight::Js { let highlight_js = misc::u8_to_base64(&self @@ -310,26 +308,20 @@ impl<'a> HtmlSingleRenderer<'a> { .get_template("html.highlight.js")? .as_bytes()); let highlight_js = format!("data:text/javascript;base64,{highlight_js}"); - mapbuilder = mapbuilder - .insert_bool("highlight_code", true) - .insert_str( - "highlight_css", - self.html.book.get_template("html.highlight.css")?, - ) - .insert_str("highlight_js", highlight_js); + data.insert("highlight_code".into(), true.into()); + data.insert( + "highlight_css".into(), + self.html.book.get_template("html.highlight.css")?.into(), + ); + data.insert("highlight_js".into(), highlight_js.into()); } - let data = mapbuilder.build(); - let template = compile_str( - self.html - .book - .get_template("html.standalone.template")? - .as_ref(), + let template_src = self.html.book.get_template("html.standalone.template")?; + let template = self.html.book.compile_str( + template_src.as_ref(), &self.html.book.source, "html.standalone.template", )?; - let mut res = vec![]; - template.render_data(&mut res, &data)?; - Ok(String::from_utf8_lossy(&res).into_owned()) + Ok(template.render(&data).to_string()?) } } diff --git a/src/lib/latex.rs b/src/lib/latex.rs index d1fdafa..c74d46c 100644 --- a/src/lib/latex.rs +++ b/src/lib/latex.rs @@ -15,7 +15,7 @@ // You should have received ba copy of the GNU Lesser General Public License // along with Crowbook. If not, see . -use crate::book::{compile_str, Book}; +use crate::book::Book; use crate::book_renderer::BookRenderer; use crate::error::{Error, Result, Source}; use crate::number::Number; @@ -39,7 +39,7 @@ use std::iter::Iterator; /// LaTeX renderer pub struct LatexRenderer<'a> { - book: &'a Book, + book: &'a Book<'a>, current_chapter: Number, handler: ResourceHandler, source: Source, @@ -210,96 +210,80 @@ impl<'a> LatexRenderer<'a> { } }); - let template = compile_str( - self.book.get_template("tex.template")?.as_ref(), + let template_src = self.book.get_template("tex.template")?; + let template = self.book.compile_str( + template_src.as_ref(), &self.book.source, "tex.template", - )?; + )?; let mut data = self .book - .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))? - .insert_str("content", content) - .insert_str("class", self.book.options.get_str("tex.class").unwrap()) - .insert_bool( - "tex_title", - self.book.options.get_bool("tex.title").unwrap(), - ) - .insert_str( - "papersize", - self.book.options.get_str("tex.paper.size").unwrap(), - ) - .insert_bool( - "stdpage", - self.book.options.get_bool("tex.stdpage").unwrap(), - ) - .insert_bool("use_url", self.book.features.url) - .insert_bool("use_taskitem", self.book.features.taskitem) - .insert_bool("use_tables", self.book.features.table) - .insert_bool("use_codeblocks", self.book.features.codeblock) - .insert_bool("use_images", self.book.features.image) - .insert_bool("use_strikethrough", self.book.features.strikethrough) - .insert_str("tex_lang", tex_lang); + .get_metadata(|s| self.render_vec(&Parser::new().parse_inline(s)?))?; + data.insert("content".into(), content.into()); + data.insert("class".into(), self.book.options.get_str("tex.class").unwrap().into()); + data.insert("tex_title".into(), self.book.options.get_bool("tex.title").unwrap().into()); + data.insert("papersize".into(), self.book.options.get_str("tex.paper.size").unwrap().into()); + data.insert("stdpage".into(), self.book.options.get_bool("tex.stdpage").unwrap().into()); + data.insert("use_url".into(), self.book.features.url.into()); + data.insert("use_taskitem".into(), self.book.features.taskitem.into()); + data.insert("use_tables".into(), self.book.features.table.into()); + data.insert("use_codeblocks".into(), self.book.features.codeblock.into()); + data.insert("use_images".into(), self.book.features.image.into()); + data.insert("use_strikethrough".into(), self.book.features.strikethrough.into()); + data.insert("tex_lang".into(), tex_lang.into()); if let Ok(tex_tmpl_add) = self.book.options.get_str("tex.template.add") { - data = data.insert_str("additional_code", tex_tmpl_add); + data.insert("additional_code".into(), tex_tmpl_add.into()); } if let Ok(tex_font_size) = self.book.options.get_i32("tex.font.size") { - data = data - .insert_bool("has_tex_size", true) - .insert_str("tex_size", format!("{tex_font_size}")); + data.insert("has_tex_size".into(), true.into()); + data.insert("tex_size".into(), format!("{tex_font_size}").into()); } // If class isn't book, set open_any to true, so margins are symetric. let mut book = false; if self.book.options.get_str("tex.class").unwrap() == "book" { - data = data.insert_bool("book", true); + data.insert("book".into(), true.into()); book = true; } - data = data - .insert_str( - "margin_left", + data.insert( + "margin_left".into(), self.book .options .get_str("tex.margin.left") - .unwrap_or(if book { "2.5cm" } else { "2cm" }), - ) - .insert_str( - "margin_right", + .unwrap_or(if book { "2.5cm" } else { "2cm" }).into(), + ); + data.insert( + "margin_right".into(), self.book .options .get_str("tex.margin.right") - .unwrap_or(if book { "1.5cm" } else { "2cm" }), - ) - .insert_str( - "margin_bottom", - self.book.options.get_str("tex.margin.bottom").unwrap(), - ) - .insert_str( - "margin_top", - self.book.options.get_str("tex.margin.top").unwrap(), - ); + .unwrap_or(if book { "1.5cm" } else { "2cm" }).into(), + ); + data.insert( + "margin_bottom".into(), + self.book.options.get_str("tex.margin.bottom").unwrap().into(), + ); + data.insert( + "margin_top".into(), + self.book.options.get_str("tex.margin.top").unwrap().into(), + ); if let Ok(chapter_name) = self.book.options.get_str("rendering.chapter") { - data = data.insert_str("chapter_name", chapter_name); + data.insert("chapter_name".into(), chapter_name.into()); } if let Ok(part_name) = self.book.options.get_str("rendering.part") { - data = data.insert_str("part_name", part_name); + data.insert("part_name".into(), part_name.into()); } if self.book.options.get_bool("rendering.initials") == Ok(true) { - data = data.insert_bool("initials", true); + data.insert("initials".into(), true.into()); } // Insert xelatex if tex.command is set to xelatex or tectonic if (self.book.options.get_str("tex.command") == Ok("xelatex")) | (self.book.options.get_str("tex.command") == Ok("tectonic")) { - data = data.insert_bool("xelatex", true); - } - let data = data.build(); - let mut res: Vec = vec![]; - template.render_data(&mut res, &data)?; - match String::from_utf8(res) { - Err(_) => panic!("{}", lformat!("generated LaTeX was not valid utf-8")), - Ok(res) => Ok(res), + data.insert("xelatex".into(), true.into()); } + Ok(template.render(&data).to_string()?) } } diff --git a/src/lib/templates.rs b/src/lib/templates.rs index 2060b27..bb261c0 100644 --- a/src/lib/templates.rs +++ b/src/lib/templates.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2016, 2017 Élisabeth HENRY. +// Copyright (C) 2016-2023 Élisabeth HENRY. // // This file is part of Crowbook. // @@ -7,7 +7,7 @@ // by the Free Software Foundation, either version 2.1 of the License, or // (at your option) any later version. // -// Caribon is distributed in the hope that it will be useful, +// Crowbook is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details.