From efdf3c3ca6609d2c839e1061edee6f466e989767 Mon Sep 17 00:00:00 2001 From: NA Date: Wed, 24 Jan 2024 15:02:52 +0000 Subject: [PATCH] Basic console interface and library functions complete. Time to actully get to work. --- .gitignore | 1 + .vscode/settings.json | 12 ++ Cargo.lock | 290 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 18 +++ src/binary.rs | 102 +++++++++++++++ src/library.rs | 71 +++++++++++ 6 files changed, 494 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/settings.json create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 src/binary.rs create mode 100644 src/library.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..3c4ce95 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +{ + "cSpell.words": [ + "archiver", + "Audiobook", + "epub", + "Webnovel" + ], + "rust-analyzer.showUnlinkedFileNotification": false, + "cSpell.ignoreWords": [ + "royalroad" + ] +} \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..86d0688 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,290 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "anstream" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "clap" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "idna" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "royal_road_archiver" +version = "0.1.0" +dependencies = [ + "clap", + "url", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "unicode-bidi" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..c355bb7 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "royal_road_archiver" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[lib] +name = "royal_road_archiver_lib" +path = "src/library.rs" + +[[bin]] +name = "royal_road_archiver_bin" +path = "src/binary.rs" + +[dependencies] +clap = { version = "4.4.18", features = ["derive"] } +url = "2.5.0" diff --git a/src/binary.rs b/src/binary.rs new file mode 100644 index 0000000..68c90fc --- /dev/null +++ b/src/binary.rs @@ -0,0 +1,102 @@ +use std::{env, fs, path::{Path, PathBuf}, process::exit}; + +use clap::{Parser, Subcommand}; +use url::Url; + +#[derive(clap::Parser, Debug)] +#[command(version)] +struct Cli { + #[command(subcommand)] + subcommand: Subcommands, + + /// Enter the URL of the Webnovel + book_url: String, + + /// Enter the output directory for the generated format. + /// Leave blank to use current directory. + output_directory: Option, +} + +#[derive(Subcommand, Debug)] +pub enum Subcommands { + /// Generate an Audiobook from the webnovel. + /// 'audiobook --help' for available arguments. + Audiobook(royal_road_archiver_lib::AudiobookArgs), + + /// Generate an epub from the Webnovel. + /// 'epub --help' for available arguments. + Epub(royal_road_archiver_lib::EpubArgs), + + /// Store the webnovel as a collection of HTML pages. + /// 'html --help' for available arguments. + Html(royal_road_archiver_lib::HtmlArgs), + + /// Generate a markdown file from the Webnovel. + /// 'markdown --help' for available arguments. + Markdown(royal_road_archiver_lib::MarkdownArgs), +} + +fn main() { + let cli_input = Cli::parse(); + + // Turn the inputted string into a path, or grab the current directory if empty. + let output_directory: PathBuf; + match cli_input.output_directory { + Some(output_directory_input) => { + output_directory = Path::new(&output_directory_input).to_path_buf(); + }, + None => { + output_directory = env::current_dir().unwrap().as_path().to_path_buf(); + } + } + + valid_directory_check(&output_directory); + let book_url = valid_url_check(&cli_input.book_url.to_lowercase()); + + match cli_input.subcommand { + Subcommands::Audiobook(audiobook_args) => royal_road_archiver_lib::generate_audiobook(audiobook_args, book_url, output_directory), + Subcommands::Epub(epub_args) => royal_road_archiver_lib::generate_epub(epub_args, book_url, output_directory), + Subcommands::Html(html_args) => royal_road_archiver_lib::generate_html(html_args, book_url, output_directory), + Subcommands::Markdown(markdown_args) => royal_road_archiver_lib::generate_markdown(markdown_args, book_url, output_directory), + } +} + +// Check if the directory exists and is writeable. Creates one if not. +fn valid_directory_check(output_directory: &Path) { + // Check if the directory exists, if it does not; attempt to create one. + if !output_directory.exists() { + match fs::create_dir_all(output_directory) { + Ok(_) => (), + Err(error) => { + eprintln!("Error! Unable to create directory: {error}"); + exit(1); + } + } + } + + // Check if the user has write permissions: + if fs::metadata(output_directory).unwrap().permissions().readonly() { + eprintln!("Error! You do not have permissions for the specified directory."); + exit(1); + } +} + +// Check if the given URL is a valid royalroad url. +fn valid_url_check(book_url: &str) -> Url { + match Url::parse(book_url) { + Ok(book_url) => { + + if book_url.host_str() == Some("www.royalroad.com") { + return book_url; + } + else { + eprintln!("Error! Please enter a RoyalRoad URL."); + exit(1); + } + }, + Err(error) => { + eprintln!("Error! Unable to parse url: {error}"); + exit(1); + } + } +} diff --git a/src/library.rs b/src/library.rs new file mode 100644 index 0000000..3baf95b --- /dev/null +++ b/src/library.rs @@ -0,0 +1,71 @@ +use std::path::PathBuf; + +use clap::Args; +use url::Url; + +#[derive(Args, Debug)] +pub struct AudiobookArgs { + /// Disable the generation of chapter titles in the audio file. Useful to avoid chapter titles appearing twice. + #[arg(short, long)] + pub no_chapter_titles: bool, + + /// Split the novel into multiple audio files by chapter. + #[arg(short, long)] + pub split_novel_by_chapters: bool, +} + +#[derive(Args, Debug)] +pub struct EpubArgs { + /// Disable the inclusion of images. + /// Will speed up epub generation and significantly decrease epub size. + #[arg(short, long)] + pub no_images: bool, +} + +#[derive(Args, Debug)] +pub struct HtmlArgs { + +} + +#[derive(Args, Debug)] +pub struct MarkdownArgs { + /// Disable the generation of chapter titles. Useful to avoid chapter titles appearing twice. + #[arg(short, long)] + pub no_chapter_titles: bool, + + /// Disables the inclusion of html image tags in the markdown. + #[arg(short='i', long)] + pub no_image_tags: bool, +} + +/// Generate an audiobook from the given arguments, url, & outputs it to the output directory. +/// +/// This function DOES NOT do any error checking on the Url or output directory & WILL panic if they are wrong. +/// Make sure the Url is valid and the output directory is writable BEFORE passing them to this. +pub fn generate_audiobook(audiobook_args: AudiobookArgs, book_url: Url, output_directory: PathBuf) { + eprintln!("This is not implemented yet."); +} + +/// Generate an epub file from the given arguments, url, & outputs it to the output directory. +/// +/// This function DOES NOT do any error checking on the Url or output directory & WILL panic if they are wrong. +/// Make sure the Url is valid and the output directory is writable BEFORE passing them to this. +pub fn generate_epub(epub_args: EpubArgs, book_url: Url, output_directory: PathBuf) { + eprintln!("This is not implemented yet."); +} + +/// Generate an html archive from the given arguments, url, & outputs it to the output directory. +/// +/// This function DOES NOT do any error checking on the Url or output directory & WILL panic if they are wrong. +/// Make sure the Url is valid and the output directory is writable BEFORE passing them to this. +pub fn generate_html(html_args: HtmlArgs, book_url: Url, output_directory: PathBuf) { + eprintln!("This is not implemented yet."); +} + +/// Generate a markdown file from the given arguments, url, & outputs it to the output directory. +/// +/// This function DOES NOT do any error checking on the Url or output directory & WILL panic if they are wrong. +/// Make sure the Url is valid and the output directory is writable BEFORE passing them to this. +pub fn generate_markdown(markdown_args: MarkdownArgs, book_url: Url, output_directory: PathBuf) { + eprintln!("This is not implemented yet."); +} \ No newline at end of file