Skip to content

Commit

Permalink
Merge branch 'main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
crowdagger authored Oct 5, 2024
2 parents 17f0fb4 + 31815e7 commit 6a1ee2c
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 80 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ zip-command = ["tempfile"]
zip-library = ["libzip", "libzip/time"]

[dependencies]
eyre = "0.6"
thiserror = "1.0"
once_cell = "1"
upon = "0.8"
chrono = { version = "0.4", default-features = false, features = ["clock", "std", "wasmbind"] }
Expand Down
108 changes: 86 additions & 22 deletions src/epub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,14 @@ use crate::templates;
use crate::toc::{Toc, TocElement};
use crate::zip::Zip;
use crate::ReferenceType;
use crate::Result;
use crate::{common, EpubContent};

use std::io;
use std::io::Read;
use std::path::Path;
use std::str::FromStr;

use eyre::{bail, Context, Result};
use upon::Engine;

/// Represents the EPUB version.
///
/// Currently, this library supports EPUB 2.0.1 and 3.0.1.
Expand All @@ -39,6 +37,28 @@ pub enum PageDirection {
Rtl,
}


/// Represents the EPUB `<meta>` content inside `content.opf` file.
///
/// <meta name="" content="">
///
#[derive(Debug)]
pub struct MetadataOpf {
/// Name of the `<meta>` tag
pub name: String,
/// Content of the `<meta>` tag
pub content: String
}

impl MetadataOpf {
/// Create new instance
///
///
pub fn new(&self, meta_name: String, meta_content: String) -> Self {
Self { name: meta_name, content: meta_content }
}
}

impl ToString for PageDirection {
fn to_string(&self) -> String {
match &self {
Expand All @@ -48,15 +68,15 @@ impl ToString for PageDirection {
}
}

impl std::str::FromStr for PageDirection {
type Err = eyre::Report;
impl FromStr for PageDirection {
type Err = crate::Error;

fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_ref() {
"rtl" => Ok(PageDirection::Rtl),
"ltr" => Ok(PageDirection::Ltr),
_ => bail!("Invalid page direction: {}", s),
_ => Err(crate::Error::PageDirectionError(s)),
}
}
}
Expand Down Expand Up @@ -146,27 +166,31 @@ impl Content {
#[derive(Debug)]
pub struct EpubBuilder<Z: Zip> {
version: EpubVersion,
direction: PageDirection,
zip: Z,
files: Vec<Content>,
metadata: Metadata,
toc: Toc,
stylesheet: bool,
inline_toc: bool,
escape_html: bool,
meta_opf: Vec<MetadataOpf>
}

impl<Z: Zip> EpubBuilder<Z> {
/// Create a new default EPUB Builder
pub fn new(zip: Z) -> Result<EpubBuilder<Z>> {
let mut epub = EpubBuilder {
version: EpubVersion::V20,
direction: PageDirection::Ltr,
zip,
files: vec![],
metadata: Metadata::default(),
toc: Toc::new(),
stylesheet: false,
inline_toc: false,
escape_html: true,
meta_opf: Vec::new()
};

epub.zip
Expand All @@ -189,6 +213,36 @@ impl<Z: Zip> EpubBuilder<Z> {
self.version = version;
self
}

/// Set EPUB Direction (default: Ltr)
///
/// * `Ltr`: Left-To-Right
/// * `Rtl`: Right-To-Left
///
///
pub fn epub_direction(&mut self, direction: PageDirection) -> &mut Self {
self.direction = direction;
self
}


/// Add custom <meta> to `content.opf`
/// Syntax: `self.add_metadata_opf(name, content)`
///
/// ### Example
/// If you wanna add `<meta name="primary-writing-mode" content="vertical-rl"/>` into `content.opf`
///
/// ```rust
/// self.add_metadata_opf(MetadataOpf {
/// name: String::from("primary-writing-mode"),
/// content: String::from("vertical-rl")
/// })
/// ```
///
pub fn add_metadata_opf(&mut self, item: MetadataOpf) -> &mut Self {
self.meta_opf.push(item);
self
}

/// Set some EPUB metadata
///
Expand Down Expand Up @@ -244,7 +298,7 @@ impl<Z: Zip> EpubBuilder<Z> {
}
"license" => self.metadata.license = Some(value.into()),
"toc_name" => self.metadata.toc_name = value.into(),
s => bail!("invalid metadata '{}'", s),
s => Err(crate::Error::InvalidMetadataError(s.to_string()))?,
}
Ok(self)
}
Expand Down Expand Up @@ -552,6 +606,14 @@ impl<Z: Zip> EpubBuilder<Z> {
common::encode_html(rights, self.escape_html),
));
}
for meta in &self.meta_opf{
optional.push(format!(
"<meta name=\"{}\" content=\"{}\"/>",
common::encode_html(&meta.name, self.escape_html),
common::encode_html(&meta.content, self.escape_html),
));
}

let date_modified = self
.metadata
.date_modified
Expand Down Expand Up @@ -658,14 +720,14 @@ impl<Z: Zip> EpubBuilder<Z> {

let mut res: Vec<u8> = vec![];
match self.version {
EpubVersion::V20 => templates::v2::CONTENT_OPF
.render(&Engine::new(), &data)
.to_writer(&mut res),
EpubVersion::V30 => templates::v3::CONTENT_OPF
.render(&Engine::new(), &data)
.to_writer(&mut res),
EpubVersion::V20 => templates::v2::CONTENT_OPF.render(&data).to_writer(&mut res),
EpubVersion::V30 => templates::v3::CONTENT_OPF.render(&data).to_writer(&mut res),
}
.wrap_err("could not render template for content.opf")?;
.map_err(|e| crate::Error::TemplateError {
msg: "could not render template for content.opf".to_string(),
cause: e.into(),
})?;
//.wrap_err("could not render template for content.opf")?;

Ok(res)
}
Expand All @@ -684,7 +746,10 @@ impl<Z: Zip> EpubBuilder<Z> {
templates::TOC_NCX
.render(&Engine::new(), &data)
.to_writer(&mut res)
.wrap_err("error rendering toc.ncx template")?;
.map_err(|e| crate::Error::TemplateError {
msg: "error rendering toc.ncx template".to_string(),
cause: e.into(),
})?;
Ok(res)
}

Expand Down Expand Up @@ -747,14 +812,13 @@ impl<Z: Zip> EpubBuilder<Z> {

let mut res: Vec<u8> = vec![];
match self.version {
EpubVersion::V20 => templates::v2::NAV_XHTML
.render(&Engine::new(), &data)
.to_writer(&mut res),
EpubVersion::V30 => templates::v3::NAV_XHTML
.render(&Engine::new(), &data)
.to_writer(&mut res),
EpubVersion::V20 => templates::v2::NAV_XHTML.render(&data).to_writer(&mut res),
EpubVersion::V30 => templates::v3::NAV_XHTML.render(&data).to_writer(&mut res),
}
.wrap_err("error rendering nav.xhtml template")?;
.map_err(|e| crate::Error::TemplateError {
msg: "error rendering nav.xhtml template".to_string(),
cause: e.into(),
})?;
Ok(res)
}
}
Expand Down
60 changes: 58 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,11 @@ mod zip_library;

