Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature/bug fixes #17

Merged
merged 2 commits into from
Nov 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 34 additions & 26 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@ mod cli;
mod file;
use std::path::PathBuf;

use crate::sed::{ReplacePair, ReplacePairError};
use crate::{
file::{
content::wikilink::Alias,
name::{Filename, FilenameLowercase},
},
sed::{ReplacePair, ReplacePairError},
};
use bon::Builder;
use clap::Parser;
use getset::Getters;
use std::io;
use thiserror;
use toml;
Expand All @@ -23,34 +28,35 @@ pub enum Error {

/// Config which contains both the cli and the config file
/// Used to reconcile the two
#[derive(Getters, Builder)]
#[getset(get = "pub")]
#[derive(Builder)]
pub struct Config {
/// See [`self::cli::Config::directories`]
#[builder(default=vec![PathBuf::from(".")])]
directories: Vec<PathBuf>,
pub directories: Vec<PathBuf>,
/// See [`self::cli::Config::ngram_size`]
#[builder(default = 2)]
ngram_size: usize,
pub ngram_size: usize,
/// See [`self::cli::Config::boundary_pattern`]
#[builder(default=r"\s".to_owned())]
boundary_pattern: String,
pub boundary_pattern: String,
/// See [`self::cli::Config::wikilink_pattern`]
#[builder(default=r"#?\[\[(.*?)]]|#([A-Za-z0-9_]+)".to_owned())]
wikilink_pattern: String,
pub wikilink_pattern: String,
/// See [`self::cli::Config::filename_spacing_pattern`]
#[builder(default=r"___|__|-|_|\s".to_owned())]
filename_spacing_pattern: String,
pub filename_spacing_pattern: String,
/// See [`self::cli::Config::filename_match_threshold`]
#[builder(default = 2)]
filename_match_threshold: i64,
pub filename_match_threshold: i64,
/// See [`self::cli::Config::exclude`]
#[builder(default=vec![])]
exclude: Vec<String>,
#[builder(default=Ok(vec![vec![
ReplacePair::new(r"([A-Za-z0-1_-]+).md", r"\[\[$1\]\]").expect("Constant"),
ReplacePair::new(r"___", r"/").expect("Constant"),
]]))]
filepath_to_title: Result<Vec<Vec<ReplacePair>>, ReplacePairError>,
#[builder(default=Ok(vec![vec![
ReplacePair::new(r"\[\[(.*?)\]\]", r"$1.md").expect("Constant"),
ReplacePair::new(r"/", r"___").expect("Constant"),
ReplacePair::new(r"(.*)", r"../pages/$1").expect("Constant"),
]]))]
title_to_filepath: Result<Vec<Vec<ReplacePair>>, ReplacePairError>,
pub exclude: Vec<String>,
/// See [`self::file::Config::filename_to_alias`]
#[builder(default=Ok(ReplacePair::new(r"___", r"/").expect("Constant")))]
pub filename_to_alias: Result<ReplacePair<Filename, Alias>, ReplacePairError>,
/// See [`self::file::Config::alias_to_filename`]
#[builder(default=Ok(ReplacePair::new(r"/", r"___").expect("Constant")))]
pub alias_to_filename: Result<ReplacePair<Alias, FilenameLowercase>, ReplacePairError>,
}

/// Things which implement the partial config trait
Expand All @@ -65,8 +71,10 @@ pub trait Partial {
fn filename_spacing_pattern(&self) -> Option<String>;
fn filename_match_threshold(&self) -> Option<i64>;
fn exclude(&self) -> Option<Vec<String>>;
fn filepath_to_title(&self) -> Option<Result<Vec<Vec<ReplacePair>>, ReplacePairError>>;
fn title_to_filepath(&self) -> Option<Result<Vec<Vec<ReplacePair>>, ReplacePairError>>;
fn filename_to_alias(&self) -> Option<Result<ReplacePair<Filename, Alias>, ReplacePairError>>;
fn alias_to_filename(
&self,
) -> Option<Result<ReplacePair<Alias, FilenameLowercase>, ReplacePairError>>;
}

