aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Letan <contact@thomasletan.fr>2020-02-02 18:05:56 +0100
committerThomas Letan <contact@thomasletan.fr>2020-02-02 18:05:56 +0100
commite2b6a8a20ee90eaed575495cfc0d0665227a3ed7 (patch)
treee61aa0f977cfd2ef2f3f3e96268ef0ffb5aa2a45
parentfeature: List dependencies of a celtchar document (diff)
refactor: Rework libceltchar organization
In this new iteration of libceltchar, there is now a very generic `BookWriter` that the `EpubWriter` extends. This paves the way towards a static website generator. In addition, we start using `rustfmt`, which means the diff is actually a bit larger that what it shall be.
-rw-r--r--app/src/filesystem.rs74
-rw-r--r--app/src/main.rs31
-rw-r--r--lib/Cargo.toml2
-rw-r--r--lib/src/assets.rs22
-rw-r--r--lib/src/epub.rs153
-rw-r--r--lib/src/error.rs6
-rw-r--r--lib/src/lib.rs14
-rw-r--r--lib/src/project.rs64
-rw-r--r--lib/src/render.rs54
-rw-r--r--lib/src/writer.rs24
-rw-r--r--rustfmt.toml1
-rw-r--r--templates/epub/chapter.xhtml (renamed from templates/chapter.xhtml)0
-rw-r--r--templates/epub/container.xml (renamed from templates/container.xml)0
-rw-r--r--templates/epub/content.opf (renamed from templates/content.opf)0
-rw-r--r--templates/epub/main.css (renamed from templates/main.css)0
-rw-r--r--templates/epub/toc.ncx (renamed from templates/toc.ncx)0
16 files changed, 228 insertions, 217 deletions
diff --git a/app/src/filesystem.rs b/app/src/filesystem.rs
index 066e2d2..1ce55fd 100644
--- a/app/src/filesystem.rs
+++ b/app/src/filesystem.rs
@@ -1,15 +1,15 @@
-use std::path::{PathBuf};
-use std::fs;
-use std::fs::{canonicalize};
use std::env::{current_dir, set_current_dir};
+use std::fs;
+use std::fs::canonicalize;
+use std::path::PathBuf;
-use libceltchar::{Loader, Raise, Error, Project, Chapter, Cover};
+use libceltchar::{Chapter, Cover, Error, Loader, Project, Raise};
const PROJECT_FILE : &str = "Book.toml";
pub struct Fs;
pub fn find_root() -> Result<PathBuf, Error> {
- let mut cwd: PathBuf = current_dir().or_raise("cannot get current directory")?;
+ let mut cwd : PathBuf = current_dir().or_raise("cannot get current directory")?;
loop {
cwd.push(PROJECT_FILE); // (*)
@@ -27,38 +27,42 @@ pub fn find_root() -> Result<PathBuf, Error> {
// `pop` returns false, we are at the root of the current FS, and
// there is no project file to find.
if !cwd.pop() {
- return Err(Error::new("could not find Book.toml"))
+ return Err(Error::new("could not find Book.toml"));
}
}
}
}
-fn canonicalize_chapter(
- chapter : &Chapter<Vec<PathBuf>>
-) -> Result<Chapter<Vec<PathBuf>>, Error> {
+fn canonicalize_chapter(chapter : &Chapter<Vec<PathBuf>>) -> Result<Chapter<Vec<PathBuf>>, Error> {
let title = chapter.title.clone();
Ok(Chapter {
- title: title,
- content: chapter.content
+ title : title,
+ content : chapter
+ .content
.iter()
.map(|x| canonicalize(x).or_raise(&format!("Could not canonicalize {:?}", x)))
- .collect::<Result<_, Error>>()?
+ .collect::<Result<_, Error>>()?,
})
}
fn canonicalize_project(
- project : Project<PathBuf, Vec<PathBuf>>
+ project : Project<PathBuf, Vec<PathBuf>>,
) -> Result<Project<PathBuf, Vec<PathBuf>>, Error> {
Ok(Project {
- author: project.author,
- title: project.title,
- cover: project.cover.map(canonicalize)
+ author : project.author,
+ title : project.title,
+ cover : project
+ .cover
+ .map(canonicalize)
.map_or(Ok(None), |r| r.map(Some))
.or_raise("…")?,
- numbering: project.numbering,
- chapters: project.chapters.iter().map(canonicalize_chapter)
+ numbering : project.numbering,
+ chapters : project
+ .chapters
+ .iter()
+ .map(canonicalize_chapter)
.collect::<Result<_, Error>>()?,
- language: project.language,
+ language : project.language,
})
}
@@ -67,49 +71,39 @@ impl Loader for Fs {
type CovId = PathBuf;
type DocId = PathBuf;
- fn load_project(
- &self,
- id : &PathBuf
- ) -> Result<Project<PathBuf, Vec<PathBuf>>, Error> {
+ fn load_project(&self, id : &PathBuf) -> Result<Project<PathBuf, Vec<PathBuf>>, Error> {
let cwd = current_dir().or_raise("could not get current dir")?;
- let input = fs::read_to_string(PROJECT_FILE)
- .or_raise("found Book.toml, but cannot read it")?;
+ let input =
+ fs::read_to_string(PROJECT_FILE).or_raise("found Book.toml, but cannot read it")?;
// We have to modify set the current directory to the PROJECT_FILE directory,
// otherwise `canonicalize` will not work.
set_current_dir(id).or_raise("could not change the current directory")?;
let res = canonicalize_project(
- toml::from_str(input.as_str())
- .or_raise(&format!("could not parse Book.toml"))?
+ toml::from_str(input.as_str()).or_raise(&format!("could not parse Book.toml"))?,
)?;
set_current_dir(cwd).or_raise("could not change the current directory")?;
Ok(res)
}
- fn load_cover(
- &self,
- id : &PathBuf
- ) -> Result<Cover, Error> {
- let extension = id.extension()
+ fn load_cover(&self, id : &PathBuf) -> Result<Cover, Error> {
+ let extension = id
+ .extension()
.or_raise("cover lacks an extension")?
.to_str()
.or_raise("cover extension is not valid utf-8")?;
- let content = fs::read(id)
- .or_raise(&format!("could not read cover from {:?}", id))?;
+ let content = fs::read(id).or_raise(&format!("could not read cover from {:?}", id))?;
Ok(Cover {
- extension: String::from(extension),
- content: content,
+ extension : String::from(extension),
+ content : content,
})
}
- fn load_document(
- &self,
- id : &PathBuf
- ) -> Result<String, Error> {
+ fn load_document(&self, id : &PathBuf) -> Result<String, Error> {
fs::read_to_string(id).or_raise(&format!("Could not read {:?}", id))
}
}
diff --git a/app/src/main.rs b/app/src/main.rs
index ea8fcbe..8b2f48e 100644
--- a/app/src/main.rs
+++ b/app/src/main.rs
@@ -1,20 +1,20 @@
extern crate clap;
-extern crate serde_json;
-extern crate toml;
extern crate libceltchar;
extern crate ogmarkup;
+extern crate serde_json;
extern crate tera;
+extern crate toml;
use std::path::PathBuf;
use clap::{App, SubCommand};
-use libceltchar::{Loader, Error, Zip, Project, EpubWriter};
+use libceltchar::{EpubWriter, Error, Loader, Project, Zip};
#[cfg(debug_assertions)]
-use std::env::current_dir;
-#[cfg(debug_assertions)]
use libceltchar::Raise;
+#[cfg(debug_assertions)]
+use std::env::current_dir;
mod filesystem;
use crate::filesystem::{find_root, Fs};
@@ -37,14 +37,14 @@ fn deps() -> Result<(), Error> {
Ok(())
}
-fn build(assets : &PathBuf) -> Result<(), Error> {
+fn build_epub(assets : &PathBuf) -> Result<(), Error> {
let root = find_root()?;
let loader = Fs;
let project = Project::load_and_render(&root, &loader)?;
let mut zip_writer = Zip::init()?;
- zip_writer.generate(&project, assets)?;
+ zip_writer.generate_epub(&project, assets)?;
Ok(())
}
@@ -64,22 +64,19 @@ fn main() -> Result<(), Error> {
.version("0.1")
.author("Thomas Letan")
.about("A tool to generate novels")
- .subcommand(SubCommand::with_name("new")
- .about("Create a new celtchar document"))
- .subcommand(SubCommand::with_name("build")
- .about("Build a celtchar document"))
- .subcommand(SubCommand::with_name("deps")
- .about("List dependencies of a celtchar document"))
+ .subcommand(SubCommand::with_name("new").about("Create a new celtchar document"))
+ .subcommand(SubCommand::with_name("build").about("Build a celtchar document"))
+ .subcommand(SubCommand::with_name("deps").about("List dependencies of a celtchar document"))
.get_matches();
let (subcommand, _args) = matches.subcommand();
- let assets: PathBuf = get_assets()?;
+ let assets : PathBuf = get_assets()?;
match subcommand {
- "build" => build(&assets)?,
- "deps" => deps()?,
- _ => unimplemented!(),
+ "build" => build_epub(&assets)?,
+ "deps" => deps()?,
+ _ => unimplemented!(),
}
Ok(())
diff --git a/lib/Cargo.toml b/lib/Cargo.toml
index 3786d25..3f385ee 100644
--- a/lib/Cargo.toml
+++ b/lib/Cargo.toml
@@ -7,6 +7,6 @@ authors = ["Thomas Letan <contact@thomasletan.fr>"]
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
-tera = "0.11"
+tera = "1.0"
zip = "0.5"
ogmarkup = { version = "0.1", git = "https://git.sr.ht/~lthms/ogmarkup" } \ No newline at end of file
diff --git a/lib/src/assets.rs b/lib/src/assets.rs
new file mode 100644
index 0000000..af21d05
--- /dev/null
+++ b/lib/src/assets.rs
@@ -0,0 +1,22 @@
+use crate::error::{Error, Raise};
+use std::path::PathBuf;
+
+pub fn template_dir(assets : &PathBuf) -> Result<String, Error> {
+ let mut res = assets.clone();
+
+ res.push("templates");
+ res.push("**");
+ res.push("*");
+
+ res.to_str()
+ .map(String::from)
+ .or_raise("Compute template dir")
+}
+
+pub fn fonts_dir(assets : &PathBuf) -> Result<PathBuf, Error> {
+ let mut res = assets.clone();
+
+ res.push("fonts");
+
+ Ok(res)
+}
diff --git a/lib/src/epub.rs b/lib/src/epub.rs
index 518bdbf..8ef5363 100644
--- a/lib/src/epub.rs
+++ b/lib/src/epub.rs
@@ -3,44 +3,22 @@ use std::path::PathBuf;
use serde_json::json;
-use tera::{Tera, Context};
+use tera::{Context, Tera};
-use crate::error::{Raise, Error};
-use crate::project::{Project, Chapter, Cover, Language};
+use crate::error::{Error, Raise};
+use crate::project::{Chapter, Cover, Language, Project};
+use std::collections::HashSet;
+use std::fs::File;
use zip::write::FileOptions;
use zip::ZipWriter;
-use std::fs::File;
-use std::collections::HashSet;
-const EPUB_MIMETYPE: &'static str = "application/epub+zip";
+use crate::assets::{fonts_dir, template_dir};
+use crate::writer::BookWriter;
-pub trait EpubWriter {
- fn write_template(
- &mut self,
- dst : &PathBuf,
- tera : & Tera,
- template : &str,
- ctx : &Context,
- ) -> Result<(), Error> {
- let content = tera.render(template, ctx)
- .or_raise(&format!("cannot render {}", template))?;
-
- self.write_bytes(dst, content.as_bytes())
- }
-
- fn write_file(
- &mut self,
- dst : &PathBuf,
- src : &PathBuf,
- ) -> Result<(), Error>;
-
- fn write_bytes(
- &mut self,
- dst : &PathBuf,
- input : &[u8]
- ) -> Result<(), Error> ;
+const EPUB_MIMETYPE : &'static str = "application/epub+zip";
+pub trait EpubWriter: BookWriter {
fn create_mimetype(&mut self) -> Result<(), Error> {
self.write_bytes(&PathBuf::from("mimetype"), EPUB_MIMETYPE.as_bytes())
}
@@ -49,7 +27,7 @@ pub trait EpubWriter {
self.write_template(
&PathBuf::from("META-INF/container.xml"),
tera,
- "container.xml",
+ "epub/container.xml",
&Context::default(),
)
}
@@ -61,7 +39,9 @@ pub trait EpubWriter {
numbering : bool,
lang : &Language,
) -> Result<(), Error> {
- chapters.iter().enumerate()
+ chapters
+ .iter()
+ .enumerate()
.map(|(idx, c)| {
let mut ctx = Context::new();
ctx.insert("number", &(idx + 1));
@@ -74,7 +54,7 @@ pub trait EpubWriter {
self.write_template(
&PathBuf::from(format!("OEBPS/Text/{}", path)),
tera,
- "chapter.xhtml",
+ "epub/chapter.xhtml",
&ctx,
)?;
@@ -101,19 +81,28 @@ pub trait EpubWriter {
self.write_bytes(&dst, cover.content.as_slice())
}
- fn generate(&mut self, project : &Project<Cover, String>, assets : &PathBuf) -> Result<(), Error> {
-
- let tera = compile_templates!(template_dir(assets)?.as_str());
+ fn generate_epub(
+ &mut self,
+ project : &Project<Cover, String>,
+ assets : &PathBuf,
+ ) -> Result<(), Error> {
+ let tera =
+ Tera::new(template_dir(assets)?.as_str()).or_raise("Could not build templates")?;
self.create_mimetype()?;
self.create_container(&tera)?;
- self.create_chapters(&tera, &project.chapters, project.numbering.unwrap_or(false), &project.language)?;
+ self.create_chapters(
+ &tera,
+ &project.chapters,
+ project.numbering.unwrap_or(false),
+ &project.language,
+ )?;
self.write_template(
&PathBuf::from("OEBPS/Style/main.css"),
&tera,
- "main.css",
+ "epub/main.css",
&Context::new(),
)?;
@@ -129,14 +118,20 @@ pub trait EpubWriter {
self.install_fonts(assets, &fonts)?;
- let files = project.chapters.iter().enumerate()
+ let files = project
+ .chapters
+ .iter()
+ .enumerate()
.map(|(idx, _)| idx)
.collect::<Vec<usize>>();
let mut ctx = Context::new();
ctx.insert("title", &project.title);
ctx.insert("author", &project.author);
- ctx.insert("cover_extension", &project.cover.as_ref().map(|x| x.extension.clone()));
+ ctx.insert(
+ "cover_extension",
+ &project.cover.as_ref().map(|x| x.extension.clone()),
+ );
ctx.insert("files", &files);
ctx.insert("fonts", &fonts);
ctx.insert("language", &project.language);
@@ -144,47 +139,31 @@ pub trait EpubWriter {
self.write_template(
&PathBuf::from("OEBPS/content.opf"),
&tera,
- "content.opf",
+ "epub/content.opf",
&ctx,
)?;
- let chaps: Vec<_> = project.chapters.iter().enumerate()
- .map(|(idx, chapter)| json!({
- "index": idx,
- "title": chapter.title,
- }))
+ let chaps : Vec<_> = project
+ .chapters
+ .iter()
+ .enumerate()
+ .map(|(idx, chapter)| {
+ json!({
+ "index": idx,
+ "title": chapter.title,
+ })
+ })
.collect();
let mut ctx = Context::new();
ctx.insert("chapters", &chaps);
- self.write_template(
- &PathBuf::from("OEBPS/toc.ncx"),
- &tera,
- "toc.ncx",
- &ctx,
- )?;
+ self.write_template(&PathBuf::from("OEBPS/toc.ncx"), &tera, "epub/toc.ncx", &ctx)?;
Ok(())
}
}
-fn template_dir(assets : &PathBuf) -> Result<String, Error> {
- let mut res = assets.clone();
-
- res.push("templates");
- res.push("**");
- res.push("*");
-
- res.to_str().map(String::from).ok_or(Error(format!("Compute template dir")))
-}
-
-fn fonts_dir(assets : &PathBuf) -> Result<PathBuf, Error> {
- let mut res = assets.clone();
-
- res.push("fonts");
-
- Ok(res)
-}
+impl<W> EpubWriter for W where W : BookWriter {}
pub struct Zip {
output : ZipWriter<File>,
@@ -203,8 +182,9 @@ impl Zip {
fn create_parent(&mut self, dst : &PathBuf) -> Result<(), Error> {
if let Some(dir) = dst.parent() {
- if self.dirs.contains(dir) {
- self.output.add_directory_from_path(dir, FileOptions::default())
+ if self.dirs.contains(dir) {
+ self.output
+ .add_directory_from_path(dir, FileOptions::default())
.or_raise(&format!("Could not create directory {:?}", dir))?;
self.dirs.insert(dir.to_path_buf());
}
@@ -214,38 +194,35 @@ impl Zip {
}
}
-impl EpubWriter for Zip {
- fn write_bytes(
- &mut self,
- dst : &PathBuf,
- input : &[u8]
- ) -> Result<(), Error> {
+impl BookWriter for Zip {
+ fn write_bytes(&mut self, dst : &PathBuf, input : &[u8]) -> Result<(), Error> {
self.create_parent(dst)?;
- self.output.start_file_from_path(dst, FileOptions::default())
+ self.output
+ .start_file_from_path(dst, FileOptions::default())
.or_raise(&format!("Could not add file {:?} to archive", dst))?;
- self.output.write_all(input)
+ self.output
+ .write_all(input)
.or_raise(&format!("Could not write {:?} content", dst))?;
Ok(())
}
- fn write_file(
- &mut self,
- dst : &PathBuf,
- src : &PathBuf,
- ) -> Result<(), Error> {
+ fn write_file(&mut self, dst : &PathBuf, src : &PathBuf) -> Result<(), Error> {
let mut buffer = Vec::new();
let mut f = File::open(src).or_raise(&format!("Could not open {:?}", src))?;
- f.read_to_end(&mut buffer).or_raise(&format!("Could not read {:?} content", src))?;
+ f.read_to_end(&mut buffer)
+ .or_raise(&format!("Could not read {:?} content", src))?;
self.create_parent(dst)?;
- self.output.start_file_from_path(dst, FileOptions::default())
+ self.output
+ .start_file_from_path(dst, FileOptions::default())
.or_raise(&format!("Could not add file {:?} to archive", dst))?;
- self.output.write_all(buffer.as_ref())
+ self.output
+ .write_all(buffer.as_ref())
.or_raise(&format!("Could not write {:?} content", dst))?;
Ok(())
diff --git a/lib/src/error.rs b/lib/src/error.rs
index d92077e..b8fbf11 100644
--- a/lib/src/error.rs
+++ b/lib/src/error.rs
@@ -10,13 +10,13 @@ impl Error {
pub trait Raise {
type Out;
- fn or_raise(self, msg: &str) -> Self::Out;
+ fn or_raise(self, msg : &str) -> Self::Out;
}
impl<T> Raise for Option<T> {
type Out = Result<T, Error>;
- fn or_raise(self, msg: &str) -> Result<T, Error> {
+ fn or_raise(self, msg : &str) -> Result<T, Error> {
self.ok_or(Error(String::from(msg)))
}
}
@@ -24,7 +24,7 @@ impl<T> Raise for Option<T> {
impl<T, E> Raise for Result<T, E> {
type Out = Result<T, Error>;
- fn or_raise(self, msg: &str) -> Result<T, Error> {
+ fn or_raise(self, msg : &str) -> Result<T, Error> {
self.map_err(|_| Error(String::from(msg)))
}
}
diff --git a/lib/src/lib.rs b/lib/src/lib.rs
index e0a9644..9859f0d 100644
--- a/lib/src/lib.rs
+++ b/lib/src/lib.rs
@@ -1,14 +1,16 @@
extern crate ogmarkup;
-#[macro_use] extern crate tera;
-extern crate zip;
extern crate serde_derive;
extern crate serde_json;
+extern crate tera;
+extern crate zip;
-mod render;
+mod assets;
+mod epub;
mod error;
mod project;
-mod epub;
+mod render;
+mod writer;
-pub use error::{Error, Raise};
-pub use project::{Project, Chapter, Cover, Loader};
pub use epub::{EpubWriter, Zip};
+pub use error::{Error, Raise};
+pub use project::{Chapter, Cover, Loader, Project};
diff --git a/lib/src/project.rs b/lib/src/project.rs
index c3af473..0a00ab2 100644
--- a/lib/src/project.rs
+++ b/lib/src/project.rs
@@ -1,8 +1,8 @@
-use ogmarkup::typography::{Typography, FRENCH, ENGLISH};
+use ogmarkup::typography::{Typography, ENGLISH, FRENCH};
use serde_derive::{Deserialize, Serialize};
+use crate::error::{Error, Raise};
use crate::render::Html;
-use crate::error::{Raise, Error};
#[derive(Debug, Serialize, Deserialize)]
pub enum Language {
@@ -30,19 +30,13 @@ pub trait Loader {
type DocId;
type ProjId;
- fn load_cover(
- &self,
- id : &Self::CovId
- ) -> Result<Cover, Error>;
+ fn load_cover(&self, id : &Self::CovId) -> Result<Cover, Error>;
- fn load_document(
- &self,
- id : &Self::DocId
- ) -> Result<String, Error>;
+ fn load_document(&self, id : &Self::DocId) -> Result<String, Error>;
fn load_project(
&self,
- id : &Self::ProjId
+ id : &Self::ProjId,
) -> Result<Project<Self::CovId, Vec<Self::DocId>>, Error>;
}
@@ -53,19 +47,16 @@ pub struct Chapter<I> {
}
impl<I> Chapter<Vec<I>> {
- fn load_and_render<T, L>(
- &self,
- loader : &L,
- typo : &T,
- ) -> Result<Chapter<String>, Error>
+ fn load_and_render<T, L>(&self, loader : &L, typo : &T) -> Result<Chapter<String>, Error>
where
T : Typography + ?Sized,
- L : Loader<DocId = I>
+ L : Loader<DocId = I>,
{
let title = &self.title;
let content = &self.content;
- let doc = content.iter()
+ let doc = content
+ .iter()
.map(|ref x| {
let input = loader.load_document(x)?;
ogmarkup::compile(&input, typo)
@@ -76,24 +67,24 @@ impl<I> Chapter<Vec<I>> {
.join("");
Ok(Chapter {
- title: title.clone(),
- content: doc,
+ title : title.clone(),
+ content : doc,
})
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Project<C, I> {
- pub author: String,
- pub title: String,
- pub chapters: Vec<Chapter<I>>,
- pub cover: Option<C>,
- pub numbering: Option<bool>,
- pub language: Language,
+ pub author : String,
+ pub title : String,
+ pub chapters : Vec<Chapter<I>>,
+ pub cover : Option<C>,
+ pub numbering : Option<bool>,
+ pub language : Language,
}
impl Project<Cover, String> {
- pub fn load_and_render<'input, L> (
+ pub fn load_and_render<'input, L>(
id : &L::ProjId,
loader : &L,
) -> Result<Project<Cover, String>, Error>
@@ -107,20 +98,23 @@ impl Project<Cover, String> {
let numbering = project.numbering;
let author = project.author;
let title = project.title;
- let cover = project.cover
+ let cover = project
+ .cover
.map(|x| loader.load_cover(&x).or_raise("cannot load the cover"))
.map_or(Ok(None), |r| r.map(Some))?;
- project.chapters.into_iter()
+ project
+ .chapters
+ .into_iter()
.map(|chapter| chapter.load_and_render(loader, typo))
.collect::<Result<Vec<Chapter<String>>, Error>>()
.map(|x| Project {
- author: author,
- title: title,
- chapters: x,
- cover: cover,
- numbering: numbering,
- language: lang,
+ author : author,
+ title : title,
+ chapters : x,
+ cover : cover,
+ numbering : numbering,
+ language : lang,
})
}
}
diff --git a/lib/src/render.rs b/lib/src/render.rs
index 3e737b0..5548d2d 100644
--- a/lib/src/render.rs
+++ b/lib/src/render.rs
@@ -1,10 +1,10 @@
-use ogmarkup::typography::Space;
use ogmarkup::generator::Output;
+use ogmarkup::typography::Space;
pub struct Html(String);
impl Html {
- fn push_str(&mut self, s: &str) -> () {
+ fn push_str(&mut self, s : &str) -> () {
self.0.push_str(s);
}
@@ -14,11 +14,11 @@ impl Html {
}
impl Output for Html {
- fn empty(input_size: usize) -> Html {
+ fn empty(input_size : usize) -> Html {
Html(String::with_capacity((15 * input_size) / 10))
}
- fn render_space(&mut self, space: Space) -> () {
+ fn render_space(&mut self, space : Space) -> () {
self.push_str(match space {
Space::Normal => " ",
Space::Nbsp => "&nbsp;",
@@ -26,48 +26,48 @@ impl Output for Html {
})
}
- fn render_word(&mut self, word: & str) -> () {
+ fn render_word(&mut self, word : &str) -> () {
self.push_str(word)
}
- fn render_mark(&mut self, mark: &str) -> () {
+ fn render_mark(&mut self, mark : &str) -> () {
self.push_str(mark)
}
- fn render_illformed(&mut self, err: &str) -> () {
+ fn render_illformed(&mut self, err : &str) -> () {
self.push_str(err)
}
- fn emph_template<F>(&mut self, format: F) -> ()
+ fn emph_template<F>(&mut self, format : F) -> ()
where
- F : FnOnce(&mut Html) -> ()
+ F : FnOnce(&mut Html) -> (),
{
self.push_str("<em>");
format(self);
self.push_str("</em>");
}
- fn strong_emph_template<F>(&mut self, format: F) -> ()
+ fn strong_emph_template<F>(&mut self, format : F) -> ()
where
- F : FnOnce(&mut Html) -> ()
+ F : FnOnce(&mut Html) -> (),
{
self.push_str("<strong>");
format(self);
self.push_str("</strong>");
}
- fn reply_template<F>(&mut self, reply: F, _author: &Option<&str>) -> ()
+ fn reply_template<F>(&mut self, reply : F, _author : &Option<&str>) -> ()
where
- F : FnOnce(&mut Html) -> ()
+ F : FnOnce(&mut Html) -> (),
{
self.push_str("<span class=\"reply\">");
reply(self);
self.push_str("</span>");
}
- fn thought_template<F>(&mut self, reply: F, author: &Option<&str>) -> ()
+ fn thought_template<F>(&mut self, reply : F, author : &Option<&str>) -> ()
where
- F : FnOnce(&mut Html) -> ()
+ F : FnOnce(&mut Html) -> (),
{
self.push_str("<span class=\"thought");
author.map(|a| {
@@ -79,9 +79,9 @@ impl Output for Html {
self.push_str("</span>");
}
- fn dialogue_template<F>(&mut self, reply: F, author: &Option<&str>) -> ()
+ fn dialogue_template<F>(&mut self, reply : F, author : &Option<&str>) -> ()
where
- F : FnOnce(&mut Html) -> ()
+ F : FnOnce(&mut Html) -> (),
{
self.push_str("<span class=\"dialogue");
author.map(|a| {
@@ -97,45 +97,45 @@ impl Output for Html {
self.push_str("</p><p>");
}
- fn illformed_inline_template<F>(&mut self, err: F) -> ()
+ fn illformed_inline_template<F>(&mut self, err : F) -> ()
where
- F: FnOnce(&mut Html) -> ()
+ F : FnOnce(&mut Html) -> (),
{
self.push_str("<span class=\"illformed_inline\">");
err(self);
self.push_str("</span>");
}
- fn paragraph_template<F>(&mut self, para: F) -> ()
+ fn paragraph_template<F>(&mut self, para : F) -> ()
where
- F : FnOnce(&mut Html) -> ()
+ F : FnOnce(&mut Html) -> (),
{
self.push_str("<p>");
para(self);
self.push_str("</p>");
}
- fn illformed_block_template<F>(&mut self, err: F) -> ()
+ fn illformed_block_template<F>(&mut self, err : F) -> ()
where
- F : FnOnce(&mut Html) -> ()
+ F : FnOnce(&mut Html) -> (),
{
self.push_str("<div class=\"illformed_block\">");
err(self);
self.push_str("</div>");
}
- fn story_template<F>(&mut self, story: F) -> ()
+ fn story_template<F>(&mut self, story : F) -> ()
where
- F : FnOnce(&mut Html) -> ()
+ F : FnOnce(&mut Html) -> (),
{
self.push_str("<div class=\"story\">");
story(self);
self.push_str("</div>");
}
- fn aside_template<F>(&mut self, cls: &Option<&str>, aside: F) -> ()
+ fn aside_template<F>(&mut self, cls : &Option<&str>, aside : F) -> ()
where
- F : FnOnce(&mut Html) -> ()
+ F : FnOnce(&mut Html) -> (),
{
self.push_str("<div class=\"aside");
cls.map(|c| {
diff --git a/lib/src/writer.rs b/lib/src/writer.rs
new file mode 100644
index 0000000..60d6cdc
--- /dev/null
+++ b/lib/src/writer.rs
@@ -0,0 +1,24 @@
+use std::path::PathBuf;
+use tera::{Context, Tera};
+
+use crate::error::{Error, Raise};
+
+pub trait BookWriter {
+ fn write_file(&mut self, dst : &PathBuf, src : &PathBuf) -> Result<(), Error>;
+
+ fn write_bytes(&mut self, dst : &PathBuf, input : &[u8]) -> Result<(), Error>;
+
+ fn write_template(
+ &mut self,
+ dst : &PathBuf,
+ tera : &Tera,
+ template : &str,
+ ctx : &Context,
+ ) -> Result<(), Error> {
+ let content = tera
+ .render(template, ctx)
+ .or_raise(&format!("cannot render {}", template))?;
+
+ self.write_bytes(dst, content.as_bytes())
+ }
+}
diff --git a/rustfmt.toml b/rustfmt.toml
new file mode 100644
index 0000000..98ad867
--- /dev/null
+++ b/rustfmt.toml
@@ -0,0 +1 @@
+space_before_colon = true \ No newline at end of file
diff --git a/templates/chapter.xhtml b/templates/epub/chapter.xhtml
index cbbc9da..cbbc9da 100644
--- a/templates/chapter.xhtml
+++ b/templates/epub/chapter.xhtml
diff --git a/templates/container.xml b/templates/epub/container.xml
index bad9982..bad9982 100644
--- a/templates/container.xml
+++ b/templates/epub/container.xml
diff --git a/templates/content.opf b/templates/epub/content.opf
index 5160fae..5160fae 100644
--- a/templates/content.opf
+++ b/templates/epub/content.opf
diff --git a/templates/main.css b/templates/epub/main.css
index 22903aa..22903aa 100644
--- a/templates/main.css
+++ b/templates/epub/main.css
diff --git a/templates/toc.ncx b/templates/epub/toc.ncx
index 1f86972..1f86972 100644
--- a/templates/toc.ncx
+++ b/templates/epub/toc.ncx