fix internal link in file with a specific path

This commit is contained in:
Mylloon 2024-11-28 19:57:19 +01:00
parent 758dfcea5b
commit 48a1518d7f
Signed by: Anri
GPG key ID: A82D63DFF8D1317F
9 changed files with 169 additions and 43 deletions

View file

@ -6,7 +6,7 @@ use crate::{
config::Config, config::Config,
template::{InfosPage, NavBar}, template::{InfosPage, NavBar},
utils::{ utils::{
markdown::File, markdown::{File, FilePath},
metadata::MType, metadata::MType,
misc::{make_kw, read_file, Html}, misc::{make_kw, read_file, Html},
routes::blog::{build_rss, get_post, get_posts, Post, BLOG_DIR, MIME_TYPE_RSS, POST_DIR}, routes::blog::{build_rss, get_post, get_posts, Post, BLOG_DIR, MIME_TYPE_RSS, POST_DIR},
@ -32,7 +32,13 @@ fn build_index(config: Config) -> String {
let mut posts = get_posts(&format!("{blog_dir}/{POST_DIR}")); let mut posts = get_posts(&format!("{blog_dir}/{POST_DIR}"));
// Get about // Get about
let about: Option<File> = read_file(format!("{blog_dir}/about.md"), MType::Generic); let about: Option<File> = read_file(
FilePath {
base: blog_dir,
path: "about.md".to_owned(),
},
MType::Generic,
);
// Sort from newest to oldest // Sort from newest to oldest
posts.sort_by_cached_key(|p| (p.date.year, p.date.month, p.date.day)); posts.sort_by_cached_key(|p| (p.date.year, p.date.month, p.date.day));

View file

@ -6,7 +6,7 @@ use crate::{
config::Config, config::Config,
template::{InfosPage, NavBar}, template::{InfosPage, NavBar},
utils::{ utils::{
markdown::File, markdown::{File, FilePath},
metadata::MType, metadata::MType,
misc::{make_kw, read_file, Html}, misc::{make_kw, read_file, Html},
routes::contact::{find_links, read, remove_paragraphs}, routes::contact::{find_links, read, remove_paragraphs},
@ -84,11 +84,26 @@ fn build_page(config: Config) -> String {
let ext = ".md"; let ext = ".md";
// Get about // Get about
let about = read_file(format!("{contacts_dir}/about.md"), MType::Generic); let about = read_file(
FilePath {
base: contacts_dir.clone(),
path: "about.md".to_owned(),
},
MType::Generic,
);
let mut socials = read(&format!("{contacts_dir}/socials/*{ext}")); let mut socials = read(FilePath {
let mut forges = read(&format!("{contacts_dir}/forges/*{ext}")); base: contacts_dir.clone(),
let mut others = read(&format!("{contacts_dir}/others/*{ext}")); path: format!("socials/*{ext}"),
});
let mut forges = read(FilePath {
base: contacts_dir.clone(),
path: format!("forges/*{ext}"),
});
let mut others = read(FilePath {
base: contacts_dir,
path: format!("others/*{ext}"),
});
// Remove paragraphs in custom statements // Remove paragraphs in custom statements
[&mut socials, &mut forges, &mut others] [&mut socials, &mut forges, &mut others]

View file

@ -8,7 +8,7 @@ use crate::{
config::Config, config::Config,
template::{InfosPage, NavBar}, template::{InfosPage, NavBar},
utils::{ utils::{
markdown::File, markdown::{File, FilePath},
metadata::MType, metadata::MType,
misc::{make_kw, read_file, Html}, misc::{make_kw, read_file, Html},
routes::cours::{excluded, get_filetree}, routes::cours::{excluded, get_filetree},
@ -54,7 +54,13 @@ fn get_content(
return None; return None;
} }
read_file(format!("{cours_dir}/{filename}"), MType::Cours) read_file(
FilePath {
base: cours_dir.to_owned(),
path: filename.to_owned(),
},
MType::Cours,
)
} }
fn build_page(info: &web::Query<PathRequest>, config: Config) -> String { fn build_page(info: &web::Query<PathRequest>, config: Config) -> String {

View file

@ -6,7 +6,7 @@ use crate::{
config::Config, config::Config,
template::{InfosPage, NavBar}, template::{InfosPage, NavBar},
utils::{ utils::{
markdown::File, markdown::{File, FilePath},
metadata::MType, metadata::MType,
misc::{make_kw, read_file, Html}, misc::{make_kw, read_file, Html},
}, },
@ -37,7 +37,10 @@ struct StyleAvatar {
#[once(time = 60)] #[once(time = 60)]
fn build_page(config: Config) -> String { fn build_page(config: Config) -> String {
let mut file = read_file( let mut file = read_file(
format!("{}/index.md", config.locations.data_dir), FilePath {
base: config.locations.data_dir,
path: "index.md".to_owned(),
},
MType::Index, MType::Index,
); );
@ -68,7 +71,13 @@ fn build_page(config: Config) -> String {
} }
} }
} else { } else {
file = read_file("README.md".to_string(), MType::Generic); file = read_file(
FilePath {
base: String::new(),
path: "README.md".to_owned(),
},
MType::Generic,
);
} }
config.tmpl.render( config.tmpl.render(

View file

@ -7,7 +7,7 @@ use crate::{
config::Config, config::Config,
template::{InfosPage, NavBar}, template::{InfosPage, NavBar},
utils::{ utils::{
markdown::File, markdown::{File, FilePath},
metadata::MType, metadata::MType,
misc::{make_kw, read_file, Html}, misc::{make_kw, read_file, Html},
}, },
@ -32,20 +32,35 @@ struct PortfolioTemplate<'a> {
#[once(time = 60)] #[once(time = 60)]
fn build_page(config: Config) -> String { fn build_page(config: Config) -> String {
let projects_dir = format!("{}/projects", config.locations.data_dir); let projects_dir = format!("{}/projects", config.locations.data_dir);
let apps_dir = format!("{projects_dir}/apps"); let apps_dir = FilePath {
base: format!("{projects_dir}/apps"),
path: String::new(),
};
let ext = ".md"; let ext = ".md";
// Get about // Get about
let about = read_file(format!("{projects_dir}/about.md"), MType::Generic); let about = read_file(
FilePath {
base: projects_dir,
path: "about.md".to_owned(),
},
MType::Generic,
);
// Get apps // Get apps
let apps = glob(&format!("{apps_dir}/*{ext}")) let apps = glob(&format!("{apps_dir}/*{ext}"))
.unwrap() .unwrap()
.map(|e| read_file(e.unwrap().to_string_lossy().to_string(), MType::Portfolio).unwrap()) .map(|e| {
read_file(
apps_dir.from(&e.unwrap().to_string_lossy()),
MType::Portfolio,
)
.unwrap()
})
.collect::<Vec<File>>(); .collect::<Vec<File>>();
let appdata = if apps.is_empty() { let appdata = if apps.is_empty() {
(None, Some(apps_dir.as_str())) (None, Some(apps_dir.to_string()))
} else { } else {
(Some(apps), None) (Some(apps), None)
}; };
@ -53,7 +68,13 @@ fn build_page(config: Config) -> String {
// Get archived apps // Get archived apps
let archived_apps = glob(&format!("{apps_dir}/archive/*{ext}")) let archived_apps = glob(&format!("{apps_dir}/archive/*{ext}"))
.unwrap() .unwrap()
.map(|e| read_file(e.unwrap().to_string_lossy().to_string(), MType::Portfolio).unwrap()) .map(|e| {
read_file(
apps_dir.from(&e.unwrap().to_string_lossy()),
MType::Portfolio,
)
.unwrap()
})
.collect::<Vec<File>>(); .collect::<Vec<File>>();
let archived_appdata = if archived_apps.is_empty() { let archived_appdata = if archived_apps.is_empty() {
@ -71,7 +92,7 @@ fn build_page(config: Config) -> String {
}, },
about, about,
apps: appdata.0, apps: appdata.0,
location_apps: appdata.1, location_apps: appdata.1.as_deref(),
archived_apps: archived_appdata.0, archived_apps: archived_appdata.0,
archived_apps_exists: archived_appdata.1, archived_apps_exists: archived_appdata.1,
err_msg: "is empty", err_msg: "is empty",

View file

@ -21,8 +21,36 @@ pub struct File {
pub content: String, pub content: String,
} }
#[derive(Hash, PartialEq, Eq, Clone)]
pub struct FilePath {
pub base: String,
pub path: String,
}
impl std::fmt::Display for FilePath {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
if self.path.is_empty() {
return write!(f, "{}", self.base);
}
if self.base.is_empty() {
return write!(f, "{}", self.path);
}
write!(f, "{}/{}", self.base, self.path)
}
}
impl FilePath {
pub fn from(&self, fullpath: &str) -> Self {
Self {
base: self.base.clone(),
path: fullpath.to_owned().split_off(self.base.len()),
}
}
}
/// Options used for parser and compiler MD --> HTML /// Options used for parser and compiler MD --> HTML
pub fn get_options(metadata_type: MType) -> ComrakOptions { pub fn get_options(path: Option<FilePath>, metadata_type: MType) -> ComrakOptions {
comrak::Options { comrak::Options {
extension: comrak::ExtensionOptions::builder() extension: comrak::ExtensionOptions::builder()
.strikethrough(true) .strikethrough(true)
@ -39,12 +67,27 @@ pub fn get_options(metadata_type: MType) -> ComrakOptions {
.math_dollars(true) .math_dollars(true)
.underline(true) .underline(true)
.maybe_link_url_rewriter(match metadata_type { .maybe_link_url_rewriter(match metadata_type {
MType::Cours => Some(Arc::new(|url: &str| { MType::Cours => Some(Arc::new(move |url: &str| {
if url.contains("://") { if url.contains("://") {
return String::from(url); return String::from(url);
} }
format!("/cours?q={url}") let file = if url.starts_with("./") {
url.to_owned().split_off(2)
} else {
url.to_owned()
};
match &path {
Some(p) => {
let mut parts = p.path.split('/').collect::<Vec<_>>();
parts.pop();
parts.push(&file);
format!("/cours?q={}", parts.join("/"))
}
None => format!("/cours?q={file}"),
}
})), })),
_ => None, _ => None,
}) })
@ -116,7 +159,7 @@ fn custom_img_size(html: &str) -> String {
/// Fix local images to base64 and integration of markdown files /// Fix local images to base64 and integration of markdown files
fn fix_images_and_integration( fn fix_images_and_integration(
path: &str, path: FilePath,
html: &str, html: &str,
metadata_type: MType, metadata_type: MType,
recursive: bool, recursive: bool,
@ -137,7 +180,8 @@ fn fix_images_and_integration(
RewriteStrSettings { RewriteStrSettings {
element_content_handlers: vec![element!("img", |el| { element_content_handlers: vec![element!("img", |el| {
if let Some(src) = el.get_attribute("src") { if let Some(src) = el.get_attribute("src") {
let img_src = Path::new(path).parent().unwrap(); let path_str = path.to_string();
let img_src = Path::new(&path_str).parent().unwrap();
let img_path = urlencoding::decode(img_src.join(src).to_str().unwrap()) let img_path = urlencoding::decode(img_src.join(src).to_str().unwrap())
.unwrap() .unwrap()
.to_string(); .to_string();
@ -145,10 +189,15 @@ fn fix_images_and_integration(
if let Ok(file) = fs::read_to_string(&img_path) { if let Ok(file) = fs::read_to_string(&img_path) {
let mime = mime_guess::from_path(&img_path).first_or_octet_stream(); let mime = mime_guess::from_path(&img_path).first_or_octet_stream();
if recursive && mime == "text/markdown" { if recursive && mime == "text/markdown" {
let mut options = get_options(metadata_type); let mut options = get_options(Some(path.clone()), metadata_type);
options.extension.footnotes = false; options.extension.footnotes = false;
let data = let data = read_md(
read_md(&img_path, &file, metadata_type, Some(options), false); path.from(&img_path),
&file,
metadata_type,
Some(options),
false,
);
el.replace(&data.content, ContentType::Html); el.replace(&data.content, ContentType::Html);
// Store the metadata for later merging // Store the metadata for later merging
@ -177,7 +226,7 @@ fn fix_images_and_integration(
/// Transform markdown string to File structure /// Transform markdown string to File structure
pub fn read_md( pub fn read_md(
path: &str, path: FilePath,
raw_text: &str, raw_text: &str,
metadata_type: MType, metadata_type: MType,
options: Option<Options>, options: Option<Options>,
@ -185,7 +234,10 @@ pub fn read_md(
) -> File { ) -> File {
let arena = Arena::new(); let arena = Arena::new();
let mut opt = options.map_or_else(|| get_options(metadata_type), |specific_opt| specific_opt); let mut opt = options.map_or_else(
|| get_options(Some(path.clone()), metadata_type),
|specific_opt| specific_opt,
);
let root = parse_document(&arena, raw_text, &opt); let root = parse_document(&arena, raw_text, &opt);
// Find metadata // Find metadata

View file

@ -12,7 +12,7 @@ use reqwest::Client;
use crate::config::FileConfiguration; use crate::config::FileConfiguration;
use super::{ use super::{
markdown::{read_md, File}, markdown::{read_md, File, FilePath},
metadata::{MType, Metadata}, metadata::{MType, Metadata},
}; };
@ -57,13 +57,14 @@ impl Responder for Html {
/// Read a file /// Read a file
#[cached] #[cached]
pub fn read_file(filename: String, expected_file: MType) -> Option<File> { pub fn read_file(filename: FilePath, expected_file: MType) -> Option<File> {
Path::new(&filename.clone()) let as_str = filename.to_string();
Path::new(&as_str)
.extension() .extension()
.and_then(|ext| match ext.to_str().unwrap() { .and_then(|ext| match ext.to_str().unwrap() {
"pdf" => fs::read(filename).map_or(None, |bytes| Some(read_pdf(bytes))), "pdf" => fs::read(&as_str).map_or(None, |bytes| Some(read_pdf(bytes))),
_ => fs::read_to_string(&filename).map_or(None, |text| { _ => fs::read_to_string(&as_str).map_or(None, |text| {
Some(read_md(&filename, &text, expected_file, None, true)) Some(read_md(filename, &text, expected_file, None, true))
}), }),
}) })
} }

View file

@ -19,7 +19,7 @@ use crate::{
template::InfosPage, template::InfosPage,
utils::{ utils::{
date::Date, date::Date,
markdown::{get_options, File}, markdown::{get_options, File, FilePath},
metadata::{get, FileMetadataBlog, MType}, metadata::{get, FileMetadataBlog, MType},
misc::{get_url, make_kw, read_file}, misc::{get_url, make_kw, read_file},
}, },
@ -45,7 +45,13 @@ impl Post {
let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}"); let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}");
let ext = ".md"; let ext = ".md";
if let Some(file) = read_file(format!("{blog_dir}/{}{ext}", self.url), MType::Blog) { if let Some(file) = read_file(
FilePath {
base: blog_dir,
path: format!("{}{ext}", self.url),
},
MType::Blog,
) {
self.content = Some(file.content); self.content = Some(file.content);
} }
} }
@ -79,7 +85,7 @@ pub fn get_posts(location: &str) -> Vec<Post> {
|text| { |text| {
let arena = Arena::new(); let arena = Arena::new();
let options = get_options(MType::Generic); let options = get_options(None, MType::Generic);
let root = parse_document(&arena, &text, &options); let root = parse_document(&arena, &text, &options);
let mut metadata = get(root, MType::Blog).blog.unwrap(); let mut metadata = get(root, MType::Blog).blog.unwrap();
@ -137,7 +143,13 @@ pub fn get_post(
let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}"); let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}");
let ext = ".md"; let ext = ".md";
*post = read_file(format!("{blog_dir}/{filename}{ext}"), MType::Blog); *post = read_file(
FilePath {
base: blog_dir,
path: format!("{filename}{ext}"),
},
MType::Blog,
);
let default = ( let default = (
filename, filename,

View file

@ -2,7 +2,11 @@ use cached::proc_macro::once;
use glob::glob; use glob::glob;
use std::fs::read_to_string; use std::fs::read_to_string;
use crate::utils::{markdown::File, metadata::MType, misc::read_file}; use crate::utils::{
markdown::{File, FilePath},
metadata::MType,
misc::read_file,
};
/// Contact node /// Contact node
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -50,10 +54,10 @@ pub fn remove_paragraphs(list: &mut [File]) {
.for_each(|file| file.content = file.content.replace("<p>", "").replace("</p>", "")); .for_each(|file| file.content = file.content.replace("<p>", "").replace("</p>", ""));
} }
pub fn read(path: &str) -> Vec<File> { pub fn read(path: FilePath) -> Vec<File> {
glob(path) glob(&path.to_string())
.unwrap() .unwrap()
.map(|e| read_file(e.unwrap().to_string_lossy().to_string(), MType::Contact).unwrap()) .map(|e| read_file(path.from(&e.unwrap().to_string_lossy()), MType::Contact).unwrap())
.filter(|f| { .filter(|f| {
!f.metadata !f.metadata
.info .info