From 44d12c270ac150001afb3023fbc69520313020b6 Mon Sep 17 00:00:00 2001 From: Filip Niklas <118931755+Firgrep@users.noreply.github.com> Date: Sat, 31 Aug 2024 19:09:42 +0200 Subject: [PATCH] refactor: new, more composable API, split out optional ignore paths --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 61 +++++++++++++++++++------------- prepyrus_settings.json | 3 ++ src/lib.rs | 60 +++++++++++++++++++------------ src/main.rs | 3 +- src/utils.rs | 44 +++++++++++++---------- tests/integration_test_verify.rs | 55 +++++++++++++++++++++++++--- 8 files changed, 157 insertions(+), 73 deletions(-) create mode 100644 prepyrus_settings.json diff --git a/Cargo.lock b/Cargo.lock index 2f64333..f7e077a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,7 +108,7 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "prepyrus" -version = "0.1.2" +version = "0.2.0" dependencies = [ "biblatex", "regex", diff --git a/Cargo.toml b/Cargo.toml index 63bc0b5..6ffca12 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ repository = "https://github.com/systemphil/prepyrus" readme = "README.md" categories = ["database", "parser-implementations", "text-processing"] keywords = ["bibtex", "biblatex", "mdx", "parser", "citation"] -version = "0.1.2" +version = "0.2.0" edition = "2021" [dependencies] diff --git a/README.md b/README.md index 70b2f40..9be8c8e 100644 --- a/README.md +++ b/README.md @@ -6,53 +6,68 @@ Prepyrus is a tool for verifying and processing MDX files that contain citations in Chicago author-date style and certain metadata. +⚠️ This tool is still in early development and API may frequently change. + ## Usage Add the crate to your `Cargo.toml` and use it as shown below: ```toml [dependencies] -prepyrus = "0.1" +prepyrus = "0.2" ``` -Main API interface is the `run_prepyrus` function. Example usage: +Main API interface is the `Prepyrus` impl. Example usage: ```rust -use prepyrus::run_prepyrus; +use prepyrus::Prepyrus; + fn main() { - let args: Vec = std::env::args().collect(); - if args.len() < 4 { - eprintln!( - "Expected more args. Usage: prepyrus " - ); - std::process::exit(1); - } - if let Err(e) = run_prepyrus(&args[1], &args[2], &args[3]) { + let args = vec![ + "_program_index".to_string(), + "tests/mocks/test.bib".to_string(), // bibliography file + "tests/mocks/data".to_string(), // target directory or .mdx file + "verify".to_string(), // mode + "tests/mocks/data/development.mdx".to_string(), // optional ignore paths, separate with commas if multiple + ]; + + let _ = run(args).unwrap_or_else(|e| { eprintln!("Error: {}", e); std::process::exit(1); - } + }); + println!("===Prepyrus completed successfully!"); } -``` -The function takes three arguments: ` ` +fn run(args: Vec) -> Result<(), Box> { + let config = Prepyrus::build_config(&args, None)?; + let all_entries = Prepyrus::get_all_bib_entries(&config.bib_file).unwrap(); + let mdx_paths = + Prepyrus::get_mdx_paths(&config.target_path, Some(config.settings.ignore_paths))?; + + // Phase 1: Verify MDX files + let articles_file_data = Prepyrus::verify(mdx_paths, &all_entries)?; -- a bibliography file (.bib), -- a target directory or .mdx file, -- and a mode (either `verify` or `process`). + // Phase 2: Process MDX files (requires mode to be set to "process") + if config.mode == "process" { + Prepyrus::process(articles_file_data); + } + + Ok(()) +} +``` `verify` mode only verifies the citations in the MDX files against the bibliography. -**⚠️ NOTE: `process` mode modifies the MDX files.** -`process` mode _additionally_ processes the MDX files by injecting bibliography and other HTML details into the MDX files. +**⚠️ NOTE: This mode modifies the MDX files.** + +`process` mode _additionally_ processes the MDX files by injecting bibliography and other details into the MDX files. ## Description The tool is designed to work with MDX files that contain citations in Chicago author-date style. Examples: -```markdown -"...nowhere on heaven or on earth is there anything which does not contain both being and nothing in itself" (Hegel 2010, 61). -``` +> "...nowhere on heaven or on earth is there anything which does not contain both being and nothing in itself" (Hegel 2010, 61). The tool parses and verifies the citations in the MDX files against a bibliography file in BibTeX format (using Biblatex). @@ -64,9 +79,7 @@ Finally, it also adds a notes heading at the end if footnotes are present in the ## Limitations The tool currently only supports citations in Chicago author-date style. - Only book entries are currently supported (plans to support more types in the future). - Only the following metadata fields are supported: - author diff --git a/prepyrus_settings.json b/prepyrus_settings.json new file mode 100644 index 0000000..8ee9612 --- /dev/null +++ b/prepyrus_settings.json @@ -0,0 +1,3 @@ +{ + "ignore_paths": [] +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 1921317..6dd6105 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,40 +2,56 @@ Prepyrus is a tool for verifying and processing MDX files that contain citations in Chicago author-date style and certain metadata. +⚠️ This tool is still in early development and API may frequently change. + ## Usage Add the crate to your `Cargo.toml` and use it as shown below: ```toml [dependencies] -prepyrus = "0.1" +prepyrus = "0.2" ``` -Main API interface is the `run_prepyrus` function. Example usage: +Main API interface is the `Prepyrus` impl. Example usage: -```rust,ignore -use prepyrus::run_prepyrus; +```rust +use prepyrus::Prepyrus; fn main() { - let args: Vec = std::env::args().collect(); - if args.len() < 4 { - eprintln!( - "Expected more args. Usage: prepyrus " - ); - std::process::exit(1); - } - if let Err(e) = run_prepyrus(&args[1], &args[2], &args[3]) { + let args = vec![ + "_program_index".to_string(), + "tests/mocks/test.bib".to_string(), // bibliography file + "tests/mocks/data".to_string(), // target directory or .mdx file + "verify".to_string(), // mode + "tests/mocks/data/development.mdx".to_string(), // optional ignore paths, separate with commas if multiple + ]; + + let _ = run(args).unwrap_or_else(|e| { eprintln!("Error: {}", e); std::process::exit(1); - } + }); + println!("===Prepyrus completed successfully!"); } -``` -The function takes three arguments: ` ` -- a bibliography file (.bib), -- a target directory or .mdx file, -- and a mode (either `verify` or `process`). +fn run(args: Vec) -> Result<(), Box> { + let config = Prepyrus::build_config(&args, None)?; + let all_entries = Prepyrus::get_all_bib_entries(&config.bib_file).unwrap(); + let mdx_paths = + Prepyrus::get_mdx_paths(&config.target_path, Some(config.settings.ignore_paths))?; + + // Phase 1: Verify MDX files + let articles_file_data = Prepyrus::verify(mdx_paths, &all_entries)?; + + // Phase 2: Process MDX files (requires mode to be set to "process") + if config.mode == "process" { + Prepyrus::process(articles_file_data); + } + + Ok(()) +} +``` `verify` mode only verifies the citations in the MDX files against the bibliography. @@ -47,9 +63,7 @@ The function takes three arguments: ` "...nowhere on heaven or on earth is there anything which does not contain both being and nothing in itself" (Hegel 2010, 61). The tool parses and verifies the citations in the MDX files against a bibliography file in BibTeX format (using Biblatex). @@ -94,11 +108,11 @@ use validator::ArticleFileData; pub struct Prepyrus {} impl Prepyrus { - pub fn verify_config( + pub fn build_config( args: &Vec, test_mode: Option, ) -> Result { - Utils::verify_config(args, test_mode) + Utils::build_config(args, test_mode) } pub fn get_all_bib_entries(bib_file: &str) -> Result, BibliographyError> { diff --git a/src/main.rs b/src/main.rs index 9f2dfb6..6e1ce18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,8 +11,9 @@ fn main() { println!("===Prepyrus completed successfully!"); } +/// Run all the methods of prepyrus fn run(args: Vec) -> Result<(), Box> { - let config = Prepyrus::verify_config(&args, None)?; + let config = Prepyrus::build_config(&args, None)?; let all_entries = Prepyrus::get_all_bib_entries(&config.bib_file).unwrap(); let mdx_paths = Prepyrus::get_mdx_paths(&config.target_path, Some(config.settings.ignore_paths))?; diff --git a/src/utils.rs b/src/utils.rs index 2ceded0..0631ebe 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -84,6 +84,7 @@ impl BiblatexUtils { } } +#[derive(Debug, Serialize, Deserialize)] pub struct Config { pub bib_file: String, pub target_path: String, @@ -129,38 +130,47 @@ impl Utils { Ok(settings) } - pub fn extract_paths(path: &str, exceptions: Option>) -> io::Result> { - let exceptions = exceptions.unwrap_or_else(|| Vec::new()); + pub fn extract_paths(path: &str, ignore_paths: Option>) -> io::Result> { + let exceptions = ignore_paths.unwrap_or_else(|| Vec::new()); let mdx_paths_raw = Self::extract_mdx_paths(path).unwrap(); let mdx_paths = Self::filter_mdx_paths_for_exceptions(mdx_paths_raw, exceptions); Ok(mdx_paths) } - pub fn verify_config( + pub fn build_config( args: &Vec, test_mode: Option, ) -> Result { - if args.len() < 3 { + if args.len() < 4 { return Err("Arguments missing: "); } - if !args[0].ends_with(".bib") { + if !args[1].ends_with(".bib") { return Err("Invalid file format. Please provide a file with .bib extension."); } - let target_arg = &args[1]; + let target_arg = &args[2]; if !Path::new(target_arg).is_dir() && !target_arg.ends_with(".mdx") { return Err("Invalid target. Please provide a directory or a single MDX file."); } - if !args[2].eq("verify") && !args[2].eq("process") { + if !args[3].eq("verify") && !args[3].eq("process") { return Err("Invalid mode. Please provide either 'verify' or 'process'."); } - let settings = Self::load_or_create_settings("prepyrus_settings.json", test_mode).unwrap(); + let settings: Settings; + if args.len() == 5 { + let ignore_parts_vector: Vec = + args[4].split(',').map(|s| s.to_string()).collect(); + settings = Settings { + ignore_paths: ignore_parts_vector, + }; + } else { + settings = Self::load_or_create_settings("prepyrus_settings.json", test_mode).unwrap(); + } let config = Config { - bib_file: args[0].clone(), - target_path: args[1].clone(), - mode: args[2].clone(), + bib_file: args[1].clone(), + target_path: args[2].clone(), + mode: args[3].clone(), settings, }; @@ -185,9 +195,6 @@ impl Utils { let path = entry.path(); if path.is_dir() { - if path.file_name() == Some(std::ffi::OsStr::new("contributing")) { - continue; // Skip the "contributing" folder - } let sub_paths = Self::extract_mdx_paths(path.to_str().unwrap())?; mdx_paths.extend(sub_paths); } else if path.is_file() && path.extension() == Some(std::ffi::OsStr::new("mdx")) { @@ -207,12 +214,11 @@ impl Utils { mdx_paths: Vec, exceptions: Vec, ) -> Vec { - let mut filtered_paths = Vec::new(); - for path in mdx_paths { - if !exceptions.contains(&path) { - filtered_paths.push(path); - } + let mut filtered_paths = mdx_paths.clone(); + if exceptions.is_empty() { + return filtered_paths; } + filtered_paths.retain(|path| !exceptions.iter().any(|exception| path.contains(exception))); filtered_paths } } diff --git a/tests/integration_test_verify.rs b/tests/integration_test_verify.rs index ebec467..5c962f6 100644 --- a/tests/integration_test_verify.rs +++ b/tests/integration_test_verify.rs @@ -6,6 +6,7 @@ use prepyrus::{ #[test] fn run_verify_with_directory() { let args = vec![ + "program_index".to_string(), "tests/mocks/test.bib".to_string(), "tests/mocks/data".to_string(), "verify".to_string(), @@ -15,7 +16,7 @@ fn run_verify_with_directory() { target_path, mode, settings, - } = Prepyrus::verify_config(&args, Some(LoadOrCreateSettingsTestMode::Test)).unwrap_or_else( + } = Prepyrus::build_config(&args, Some(LoadOrCreateSettingsTestMode::Test)).unwrap_or_else( |e| { eprintln!("Error: {}", e); std::process::exit(1); @@ -33,8 +34,9 @@ fn run_verify_with_directory() { } #[test] -fn run_verify_with_directory_with_ignored_paths() { +fn run_verify_with_directory_with_ignored_paths_from_settings() { let args = vec![ + "program_index".to_string(), "tests/mocks/test.bib".to_string(), "tests/mocks/data".to_string(), "verify".to_string(), @@ -44,7 +46,7 @@ fn run_verify_with_directory_with_ignored_paths() { target_path, mode, settings, - } = Prepyrus::verify_config(&args, Some(LoadOrCreateSettingsTestMode::Test)).unwrap_or_else( + } = Prepyrus::build_config(&args, Some(LoadOrCreateSettingsTestMode::Test)).unwrap_or_else( |e| { eprintln!("Error: {}", e); std::process::exit(1); @@ -61,9 +63,54 @@ fn run_verify_with_directory_with_ignored_paths() { assert!(!articles_file_data.is_empty()); } +#[test] +fn run_verify_with_directory_with_ignored_paths_from_cli_args() { + fn run_test(ignored_paths: &str) { + let args = vec![ + "program_index".to_string(), + "tests/mocks/test.bib".to_string(), + "tests/mocks/data".to_string(), + "verify".to_string(), + ignored_paths.to_string(), + ]; + let Config { + bib_file, + target_path, + mode, + settings, + } = Prepyrus::build_config(&args, None).unwrap_or_else(|e| { + eprintln!("Error: {}", e); + std::process::exit(1); + }); + + let all_entries = Prepyrus::get_all_bib_entries(&bib_file).unwrap(); + let mdx_paths = Prepyrus::get_mdx_paths(&target_path, Some(settings.ignore_paths)).unwrap(); + let articles_file_data = Prepyrus::verify(mdx_paths, &all_entries).unwrap(); + let ignored_paths_vec: Vec = + ignored_paths.split(',').map(|s| s.to_string()).collect(); + assert!(mode == "verify"); + for ignored_path in &ignored_paths_vec { + assert!( + articles_file_data + .iter() + .find(|article| article.path == *ignored_path) + .is_none(), + "Article with the path '{}' found", + ignored_path + ); + } + assert!(articles_file_data.len() >= 1); + assert!(!articles_file_data.is_empty()); + } + + run_test("tests/mocks/data/development.mdx"); + run_test("tests/mocks/data/development.mdx,tests/mocks/data/first-paragraph.mdx"); +} + #[test] fn run_verify_with_single_file() { let args = vec![ + "program_index".to_string(), "tests/mocks/test.bib".to_string(), "tests/mocks/data/science-of-logic-introduction.mdx".to_string(), "verify".to_string(), @@ -73,7 +120,7 @@ fn run_verify_with_single_file() { target_path, mode, settings, - } = Prepyrus::verify_config(&args, Some(LoadOrCreateSettingsTestMode::Test)).unwrap_or_else( + } = Prepyrus::build_config(&args, Some(LoadOrCreateSettingsTestMode::Test)).unwrap_or_else( |e| { eprintln!("Error: {}", e); std::process::exit(1);