This repository has been archived on 2025-10-10. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
mdparser/src/links.rs
2024-03-26 15:13:33 -03:00

144 lines
4.1 KiB
Rust

use std::io::Write;
use std::{cell::RefCell, collections::HashMap};
use std::{fs, io, path::PathBuf};
use comrak::nodes::{Ast, NodeLink, NodeValue};
use comrak::{arena_tree::Node, Arena};
pub struct ParseOptions {
pub alias_prop: Option<String>,
pub path_root: PathBuf,
pub to_complete_paths: bool,
pub remove_unalised: bool,
pub remove_unfound: bool,
}
impl Default for ParseOptions {
fn default() -> Self {
ParseOptions {
alias_prop: None,
path_root: PathBuf::new(),
to_complete_paths: true,
remove_unalised: true,
remove_unfound: true,
}
}
}
#[derive(Debug)]
pub enum ParsingError {
AliasNotFound { file: String },
AliasErr(GetAliasError),
IoErr(io::Error),
}
pub fn parse<'a>(
node: &'a Node<'a, RefCell<Ast>>,
link: &mut NodeLink,
opts: &ParseOptions,
) -> Result<(), ParsingError> {
let path = match find_path(link, &opts.path_root) {
Ok(p) => p,
Err(err) => {
if opts.remove_unfound {
node.children().for_each(|n| node.insert_before(n));
node.detach();
return Ok(());
} else {
return Err(ParsingError::IoErr(err));
}
}
};
if opts.to_complete_paths {
link.url = String::from(path.to_string_lossy())
}
if let Some(a) = &opts.alias_prop {
let alias = match get_alias(&path, &a) {
Ok(a) => a,
Err(err) => return Err(ParsingError::AliasErr(err)),
};
if let Some(v) = alias {
link.url = v;
} else if opts.remove_unalised {
node.children().for_each(|n| node.insert_before(n));
node.detach();
} else {
return Err(ParsingError::AliasNotFound {
file: link.url.clone(),
});
}
}
Ok(())
}
#[derive(Debug)]
pub enum GetAliasError {
IoErr(io::Error),
YamlErr(serde_yaml::Error),
}
pub fn get_alias(path: &PathBuf, alias_prop: &String) -> Result<Option<String>, GetAliasError> {
let file = match fs::read_to_string(path) {
Ok(f) => f,
Err(err) => return Err(GetAliasError::IoErr(err)),
};
let arena = Arena::new();
let ast = comrak::parse_document(&arena, &file, &crate::utils::default_options());
let alias = crate::utils::iter_nodes_r(ast, &|node| {
if let NodeValue::FrontMatter(f) = &node.data.borrow().value {
// Removes starting and trailing "---" delimiters from frontmatter's string.
let f = String::from(f.split("---").collect::<Vec<&str>>()[1]);
let map = match serde_yaml::from_str::<HashMap<String, String>>(&f) {
Ok(m) => m,
Err(err) => {
return Some(Err::<String, GetAliasError>(GetAliasError::YamlErr(err)));
}
};
match map.get(alias_prop) {
// The hashmap will be dropped anyways and free up space,
// so whatever
Some(r) => Some(Ok(r.clone())),
None => None,
}
} else {
None
}
});
match alias {
Some(r) => match r {
Ok(s) => Ok(Some(s)),
Err(err) => Err(err),
},
None => Ok(None),
}
}
pub fn find_path(link: &NodeLink, path_root: &PathBuf) -> Result<PathBuf, io::Error> {
find_file(&link.url, &path_root)
}
fn find_file(file: &String, path: &PathBuf) -> Result<PathBuf, io::Error> {
match fs::read_dir(path)?.find_map(|e| match e {
Ok(e) => {
if e.path().is_dir() {
match find_file(&file, &e.path()) {
Ok(r) => Some(r),
Err(_) => None,
}
} else if file == &e.file_name().to_string_lossy().to_string() {
Some(e.path())
} else {
None
}
}
_ => None,
}) {
Some(r) => Ok(r),
None => Err(io::Error::new(io::ErrorKind::NotFound, "File not found")),
}
}