Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 9 additions & 27 deletions src-tauri/src/commands/content.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::AppData;
use crate::EpubWrapper;
use percent_encoding::{AsciiSet, CONTROLS, percent_decode_str, utf8_percent_encode};
use rbook::Epub;
use regex::Regex;
use std::path::Path;
use std::path::PathBuf;
Expand Down Expand Up @@ -165,25 +165,17 @@ impl<'a> UrlInjector<'a> {

#[tauri::command]
pub fn get_epub_content(state: State<'_, Arc<AppData>>) -> Result<String, String> {
let source = &state.source;
get_epub_content_inner(source).map_err(|e| e.to_string())
get_epub_content_inner(&state.epub).map_err(|e| e.to_string())
}

/// Extracts all XHTML content from an EPUB in canonical reading order.
///
/// # Arguments
///
/// * `source` - Path to EPUB
///
/// # Errors
///
/// Returns an error if:
/// * EPUB does not exist at `source`.
/// * EPUB at `source` is malformed.
/// * A resource cannot be read for some reason.
fn get_epub_content_inner(source: &PathBuf) -> anyhow::Result<String> {
let epub = Epub::open(source)?;

fn get_epub_content_inner(epub_wrapper: &EpubWrapper) -> anyhow::Result<String> {
let epub = &epub_wrapper.epub;
let mut content = String::new();

// Loop through each entry in the manifest in canonical reading order
Expand All @@ -209,26 +201,16 @@ fn get_epub_content_inner(source: &PathBuf) -> anyhow::Result<String> {

/// Fetches an EPUB resource, given its absolute path within the container.
///
/// # Arguments
///
/// * `epub_source` - The path to the EPUB file to be read from.
/// * `path` - The absolute path of the resource within the container, e.g. `/OEBPS/images/cover.png`
///
/// # Examples
/// ```ignore
/// let resource = get_resource(&PathBuf::from("./test.epub"), "/OEBPS/images/cover.png").unwrap();
/// println!("{}", resource.content_type()); // "image/png"
/// ```
/// `path` should be the absolute path of the resource within the container,
/// e.g. `/OEBPS/images/cover.png`
///
/// # Errors
///
/// Returns an error if:
/// * EPUB does not exist at `epub_source`
/// * Resource does not exist at `path`
pub(crate) fn get_resource(epub_source: &PathBuf, path: &str) -> anyhow::Result<Resource> {
let epub = Epub::open(epub_source)?;

let resource = epub
pub(crate) fn get_resource(epub_wrapper: &EpubWrapper, path: &str) -> anyhow::Result<Resource> {
let resource = epub_wrapper
.epub
.manifest()
.iter()
.find(|entry| {
Expand Down
37 changes: 6 additions & 31 deletions src-tauri/src/commands/metadata.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
use rbook::Epub;
use std::{path::PathBuf, sync::Arc};
use std::sync::Arc;
use tauri::State;

use crate::AppData;

/// Stores metadata of an EPUB.
#[derive(serde::Serialize)]
#[derive(serde::Serialize, Clone)]
pub struct Metadata {
title: Option<String>,
year: Option<i16>,
creators: Vec<String>,
}

impl From<Epub> for Metadata {
fn from(value: Epub) -> Self {
impl From<&Epub> for Metadata {
fn from(value: &Epub) -> Self {
let title = value
.metadata()
.title()
Expand All @@ -37,31 +37,6 @@ impl From<Epub> for Metadata {
}

#[tauri::command]
pub async fn read_epub_metadata(state: State<'_, Arc<AppData>>) -> Result<Metadata, String> {
let source = &state.source;
read_epub_metadata_inner(source)
.await
.map_err(|e| e.to_string())
}

/// Extracts metadata from EPUB.
///
/// # Arguments
///
/// * `source` - Path to EPUB
///
/// # Errors
///
/// Returns an error if:
/// * EPUB does not exist at `source`.
/// * EPUB at `source` is malformed.
async fn read_epub_metadata_inner(source: &PathBuf) -> anyhow::Result<Metadata> {
// Skip manifest and spine, since we just want metadata right now
let epub = Epub::options()
.skip_toc(true)
.skip_manifest(true)
.skip_spine(true)
.open(source)?;

Ok(epub.into())
pub fn read_epub_metadata(state: State<'_, Arc<AppData>>) -> Metadata {
state.epub.metadata.to_owned()
}
53 changes: 49 additions & 4 deletions src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,70 @@
use crate::commands::content::get_epub_content;
use crate::commands::metadata::Metadata;
use crate::commands::metadata::read_epub_metadata;
use commands::content::get_resource;
use http::HeaderValue;
use rbook::Epub;
use rbook::ebook::errors::EbookError;
use std::path::PathBuf;
use std::sync::Arc;
use tauri::Manager;

pub mod commands;

pub struct AppData {
source: PathBuf,
epub: EpubWrapper,
}

/// Stores data of the EPUB currently being edited.
pub(crate) struct EpubWrapper {
epub: Epub,
metadata: Metadata,
}

impl EpubWrapper {
pub(crate) fn new(epub: Epub, metadata: Metadata) -> Self {
Self { epub, metadata }
}
}

/// # Panics
///
/// * If no source file is provided.
/// * If the path to the source file is invalid.
/// * If the EPUB at the provided path cannot be opened for some reason.
fn bootstrap_app() -> AppData {
let source = PathBuf::from(
std::env::args()
.nth(1)
.expect("No source file given, exiting..."),
);
)
.canonicalize()
.unwrap_or_else(|e| {
let err_msg = format!("Source file path is invalid: {e}");
panic!("{}", err_msg);
});

let epub = match Epub::open(&source) {
Ok(epub) => epub,
Err(EbookError::Archive(e)) => {
let err_msg = format!("Missing or invalid EPUB at {source:?}.\nError: {e}");
panic!("{}", err_msg);
}
Err(EbookError::Format(e)) => {
let err_msg = format!("Malformed EPUB at {source:?}.\nError: {e}");
panic!("{}", err_msg);
}
Err(e) => {
let err_msg = format!("Error opening EPUB at {source:?}.\nError: {e}");
panic!("{}", err_msg);
}
};

let metadata = Metadata::from(&epub);

let epub = EpubWrapper::new(epub, metadata);

AppData { source }
AppData { epub }
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
Expand All @@ -44,7 +89,7 @@ pub fn run() {
.register_uri_scheme_protocol("epub", move |_ctx, request| {
let path = request.uri().path();

if let Ok(resource) = get_resource(&protocol_data.source, path) {
if let Ok(resource) = get_resource(&protocol_data.epub, path) {
let data = resource.bytes().to_owned();
let content_type = resource.content_type();

Expand Down
Loading