Skip to content

Commit

Permalink
feat: external settings file in json
Browse files Browse the repository at this point in the history
  • Loading branch information
Firgrep committed Aug 31, 2024
1 parent da9b636 commit 6452316
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 68 deletions.
43 changes: 43 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ biblatex = "0.9"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
regex = "1.10.5"
serde_json = "=1.0.1"
22 changes: 13 additions & 9 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,25 +87,29 @@ pub mod validator;

use std::io::Error;

pub use crate::utils::VerifiedConfig;
pub use crate::utils::Config;
use biblatex::Entry;
use utils::{BiblatexUtils, CoreUtils};
use utils::{BiblatexUtils, BibliographyError, LoadOrCreateSettingsTestMode, Utils};
use validator::ArticleFileData;
pub struct Prepyrus {}

impl Prepyrus {
pub fn verify_config(args: &Vec<String>) -> VerifiedConfig {
CoreUtils::verify_config(args)
pub fn verify_config(
args: &Vec<String>,
test_mode: Option<LoadOrCreateSettingsTestMode>,
) -> Result<Config, &'static str> {
Utils::verify_config(args, test_mode)
}

pub fn get_all_bib_entries(
bib_file: &str,
) -> Result<Vec<biblatex::Entry>, Box<dyn std::error::Error>> {
pub fn get_all_bib_entries(bib_file: &str) -> Result<Vec<biblatex::Entry>, BibliographyError> {
Ok(BiblatexUtils::retrieve_bibliography_entries(bib_file)?)
}

pub fn get_mdx_paths(target_path: &str) -> Result<Vec<String>, Box<dyn std::error::Error>> {
Ok(CoreUtils::extract_paths(target_path)?)
pub fn get_mdx_paths(
target_path: &str,
ignore_paths: Option<Vec<String>>,
) -> Result<Vec<String>, Box<dyn std::error::Error>> {
Ok(Utils::extract_paths(target_path, ignore_paths)?)
}

pub fn verify(
Expand Down
27 changes: 16 additions & 11 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,29 @@
use prepyrus::{Prepyrus, VerifiedConfig};
use prepyrus::Prepyrus;

fn main() {
let args: Vec<String> = std::env::args().collect();

let VerifiedConfig {
bib_file,
target_path,
mode,
} = Prepyrus::verify_config(&args);
let _ = run(args).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).unwrap();
println!("===Prepyrus completed successfully!");
}

fn run(args: Vec<String>) -> Result<(), Box<dyn std::error::Error>> {
let config = Prepyrus::verify_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).unwrap();
let articles_file_data = Prepyrus::verify(mdx_paths, &all_entries)?;

// Phase 2: Process MDX files (requires mode to be set to "process")
if mode == "process" {
if config.mode == "process" {
Prepyrus::process(articles_file_data);
}

println!("===Prepyrus completed successfully!");
Ok(())
}
176 changes: 140 additions & 36 deletions src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,40 @@
use biblatex::{Bibliography, Chunk, Date, DateValue, Entry, PermissiveType, Spanned};
use std::{fs, io, path::Path};
use serde::{Deserialize, Serialize};
use std::{
fs::{self, create_dir_all, File},
io::{self, Write},
path::Path,
};

pub struct BiblatexUtils;
pub struct CoreUtils;
pub struct Utils;

#[derive(Debug)]
pub enum BibliographyError {
IoError(std::io::Error),
ParseError(biblatex::ParseError),
}

impl BiblatexUtils {
pub fn retrieve_bibliography_entries(bibliography_path: &str) -> io::Result<Vec<Entry>> {
let bibliography_path = fs::read_to_string(bibliography_path).unwrap();
let bibliography = Bibliography::parse(&bibliography_path).unwrap();
pub fn retrieve_bibliography_entries(
bibliography_path: &str,
) -> Result<Vec<Entry>, BibliographyError> {
let bibliography_path =
fs::read_to_string(bibliography_path).map_err(BibliographyError::IoError)?;
let bibliography =
Bibliography::parse(&bibliography_path).map_err(BibliographyError::ParseError)?;
Ok(bibliography.into_vec())
}

pub fn extract_year(date: &PermissiveType<Date>, reference: String) -> Result<i32, io::Error> {
pub fn extract_year(date: &PermissiveType<Date>, reference: String) -> Result<i32, String> {
match date {
PermissiveType::Typed(date) => match date.value {
DateValue::At(datetime) => Ok(datetime.year),
DateValue::After(datetime) => Ok(datetime.year),
DateValue::Before(datetime) => Ok(datetime.year),
DateValue::Between(start, _end) => Ok(start.year), // Or use end.year
},
_ => Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Unable to retrieve year for: {}", reference),
)),
_ => return Err(format!("Unable to retrieve year for: {}", reference)),
}
}

Expand Down Expand Up @@ -72,59 +84,87 @@ impl BiblatexUtils {
}
}

pub struct VerifiedConfig {
pub struct Config {
pub bib_file: String,
pub target_path: String,
pub mode: String,
pub settings: Settings,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct Settings {
#[serde(default)]
pub ignore_paths: Vec<String>,
}

impl CoreUtils {
pub fn extract_paths(path: &str) -> io::Result<Vec<String>> {
let exceptions = vec![
"src/pages/contributing/",
"src/pages/privacy.mdx",
"src/pages/terms.mdx",
"src/pages/team.mdx",
"src/pages/acknowledgements.mdx",
"src/pages/index.mdx",
"src/pages/_app.mdx",
]
.iter()
.map(|&s| s.to_string())
.collect::<Vec<String>>();
pub enum LoadOrCreateSettingsTestMode {
Test,
}

impl Utils {
fn load_or_create_settings(
settings_path: &str,
test_mode: Option<LoadOrCreateSettingsTestMode>,
) -> Result<Settings, Box<dyn std::error::Error>> {
if let Some(LoadOrCreateSettingsTestMode::Test) = test_mode {
return Ok(Settings {
ignore_paths: vec!["tests/mocks/data/development.mdx".to_string()],
});
}
if !std::path::Path::new(settings_path).exists() {
create_dir_all(std::path::Path::new(settings_path).parent().unwrap())?;

let default_settings = Settings {
ignore_paths: Vec::new(),
};
let config_json = serde_json::to_string_pretty(&default_settings)?;

let mut file = File::create(settings_path)?;
file.write_all(config_json.as_bytes())?;
}

let file = File::open(settings_path)?;
let settings: Settings = serde_json::from_reader(file)?;

Ok(settings)
}

pub fn extract_paths(path: &str, exceptions: Option<Vec<String>>) -> io::Result<Vec<String>> {
let exceptions = exceptions.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(args: &Vec<String>) -> VerifiedConfig {
pub fn verify_config(
args: &Vec<String>,
test_mode: Option<LoadOrCreateSettingsTestMode>,
) -> Result<Config, &'static str> {
if args.len() < 3 {
eprintln!("Arguments missing: <bibliography.bib> <target_dir_or_file> <mode>");
std::process::exit(1);
return Err("Arguments missing: <bibliography.bib> <target_dir_or_file> <mode>");
}
if !args[0].ends_with(".bib") {
eprintln!("Invalid file format. Please provide a file with .bib extension.");
std::process::exit(1);
return Err("Invalid file format. Please provide a file with .bib extension.");
}
let target_arg = &args[1];
if !Path::new(target_arg).is_dir() && !target_arg.ends_with(".mdx") {
eprintln!("Invalid target. Please provide a directory or a single MDX file.");
std::process::exit(1);
return Err("Invalid target. Please provide a directory or a single MDX file.");
}
if !args[2].eq("verify") && !args[2].eq("process") {
eprintln!("Invalid mode. Please provide either 'verify' or 'process'.");
std::process::exit(1);
return Err("Invalid mode. Please provide either 'verify' or 'process'.");
}

let config = VerifiedConfig {
let 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(),
settings,
};

config
Ok(config)
}

/// Excavates all MDX files in a directory and its subdirectories
Expand Down Expand Up @@ -176,3 +216,67 @@ impl CoreUtils {
filtered_paths
}
}

#[cfg(test)]
mod tests_utils {
use super::*;

#[test]
fn load_or_create_settings_with_test_mode() {
let settings = Utils::load_or_create_settings(
"test_prepyrus_settings.json",
Some(LoadOrCreateSettingsTestMode::Test),
)
.expect("Failed to load or create settings");

assert_eq!(
settings.ignore_paths,
vec!["tests/mocks/data/development.mdx"]
);
}

#[test]
fn load_or_create_settings_with_dummy_data() {
let test_settings_path = "test_prepyrus_settings.json";

// Setup: make sure test starts with no existing file
if std::path::Path::new(test_settings_path).exists() {
fs::remove_file(test_settings_path)
.expect("Failed to remove existing test settings file");
}

// 1. Create file with default settings
let _ = Utils::load_or_create_settings(test_settings_path, None)
.expect("Failed to create settings");
assert!(std::path::Path::new(test_settings_path).exists());

// 2. Write to file with test settings
let mut file = fs::OpenOptions::new()
.write(true)
.truncate(true)
.open(test_settings_path)
.expect("Failed to open the settings file for writing");
let modified_settings = Settings {
ignore_paths: vec![
"tests/mocks/data/engels.mdx".to_string(),
"tests/mocks/data/marx.mdx".to_string(),
],
};
let config_json = serde_json::to_string_pretty(&modified_settings)
.expect("Failed to serialize modified settings");
file.write_all(config_json.as_bytes())
.expect("Failed to write to the settings file");

// 3. Read and verify test settings file
let reloaded_settings = Utils::load_or_create_settings(test_settings_path, None)
.expect("Failed to reload settings");
assert_eq!(
reloaded_settings.ignore_paths,
vec!["tests/mocks/data/engels.mdx", "tests/mocks/data/marx.mdx"]
);

// Cleanup: remove test settings file
fs::remove_file(test_settings_path).expect("Failed to remove the test settings file");
assert!(!std::path::Path::new(test_settings_path).exists());
}
}
Loading

0 comments on commit 6452316

Please sign in to comment.