/// Now we implement a combine function for patrial configs which
Expand All @@ -83,8 +91,8 @@ fn combine_partials(partials: &[&dyn Partial]) -> Config {
.maybe_filename_spacing_pattern(partials.iter().find_map(|p| p.filename_spacing_pattern()))
.maybe_filename_match_threshold(partials.iter().find_map(|p| p.filename_match_threshold()))
.maybe_exclude(partials.iter().find_map(|p| p.exclude()))
.maybe_filepath_to_title(partials.iter().find_map(|p| p.filepath_to_title()))
.maybe_title_to_filepath(partials.iter().find_map(|p| p.title_to_filepath()))
.maybe_filename_to_alias(partials.iter().find_map(|p| p.filename_to_alias()))
.maybe_alias_to_filename(partials.iter().find_map(|p| p.alias_to_filename()))
.build()
}

Expand Down
18 changes: 13 additions & 5 deletions src/config/cli.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
use clap::Parser;
use std::path::PathBuf;

use crate::sed::{ReplacePair, ReplacePairError};
use crate::{
file::{
content::wikilink::Alias,
name::{Filename, FilenameLowercase},
},
sed::{ReplacePair, ReplacePairError},
};

use super::Partial;

Expand All @@ -23,15 +29,15 @@ pub(super) struct Config {
#[clap(short = 'n', long = "ngram")]
pub ngram_size: Option<usize>,

/// Regex pattern to stop n-gram generation on, like , or .")
/// Regex pattern to stop n-gram generation on, like , or .
#[clap(short = 'b', long = "bound")]
pub boundary_pattern: Option<String>,

/// Regex pattern for wikilinks
#[clap(short = 'w', long = "wikilink")]
pub wikilink_pattern: Option<String>,

/// Regex pattern to split filenames on, like _ or -")
/// Regex pattern to split filenames on, like ___ or /
#[clap(short = 's', long = "space")]
pub filename_spacing_pattern: Option<String>,

Expand Down Expand Up @@ -77,10 +83,12 @@ impl Partial for Config {
Some(out)
}
}
fn filepath_to_title(&self) -> Option<Result<Vec<Vec<ReplacePair>>, ReplacePairError>> {
fn filename_to_alias(&self) -> Option<Result<ReplacePair<Filename, Alias>, ReplacePairError>> {
None
}
fn title_to_filepath(&self) -> Option<Result<Vec<Vec<ReplacePair>>, ReplacePairError>> {
fn alias_to_filename(
&self,
) -> Option<Result<ReplacePair<Alias, FilenameLowercase>, ReplacePairError>> {
None
}
}
100 changes: 45 additions & 55 deletions src/config/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,55 @@ use std::path::{Path, PathBuf};

use serde::{Deserialize, Serialize};

use crate::sed::{ReplacePair, ReplacePairError};
use crate::{
file::{
content::wikilink::Alias,
name::{Filename, FilenameLowercase},
},
sed::{ReplacePair, ReplacePairError},
};

use super::{Error, Partial};

#[derive(Serialize, Deserialize, Debug, Default)]
pub(super) struct Config {
/// The directories to search in
/// May provide more than one directory
/// See [`super::cli::Config::directories`]
#[serde(default)]
pub directories: Vec<PathBuf>,

/// Size of the n-grams to generate from filenames
/// Will generate n-grams UP TO and INCLUDING this size
/// See [`super::cli::Config::ngram_size`]
#[serde(default)]
pub ngram_size: Option<usize>,

/// Regex pattern to stop n-gram generation on, like , or .")
/// See [`super::cli::Config::boundary_pattern`]
#[serde(default)]
pub boundary_pattern: Option<String>,

/// Regex pattern for wikilinks
/// See [`super::cli::Config::wikilink_pattern`]
#[serde(default)]
pub wikilink_pattern: Option<String>,

/// Regex pattern to split filenames on, like _ or -")
/// See [`super::cli::Config::filename_spacing_pattern`]
#[serde(default)]
pub filename_spacing_pattern: Option<String>,

/// The minimum score to consider a match for filename ngrams
/// See [`super::cli::Config::filename_match_threshold`]
#[serde(default)]
pub filename_match_threshold: Option<i64>,

/// Exclude certain error codes
/// If an error code **starts with** this string, it will be excluded
/// See [`super::cli::Config::exclude`]
#[serde(default)]
pub exclude: Vec<String>,

/// Link conversion to file path using sed regex
/// Each outer vec contains an inner vec of a sequence of find/replace pairs
/// Meaning you can have several different and independent sequences of find/replace pairs
/// This makes it easier to manage multiple different types of links and conversions
/// Convert an alias to a filename
/// Kinda like a sed command
#[serde(default)]
pub title_to_filepath: Vec<Vec<(String, String)>>,
pub alias_to_filename: (String, String),

/// Convert a filepath to the "title" lr name in a wikilink
/// Each outer vec contains an inner vec of a sequence of find/replace pairs
/// Meaning you can have several different and independent sequences of find/replace pairs
/// This makes it easier to manage multiple different types of links and conversions
/// Convert a filename to an alias
/// Kinda like a sed command
#[serde(default)]
pub filepath_to_title: Vec<Vec<(String, String)>>,
pub filename_to_alias: (String, String),
}

impl Config {
Expand Down Expand Up @@ -100,42 +99,33 @@ impl Partial for Config {
}
}

fn filepath_to_title(&self) -> Option<Result<Vec<Vec<ReplacePair>>, ReplacePairError>> {
let out = self.filepath_to_title.clone();
if out.is_empty() {
None
} else {
let mut res = Vec::new();
for inner in out {
let mut inner_res = Vec::new();
for (find, replace) in inner {
match ReplacePair::new(&find, &replace) {
Ok(pair) => inner_res.push(pair),
Err(e) => return Some(Err(e)),
}
}
res.push(inner_res);
}
Some(Ok(res))
fn alias_to_filename(
&self,
) -> Option<Result<ReplacePair<Alias, FilenameLowercase>, ReplacePairError>> {
let (to, from) = self.alias_to_filename.clone();
match (to.is_empty(), from.is_empty()) {
(true, true) => None,
(false, false) => Some(ReplacePair::new(&to, &from)),
(true, false) => Some(Err(ReplacePairError::ToError(regex::Error::Syntax(
"To is empty".to_string(),
)))),
(false, true) => Some(Err(ReplacePairError::FromError(regex::Error::Syntax(
"From is empty".to_string(),
)))),
}
}
fn title_to_filepath(&self) -> Option<Result<Vec<Vec<ReplacePair>>, ReplacePairError>> {
let out = self.title_to_filepath.clone();
if out.is_empty() {
None
} else {
let mut res = Vec::new();
for inner in out {
let mut inner_res = Vec::new();
for (find, replace) in inner {
match ReplacePair::new(&find, &replace) {
Ok(pair) => inner_res.push(pair),
Err(e) => return Some(Err(e)),
}
}
res.push(inner_res);
}
Some(Ok(res))

fn filename_to_alias(&self) -> Option<Result<ReplacePair<Filename, Alias>, ReplacePairError>> {
let (to, from) = self.alias_to_filename.clone();
match (to.is_empty(), from.is_empty()) {
(true, true) => None,
(false, false) => Some(ReplacePair::new(&to, &from)),
(true, false) => Some(Err(ReplacePairError::ToError(regex::Error::Syntax(
"To is empty".to_string(),
)))),
(false, true) => Some(Err(ReplacePairError::FromError(regex::Error::Syntax(
"From is empty".to_string(),
)))),
}
}
}
16 changes: 8 additions & 8 deletions src/file/content/front_matter.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
mod logseq;
mod yaml;

use super::Error;
use super::{wikilink::Alias, Error};

#[derive(Debug, Default, Clone)]
pub struct FrontMatter {
/// The aliases of the file
pub aliases: Vec<String>,
pub aliases: Vec<Alias>,
}

impl FrontMatter {
Expand All @@ -15,15 +15,15 @@ impl FrontMatter {
let out = yaml::Config::new(contents)?;
if !out.is_empty() {
return Ok(FrontMatter {
aliases: out.aliases.iter().map(|x| x.to_lowercase()).collect(),
aliases: out.aliases.iter().map(|x| Alias::new(x)).collect(),
});
}

// Try to parse as Logseq
let out = logseq::Config::new(contents)?;
if !out.is_empty() {
return Ok(FrontMatter {
aliases: out.aliases.iter().map(|x| x.to_lowercase()).collect(),
aliases: out.aliases.iter().map(|x| Alias::new(x)).collect(),
});
}

Expand All @@ -44,9 +44,9 @@ mod tests {
assert_eq!(
config.aliases,
vec![
"name1".to_string(),
"name2".to_string(),
"name3".to_string()
Alias::new("name1"),
Alias::new("name2"),
Alias::new("name3")
]
);
}
Expand All @@ -58,7 +58,7 @@ mod tests {
let config = FrontMatter::new(text).unwrap();
assert_eq!(
config.aliases,
vec!["a".to_string(), "b".to_string(), "c".to_string()]
vec![Alias::new("a"), Alias::new("b"), Alias::new("c")]
);
}
}
Loading
Loading