diff --git a/src/routes/blog.rs b/src/routes/blog.rs index 6e27f65..0f26237 100644 --- a/src/routes/blog.rs +++ b/src/routes/blog.rs @@ -6,7 +6,7 @@ use crate::{ config::Config, template::{InfosPage, NavBar}, utils::{ - markdown::File, + markdown::{File, FilePath}, metadata::MType, misc::{make_kw, read_file, Html}, 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}")); // Get about - let about: Option = read_file(format!("{blog_dir}/about.md"), MType::Generic); + let about: Option = read_file( + FilePath { + base: blog_dir, + path: "about.md".to_owned(), + }, + MType::Generic, + ); // Sort from newest to oldest posts.sort_by_cached_key(|p| (p.date.year, p.date.month, p.date.day)); diff --git a/src/routes/contact.rs b/src/routes/contact.rs index f1b0d2d..56a8396 100644 --- a/src/routes/contact.rs +++ b/src/routes/contact.rs @@ -6,7 +6,7 @@ use crate::{ config::Config, template::{InfosPage, NavBar}, utils::{ - markdown::File, + markdown::{File, FilePath}, metadata::MType, misc::{make_kw, read_file, Html}, routes::contact::{find_links, read, remove_paragraphs}, @@ -84,11 +84,26 @@ fn build_page(config: Config) -> String { let ext = ".md"; // 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 forges = read(&format!("{contacts_dir}/forges/*{ext}")); - let mut others = read(&format!("{contacts_dir}/others/*{ext}")); + let mut socials = read(FilePath { + base: contacts_dir.clone(), + 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 [&mut socials, &mut forges, &mut others] diff --git a/src/routes/cours.rs b/src/routes/cours.rs index 4c8c7f0..fb9bdad 100644 --- a/src/routes/cours.rs +++ b/src/routes/cours.rs @@ -8,7 +8,7 @@ use crate::{ config::Config, template::{InfosPage, NavBar}, utils::{ - markdown::File, + markdown::{File, FilePath}, metadata::MType, misc::{make_kw, read_file, Html}, routes::cours::{excluded, get_filetree}, @@ -54,7 +54,13 @@ fn get_content( 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, config: Config) -> String { diff --git a/src/routes/index.rs b/src/routes/index.rs index 11492ed..1b5855e 100644 --- a/src/routes/index.rs +++ b/src/routes/index.rs @@ -6,7 +6,7 @@ use crate::{ config::Config, template::{InfosPage, NavBar}, utils::{ - markdown::File, + markdown::{File, FilePath}, metadata::MType, misc::{make_kw, read_file, Html}, }, @@ -37,7 +37,10 @@ struct StyleAvatar { #[once(time = 60)] fn build_page(config: Config) -> String { 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, ); @@ -68,7 +71,13 @@ fn build_page(config: Config) -> String { } } } 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( diff --git a/src/routes/portfolio.rs b/src/routes/portfolio.rs index b4091f3..e0d3b49 100644 --- a/src/routes/portfolio.rs +++ b/src/routes/portfolio.rs @@ -7,7 +7,7 @@ use crate::{ config::Config, template::{InfosPage, NavBar}, utils::{ - markdown::File, + markdown::{File, FilePath}, metadata::MType, misc::{make_kw, read_file, Html}, }, @@ -32,20 +32,35 @@ struct PortfolioTemplate<'a> { #[once(time = 60)] fn build_page(config: Config) -> String { 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"; // 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 let apps = glob(&format!("{apps_dir}/*{ext}")) .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::>(); let appdata = if apps.is_empty() { - (None, Some(apps_dir.as_str())) + (None, Some(apps_dir.to_string())) } else { (Some(apps), None) }; @@ -53,7 +68,13 @@ fn build_page(config: Config) -> String { // Get archived apps let archived_apps = glob(&format!("{apps_dir}/archive/*{ext}")) .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::>(); let archived_appdata = if archived_apps.is_empty() { @@ -71,7 +92,7 @@ fn build_page(config: Config) -> String { }, about, apps: appdata.0, - location_apps: appdata.1, + location_apps: appdata.1.as_deref(), archived_apps: archived_appdata.0, archived_apps_exists: archived_appdata.1, err_msg: "is empty", diff --git a/src/utils/markdown.rs b/src/utils/markdown.rs index ddb0cb8..83a6492 100644 --- a/src/utils/markdown.rs +++ b/src/utils/markdown.rs @@ -21,8 +21,36 @@ pub struct File { 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 -pub fn get_options(metadata_type: MType) -> ComrakOptions { +pub fn get_options(path: Option, metadata_type: MType) -> ComrakOptions { comrak::Options { extension: comrak::ExtensionOptions::builder() .strikethrough(true) @@ -39,12 +67,27 @@ pub fn get_options(metadata_type: MType) -> ComrakOptions { .math_dollars(true) .underline(true) .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("://") { 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::>(); + parts.pop(); + parts.push(&file); + + format!("/cours?q={}", parts.join("/")) + } + None => format!("/cours?q={file}"), + } })), _ => None, }) @@ -116,7 +159,7 @@ fn custom_img_size(html: &str) -> String { /// Fix local images to base64 and integration of markdown files fn fix_images_and_integration( - path: &str, + path: FilePath, html: &str, metadata_type: MType, recursive: bool, @@ -137,7 +180,8 @@ fn fix_images_and_integration( RewriteStrSettings { element_content_handlers: vec![element!("img", |el| { 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()) .unwrap() .to_string(); @@ -145,10 +189,15 @@ fn fix_images_and_integration( if let Ok(file) = fs::read_to_string(&img_path) { let mime = mime_guess::from_path(&img_path).first_or_octet_stream(); 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; - let data = - read_md(&img_path, &file, metadata_type, Some(options), false); + let data = read_md( + path.from(&img_path), + &file, + metadata_type, + Some(options), + false, + ); el.replace(&data.content, ContentType::Html); // Store the metadata for later merging @@ -177,7 +226,7 @@ fn fix_images_and_integration( /// Transform markdown string to File structure pub fn read_md( - path: &str, + path: FilePath, raw_text: &str, metadata_type: MType, options: Option, @@ -185,7 +234,10 @@ pub fn read_md( ) -> File { 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); // Find metadata diff --git a/src/utils/misc.rs b/src/utils/misc.rs index 834d565..60c09be 100644 --- a/src/utils/misc.rs +++ b/src/utils/misc.rs @@ -12,7 +12,7 @@ use reqwest::Client; use crate::config::FileConfiguration; use super::{ - markdown::{read_md, File}, + markdown::{read_md, File, FilePath}, metadata::{MType, Metadata}, }; @@ -57,13 +57,14 @@ impl Responder for Html { /// Read a file #[cached] -pub fn read_file(filename: String, expected_file: MType) -> Option { - Path::new(&filename.clone()) +pub fn read_file(filename: FilePath, expected_file: MType) -> Option { + let as_str = filename.to_string(); + Path::new(&as_str) .extension() .and_then(|ext| match ext.to_str().unwrap() { - "pdf" => fs::read(filename).map_or(None, |bytes| Some(read_pdf(bytes))), - _ => fs::read_to_string(&filename).map_or(None, |text| { - Some(read_md(&filename, &text, expected_file, None, true)) + "pdf" => fs::read(&as_str).map_or(None, |bytes| Some(read_pdf(bytes))), + _ => fs::read_to_string(&as_str).map_or(None, |text| { + Some(read_md(filename, &text, expected_file, None, true)) }), }) } diff --git a/src/utils/routes/blog.rs b/src/utils/routes/blog.rs index 9e63a67..56a41df 100644 --- a/src/utils/routes/blog.rs +++ b/src/utils/routes/blog.rs @@ -19,7 +19,7 @@ use crate::{ template::InfosPage, utils::{ date::Date, - markdown::{get_options, File}, + markdown::{get_options, File, FilePath}, metadata::{get, FileMetadataBlog, MType}, misc::{get_url, make_kw, read_file}, }, @@ -45,7 +45,13 @@ impl Post { let blog_dir = format!("{data_dir}/{BLOG_DIR}/{POST_DIR}"); 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); } } @@ -79,7 +85,7 @@ pub fn get_posts(location: &str) -> Vec { |text| { 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 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 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 = ( filename, diff --git a/src/utils/routes/contact.rs b/src/utils/routes/contact.rs index 0d9c603..d4128fd 100644 --- a/src/utils/routes/contact.rs +++ b/src/utils/routes/contact.rs @@ -2,7 +2,11 @@ use cached::proc_macro::once; use glob::glob; 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 #[derive(Clone, Debug)] @@ -50,10 +54,10 @@ pub fn remove_paragraphs(list: &mut [File]) { .for_each(|file| file.content = file.content.replace("

", "").replace("

", "")); } -pub fn read(path: &str) -> Vec { - glob(path) +pub fn read(path: FilePath) -> Vec { + glob(&path.to_string()) .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| { !f.metadata .info