update book.toml

This commit is contained in:
Maverick Liu 2025-02-24 15:57:15 +08:00
parent ef9ded9a17
commit 2bf293c4d7
3 changed files with 1859 additions and 39 deletions

1815
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[package] [package]
name = "epub2mdbook" name = "epub2mdbook"
version = "0.14.0" version = "0.15.0"
edition = "2024" edition = "2024"
description = "A tool to convert EPUB files to MDBook format" description = "A tool to convert EPUB files to MDBook format"
authors = ["Maverick Liu <maverick.liu42@gmail.com>"] authors = ["Maverick Liu <maverick.liu42@gmail.com>"]
@ -13,5 +13,7 @@ categories = ["command-line-utilities", "text-processing"]
clap = { version = "4.5.30", features = ["derive"] } clap = { version = "4.5.30", features = ["derive"] }
epub = "2.1.1" epub = "2.1.1"
htmd = "0.1.6" htmd = "0.1.6"
mdbook = "0.4.45"
regex = "1.11.1" regex = "1.11.1"
thiserror = "2.0.11" thiserror = "2.0.11"
toml = "0.8.20"

View file

@ -2,6 +2,7 @@ pub mod error;
use epub::doc::{EpubDoc, NavPoint}; use epub::doc::{EpubDoc, NavPoint};
use error::Error; use error::Error;
use mdbook::config::BookConfig;
use regex::{Captures, Regex}; use regex::{Captures, Regex};
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsStr; use std::ffi::OsStr;
@ -10,7 +11,7 @@ use std::path::{Path, PathBuf};
use std::sync::LazyLock; use std::sync::LazyLock;
use std::{fs, io}; use std::{fs, io};
/// Convert an EPUB file to an MDBook /// Convert an EPUB file to MDBook format
/// ///
/// # Arguments /// # Arguments
/// ///
@ -23,37 +24,26 @@ pub fn convert_epub_to_mdbook(
with_file_name: bool, with_file_name: bool,
) -> Result<(), Error> { ) -> Result<(), Error> {
let epub_path = epub_path.as_ref(); let epub_path = epub_path.as_ref();
let output_dir = output_dir.as_ref();
if !epub_path.is_file() { if !epub_path.is_file() {
return Err(Error::NotAFile(epub_path.display().to_string())); return Err(Error::NotAFile(epub_path.display().to_string()));
} }
let mut output_dir = output_dir.as_ref().to_owned();
if with_file_name {
let book_name = epub_path let book_name = epub_path
.with_extension("") .with_extension("")
.file_name() .file_name()
.expect("unreachable") .expect("unreachable")
.to_string_lossy() .to_string_lossy()
.to_string(); .to_string();
let output_dir = if with_file_name { output_dir.push(book_name)
output_dir.join(&book_name) }
} else {
output_dir.to_owned()
};
fs::create_dir_all(output_dir.join("src"))?; fs::create_dir_all(output_dir.join("src"))?;
let mut epub_doc = EpubDoc::new(epub_path)?; let mut epub_doc = EpubDoc::new(epub_path)?;
let title = epub_doc let (summary_md, html_to_md) = generate_summary_md(&epub_doc);
.metadata
.get("title")
.and_then(|v| v.first().cloned())
.unwrap_or(book_name);
let creator = epub_doc
.metadata
.get("creator")
.and_then(|v| v.first().cloned());
let (summary_md, html_to_md) = generate_summary_md(&epub_doc, &title);
extract_chapters_and_resources(&mut epub_doc, &output_dir, &html_to_md)?; extract_chapters_and_resources(&mut epub_doc, &output_dir, &html_to_md)?;
fs::write(output_dir.join("src/SUMMARY.md"), summary_md)?; fs::write(output_dir.join("src/SUMMARY.md"), summary_md)?;
write_book_toml(&output_dir, &title, creator)?; write_book_toml(&epub_doc, &output_dir)?;
Ok(()) Ok(())
} }
@ -82,7 +72,6 @@ fn epub_nav_to_md(
/// # Arguments /// # Arguments
/// ///
/// * `epub_doc` - The EPUB document /// * `epub_doc` - The EPUB document
/// * `title` - The title of the book
/// ///
/// # Returns /// # Returns
/// ///
@ -90,9 +79,13 @@ fn epub_nav_to_md(
/// * `html_to_md` - The file mapping from html to md /// * `html_to_md` - The file mapping from html to md
pub fn generate_summary_md<R: Read + Seek>( pub fn generate_summary_md<R: Read + Seek>(
epub_doc: &EpubDoc<R>, epub_doc: &EpubDoc<R>,
title: &str,
) -> (String, HashMap<PathBuf, PathBuf>) { ) -> (String, HashMap<PathBuf, PathBuf>) {
let mut summary_md = format!("# {}\n\n", title); let title = epub_doc.metadata.get("title").and_then(|v| v.first());
let mut summary_md = if let Some(title) = title {
format!("# {}\n\n", title)
} else {
"".to_string()
};
let html_to_md = epub_doc let html_to_md = epub_doc
.resources .resources
.iter() .iter()
@ -185,17 +178,35 @@ fn post_process_md(markdown: &str, file_name_map: &HashMap<&OsStr, &OsStr>) -> S
.to_string() .to_string()
} }
fn write_book_toml( fn write_book_toml<R: Read + Seek>(
epub_doc: &EpubDoc<R>,
output_dir: impl AsRef<Path>, output_dir: impl AsRef<Path>,
title: &str,
creator: Option<String>,
) -> io::Result<()> { ) -> io::Result<()> {
let output_dir = output_dir.as_ref(); let output_dir = output_dir.as_ref();
let author = match creator { let title = epub_doc
Some(creator) => format!("author = \"{creator}\"\n"), .metadata
None => "".to_string(), .get("title")
.and_then(|v| v.first().cloned());
let authors = epub_doc.metadata.get("creator").cloned().unwrap_or(vec![]);
let description = epub_doc
.metadata
.get("description")
.and_then(|v| v.first().cloned())
.map(|s| htmd::convert(&s).expect("unreachable"));
let lang = epub_doc
.metadata
.get("lang")
.and_then(|v| v.first().cloned());
let config = BookConfig {
title,
authors,
description,
src: PathBuf::from("src"),
multilingual: false,
language: lang,
text_direction: None,
}; };
let toml_content = format!("[book]\ntitle = \"{title}\"\n{author}",); let toml_content = format!("[book]\n{}", toml::to_string(&config).expect("unreachable"));
fs::write(output_dir.join("book.toml"), toml_content)?; fs::write(output_dir.join("book.toml"), toml_content)?;
Ok(()) Ok(())
} }