pub use epub::EpubBuilder;
pub use epub::EpubVersion;
pub use epub::MetadataOpf;
pub use epub::PageDirection;
pub use epub_content::EpubContent;
pub use epub_content::ReferenceType;
use libzip::result::ZipError;
pub use toc::Toc;
pub use toc::TocElement;
#[cfg(feature = "zip-command")]
Expand All @@ -153,5 +155,59 @@ pub use zip_command_or_library::ZipCommandOrLibrary;
#[cfg(feature = "libzip")]
pub use zip_library::ZipLibrary;

/// Re-exports the result type used across the library.
pub use eyre::Result;
/// Error type of this crate. Each variant represent a type of event that may happen during this crate's operations.
#[derive(thiserror::Error, Debug)]
pub enum Error {
/// An error caused while processing a template or its rendering.
#[error("{msg}: {cause:?}")]
TemplateError {
/// A message explaining what was happening when we recieved this error.
msg: String,
/// The root cause of the error.
// Box the error, since it is quite large (at least 136 bytes, thanks clippy!)
cause: Box<upon::Error>,
},
/// An error returned when encountering an unknown [`PageDirection`].
#[error("Invalid page direction specification: {0}")]
PageDirectionError(String),
/// An error returned when an unknown metadata key has been encountered.
#[error("Invalid metadata key: {0}")]
InvalidMetadataError(String),
/// An error returned when attempting to access the filesystem
#[error("{msg}: {cause:?}")]
IoError {
/// A message explaining what was happening when we recieved this error.
msg: String,
/// The root cause of the error.
cause: std::io::Error,
},
/// An error returned when something happened while invoking a zip program. See [`ZipCommand`].
#[error("Error while executing zip command: {0}")]
ZipCommandError(String),
/// An error returned when the zip library itself returned an error. See [`ZipLibrary`].
#[error(transparent)]
ZipError(#[from] ZipError),
/// An error returned when the zip library itself returned an error, but with an additional message. See [`ZipLibrary`].
#[error("{msg}: {cause:?}")]
ZipErrorWithMessage {
/// A message explaining what was happening when we recieved this error.
msg: String,
/// The root cause of the error.
cause: ZipError,
},
/// An error returned when an invalid [`Path`] has been encountered during epub processing.
#[error("Invalid path: {0}")]
InvalidPath(String),
}

impl From<std::io::Error> for Error {
fn from(value: std::io::Error) -> Self {
Error::IoError {
msg: format!("{value:?}"),
cause: value,
}
}
}

/// A more convenient shorthand for functions returning an error in this crate.
pub type Result<T> = std::result::Result<T, Error>;
2 changes: 1 addition & 1 deletion src/zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::io::Read;
use std::io::Write;
use std::path::Path;

use eyre::Result;
use crate::Result;

/// An abstraction over possible Zip implementations.
///
Expand Down
Loading

0 comments on commit 6a1ee2c

Please sign in to comment.