diff --git a/Cargo.toml b/Cargo.toml index 11f9441..8b2a2c3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ debug = false [dependencies] bon = "2.3.0" +cached = "0.53.1" clap = { version = "4.5.16", features = ["derive"] } env_logger = "0.11.5" fuzzy-matcher = "0.3.7" diff --git a/src/file.rs b/src/file.rs index 920b816..db1db03 100644 --- a/src/file.rs +++ b/src/file.rs @@ -2,6 +2,10 @@ use std::path::PathBuf; use walkdir::WalkDir; +use thiserror::Error; + +use std; + pub mod content; pub mod name; @@ -18,3 +22,17 @@ pub fn get_files(dirs: Vec) -> Vec { } out } + +/// A bunch of bad things can happen while you're reading files, +/// This covers most of them. +#[derive(Debug, Error)] +pub enum Error { + #[error("Error reading the file.")] + IoError(#[from] std::io::Error), + #[error("Error parsing the yaml based on expected template.")] + SerdeError(#[from] serde_yaml::Error), + #[error("Found duplicate property {0} in file contents")] + DuplicateProperty(String), +} + + diff --git a/src/file/content.rs b/src/file/content.rs index 01e5c86..2eaa75c 100644 --- a/src/file/content.rs +++ b/src/file/content.rs @@ -1,3 +1,19 @@ +use std::{fs, path::PathBuf}; + +use cached::proc_macro::cached; +use front_matter::FrontMatter; + +use super::Error; + pub mod body; pub mod front_matter; pub mod lists; + +/// Get all information from a file needed for the rest of the program +/// Importantly, this is cached, so you don't have to pass around the results +/// Just run it at the very beginning of the program +#[cached(result = true)] +pub fn from_file(path: PathBuf) -> Result { + let contents = fs::read_to_string(path).map_err(Error::IoError)?; + FrontMatter::new(&contents) +} diff --git a/src/file/content/front_matter.rs b/src/file/content/front_matter.rs index b8955ca..9c9a8aa 100644 --- a/src/file/content/front_matter.rs +++ b/src/file/content/front_matter.rs @@ -1,18 +1,16 @@ mod logseq; mod yaml; -use std::path::PathBuf; +use super::Error; -use miette::{miette, Result}; - -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct FrontMatter { /// The aliases of the file pub aliases: Vec, } impl FrontMatter { - pub fn new(contents: &str) -> Result { + pub fn new(contents: &str) -> Result { // Try to parse as YAML let out = yaml::Config::new(contents)?; if !out.is_empty() { @@ -28,11 +26,6 @@ impl FrontMatter { // If we can't parse it, return the default Ok(Self::default()) } - - pub fn from_file(path: &PathBuf) -> Result { - let contents = std::fs::read_to_string(path).map_err(|e| miette!(e))?; - Self::new(&contents) - } } #[cfg(test)] diff --git a/src/file/content/front_matter/logseq.rs b/src/file/content/front_matter/logseq.rs index 944b570..8b9d8dc 100644 --- a/src/file/content/front_matter/logseq.rs +++ b/src/file/content/front_matter/logseq.rs @@ -3,9 +3,10 @@ use serde::{Deserialize, Serialize}; -use miette::{miette, Result}; use regex::Regex; +use crate::file::Error; + #[derive(Serialize, Deserialize, Debug, Default)] pub struct Config { /// The aliases of the file @@ -13,7 +14,7 @@ pub struct Config { pub alias: Vec, } -fn parse_csv(contents: &str) -> Result> { +fn parse_csv(contents: &str) -> Result, Error> { contents .split(',') .map(|s| Ok(s.trim().to_string())) @@ -21,7 +22,7 @@ fn parse_csv(contents: &str) -> Result> { } impl Config { - pub fn new(contents: &str) -> Result { + pub fn new(contents: &str) -> Result { // find alias:: and capture the rest of the line as csv let re = Regex::new(r"alias::\s*(.*)").expect("Its a constant."); @@ -34,7 +35,7 @@ impl Config { // The first capture group is the regex match as a whole // The second is the parenthesized subexpression if caps.len() > 2 { - return Err(miette!("More than one alias property found.")); + return Err(Error::DuplicateProperty("alias".to_owned())); } let alias = parse_csv(&caps[1]).expect("Already checked for exactly one capture group."); diff --git a/src/file/content/front_matter/yaml.rs b/src/file/content/front_matter/yaml.rs index 20850c8..68005dc 100644 --- a/src/file/content/front_matter/yaml.rs +++ b/src/file/content/front_matter/yaml.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; -use miette::{miette, Result}; +use crate::file::Error; #[derive(Serialize, Deserialize, Debug, Default)] pub struct Config { @@ -15,7 +15,7 @@ pub struct Config { } impl Config { - pub fn new(contents: &str) -> Result { + pub fn new(contents: &str) -> Result { // See if contents contains "---" and a newline and another "---" using multiline regex let re = regex::Regex::new(r"(?s)---\n(.*)\n---").expect("Its a constant."); let frontmatter = re.captures(contents); @@ -25,7 +25,7 @@ impl Config { None => Ok(Self::default()), Some(caps) => { // Parse the YAML - serde_yaml::from_str(&caps[1]).map_err(|e| miette!(e)) + serde_yaml::from_str(&caps[1]).map_err(Error::SerdeError) } } } diff --git a/src/rules/broken_wikilink.rs b/src/rules/broken_wikilink.rs index b2c3119..66ac202 100644 --- a/src/rules/broken_wikilink.rs +++ b/src/rules/broken_wikilink.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, path::PathBuf}; use miette::{miette, Diagnostic, NamedSource, Result, SourceSpan}; use thiserror::Error; -use crate::{file::content::front_matter::FrontMatter, rules::duplicate_alias::DuplicateAlias}; +use crate::{file::content::from_file, rules::duplicate_alias::DuplicateAlias}; use super::HasCode; @@ -47,7 +47,7 @@ impl BrokenWikilink { let mut lookup_table = HashMap::::new(); for file_path in files { let front_matter = - FrontMatter::from_file(&file_path).expect("This file was reported as existing"); + from_file(file_path.clone()).map_err(|e| miette!(e))?; for alias in front_matter.aliases { if let Some(out) = lookup_table.insert(alias.clone(), file_path.clone()) { return match DuplicateAlias::new(&alias, &out, &file_path) { diff --git a/src/rules/duplicate_alias.rs b/src/rules/duplicate_alias.rs index f4361f2..218d636 100644 --- a/src/rules/duplicate_alias.rs +++ b/src/rules/duplicate_alias.rs @@ -4,7 +4,7 @@ use bon::Builder; use miette::{miette, Diagnostic, NamedSource, Result, SourceOffset, SourceSpan}; use thiserror::Error; -use crate::{file::content::front_matter::FrontMatter, sed::MissingSubstringError}; +use crate::{file::content::from_file, sed::MissingSubstringError}; use super::HasCode; @@ -173,7 +173,7 @@ impl DuplicateAlias { let mut lookup_table = HashMap::::new(); let mut duplicates: Vec = Vec::new(); for file_path in files { - let front_matter = FrontMatter::from_file(&file_path).map_err(|e| miette!(e))?; + let front_matter = from_file(file_path.clone()).map_err(|e| miette!(e))?; for alias in front_matter.aliases { if let Some(out) = lookup_table.insert(alias.clone(), file_path.clone()) { duplicates.push(