diff options
author | Thomas Letan <contact@thomasletan.fr> | 2019-08-14 15:46:45 +0200 |
---|---|---|
committer | Thomas Letan <contact@thomasletan.fr> | 2019-08-14 15:46:45 +0200 |
commit | 4a512ae35ce03d98c08fdf3f1531a438f99fb086 (patch) | |
tree | 5289ee92f0de31650d22de79aa4c1c74df9d5c98 | |
parent | refactor: Introduce the [EpubWriter] trait (diff) |
feature: Generate the epub archive
Previously, the content of the epub was generated in a dedicated build
directory, and it was necessary to make the final step (generating the
archive) manually. With this patch, the archive is generated, but no
compression method is performed.
We add a new dependency to the `zip-rs' crate, since the latter
appears to be the go-to solution for generating zip archives.
-rw-r--r-- | Cargo.lock | 79 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/epub.rs | 104 | ||||
-rw-r--r-- | src/main.rs | 8 |
4 files changed, 184 insertions, 8 deletions
@@ -1,6 +1,11 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] +name = "adler32" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "aho-corasick" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -86,6 +91,24 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "bzip2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "cc" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -101,6 +124,7 @@ dependencies = [ "serde_json 1.0.40 (registry+https://github.com/rust-lang/crates.io-index)", "tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "zip 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -134,6 +158,14 @@ dependencies = [ ] [[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "deunicode" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -216,6 +248,17 @@ version = "0.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "libflate" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] name = "maplit" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -320,6 +363,11 @@ dependencies = [ ] [[package]] +name = "podio" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "proc-macro2" version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -366,6 +414,11 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "rle-decode-fast" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "rustc-demangle" version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -475,6 +528,11 @@ dependencies = [ ] [[package]] +name = "take_mut" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "tera" version = "0.11.20" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -676,7 +734,20 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "zip" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libflate 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", +] + [metadata] +"checksum adler32 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "7e522997b529f05601e05166c07ed17789691f562762c7f3b987263d2dedee5c" "checksum aho-corasick 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "58fb5e95d83b38284460a5fda7d6470aa0b8844d283a0b614b8535e880800d2d" "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" @@ -688,10 +759,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum block-padding 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "6d4dc3af3ee2e12f3e5d224e5e1e3d73668abbeb69e566d361f7d5563a4fdf09" "checksum byte-tools 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" +"checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" +"checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f" "checksum cc 1.0.38 (registry+https://github.com/rust-lang/crates.io-index)" = "ce400c638d48ee0e9ab75aef7997609ec57367ccfe1463f21bf53c3eca67bf46" "checksum cfg-if 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" "checksum chrono 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "77d81f58b7301084de3b958691458a53c3f7e0b1d702f77e550b6a88e3a88abe" "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" +"checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum deunicode 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "850878694b7933ca4c9569d30a34b55031b9b139ee1fc7b94a527c4ef960d690" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" "checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" @@ -704,6 +778,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" "checksum lexical-core 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b0f90c979adde96d19eb10eb6431ba0c441e2f9e9bdff868b2f6f5114ff519" "checksum libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)" = "6281b86796ba5e4366000be6e9e18bf35580adf9e63fbe2294aadb587613a319" +"checksum libflate 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "45c97cf62125b79dcac52d506acdc4799f21a198597806947fd5f40dc7b93412" "checksum maplit 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "08cbb6b4fef96b6d77bfc40ec491b1690c779e77b05cd9f07f787ed376fd4c43" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2efc7bc57c883d4a4d6e3246905283d8dae951bb3bd32f49d6ef297f546e1c39" @@ -718,12 +793,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum pest_derive 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0" "checksum pest_generator 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "63120576c4efd69615b5537d3d052257328a4ca82876771d6944424ccfd9f646" "checksum pest_meta 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f249ea6de7c7b7aba92b4ff4376a994c6dbd98fd2166c89d5c4947397ecb574d" +"checksum podio 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum quote 0.6.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faf4799c5d274f3868a4aae320a0a182cbd2baee377b378f080e16a23e9d80db" "checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 1.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "d9d8297cc20bbb6184f8b45ff61c8ee6a9ac56c156cec8e38c3e5084773c44ad" "checksum regex-syntax 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b143cceb2ca5e56d5671988ef8b15615733e7ee16cd348e064333b251b89343f" +"checksum rle-decode-fast 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac" "checksum rustc-demangle 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7f4dccf6f4891ebcc0c39f9b6eb1a83b9bf5d747cb439ec6fba4f3b977038af" "checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" "checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" @@ -739,6 +816,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum static_assertions 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4f8de36da215253eb5f24020bfaa0646613b48bf7ebe36cdfa37c3b3b33b241" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum syn 0.15.35 (registry+https://github.com/rust-lang/crates.io-index)" = "641e117d55514d6d918490e47102f7e08d096fdde360247e4a10f7a91a8478d3" +"checksum take_mut 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" "checksum tera 0.11.20 (registry+https://github.com/rust-lang/crates.io-index)" = "4b505279e19d8f7d24b1a9dc58327c9c36174b1a2c7ebdeac70792d017cb64f3" "checksum termion 1.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dde0593aeb8d47accea5392b39350015b5eccb12c0d98044d856983d89548dea" "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" @@ -766,3 +844,4 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +"checksum zip 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c18fc320faf909036e46ac785ea827f72e485304877faf1a3a39538d3714dbc3" @@ -10,6 +10,7 @@ serde = "1.0" serde_derive = "1.0" serde_json = "1.0" tera = "0.11" +zip = "0.5" [dependencies.ogmarkup] git = "https://git.sr.ht/~lthms/ogmarkup/" diff --git a/src/epub.rs b/src/epub.rs index 15167c1..785f497 100644 --- a/src/epub.rs +++ b/src/epub.rs @@ -1,14 +1,21 @@ -use std::fs; +use std::env::set_current_dir; use std::fs::{create_dir, remove_dir_all, create_dir_all}; +use std::fs; +use std::io::{Read, Write}; use std::path::{Path, PathBuf}; -use std::env::set_current_dir; -use tera::{Tera, Context}; use serde_json::json; +use tera::{Tera, Context}; + use crate::error::{Raise, Error}; use crate::project::{Project, Chapter}; +use zip::write::FileOptions; +use zip::ZipWriter; +use std::fs::File; +use std::collections::HashSet; + const EPUB_MIMETYPE: &'static str = "application/epub+zip"; pub trait EpubWriter { @@ -31,9 +38,7 @@ pub trait EpubWriter { dst : &PathBuf, input : &str ) -> Result<(), Error> ; -} -impl EpubWriter { fn create_mimetype(&mut self) -> Result<(), Error> { self.write_str(&PathBuf::from("mimetype"), EPUB_MIMETYPE) } @@ -94,7 +99,7 @@ impl EpubWriter { Ok(extension.into()) } - pub fn generate(&mut self, project : &Project<String>, assets : &PathBuf) -> Result<(), Error> { + fn generate(&mut self, project : &Project<String>, assets : &PathBuf) -> Result<(), Error> { let tera = compile_templates!(template_dir(assets)?.as_str()); @@ -243,3 +248,90 @@ impl EpubWriter for Fs { Ok(()) } } + +pub struct Zip { + output : ZipWriter<File>, + dirs : HashSet<PathBuf>, +} + +impl Zip { + pub fn init() -> Result<Zip, Error> { + let file = File::create("Book.epub").or_raise("Could not create Book.epub")?; + + Ok(Zip { + output : ZipWriter::new(file), + dirs : HashSet::new(), + }) + } + + 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()) + .or_raise(&format!("Could not create directory {:?}", dir))?; + self.dirs.insert(dir.to_path_buf()); + } + } + + Ok(()) + } +} + +impl EpubWriter for Zip { + fn write_template( + &mut self, + dst : &PathBuf, + tera : & Tera, + template : &str, + ctx : &Context, + ) -> Result<(), Error> { + self.create_parent(dst)?; + + let content = tera.render(template, ctx) + .or_raise(&format!("cannot render {}", template))?; + + self.output.start_file_from_path(dst, FileOptions::default()) + .or_raise(&format!("Could not add file {:?} to archive", dst))?; + + self.output.write_all(content.as_bytes()) + .or_raise(&format!("Could not write {:?} content", dst))?; + + Ok(()) + } + + fn write_str( + &mut self, + dst : &PathBuf, + input : &str + ) -> Result<(), Error> { + self.create_parent(dst)?; + + self.output.start_file_from_path(dst, FileOptions::default()) + .or_raise(&format!("Could not add file {:?} to archive", dst))?; + + self.output.write_all(input.as_bytes()) + .or_raise(&format!("Could not write {:?} content", dst))?; + + Ok(()) + } + + 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))?; + + self.create_parent(dst)?; + + 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()) + .or_raise(&format!("Could not write {:?} content", dst))?; + + Ok(()) + } +} diff --git a/src/main.rs b/src/main.rs index 63f80c3..d95e119 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ extern crate serde_json; extern crate toml; #[macro_use] extern crate tera; +extern crate zip; use clap::{App, SubCommand}; @@ -24,7 +25,7 @@ use ogmarkup::typography::FRENCH; use error::Error; use project::Project; -use epub::Fs; +use epub::{Zip, Fs}; use epub::EpubWriter; @@ -34,8 +35,11 @@ pub fn build(assets : &PathBuf) -> Result<(), Error> { let project = Project::find_project()? .load_and_render(&FRENCH)?; + let mut zip_writer = Zip::init()?; + zip_writer.generate(&project, assets)?; + let mut fs_writer = Fs::init()?; - EpubWriter::generate(&mut fs_writer, &project, assets)?; + fs_writer.generate(&project, assets)?; Ok(()) } |