use actix_web::{get, routes, web, HttpRequest, Responder}; use cached::proc_macro::once; use glob::glob; use ramhorns::Content; use std::fs::read_to_string; use crate::{ config::Config, misc::{ markdown::{read_file, File, TypeFileMetadata}, utils::{make_kw, Html}, }, template::{Infos, NavBar}, }; pub fn pages(cfg: &mut web::ServiceConfig) { // Here define the services used let routes = |route_path| { web::scope(route_path) .service(page) .service(service_redirection) }; // Here define the routes aliases cfg.service(routes("/contact")).service(routes("/c")); } #[get("")] async fn page(config: web::Data) -> impl Responder { Html(build_page(config.get_ref().to_owned())) } /// Contact node #[derive(Clone, Debug)] struct ContactLink { service: String, scope: Option, link: String, } #[once(time = 60)] fn find_links(directory: String) -> Vec { // TOML filename let toml_file = "links.toml"; // Read the TOML file and parse it let toml_str = read_to_string(format!("{directory}/{toml_file}")).unwrap_or_default(); let mut redirections = vec![]; match toml::de::from_str::(&toml_str) { Ok(data) => { if let Some(section) = data.as_table() { section.iter().for_each(|(key, value)| { // Scopes are delimited with `/` let (service, scope) = match key.split_once('/') { Some((service, scope)) => (service.to_owned(), Some(scope.to_owned())), None => (key.to_owned(), None), }; redirections.push(ContactLink { service, scope, link: value.as_str().unwrap().to_owned(), }); }); } } Err(_) => return vec![], } redirections } #[routes] #[get("/{service}")] #[get("/{service}/{scope}")] async fn service_redirection(config: web::Data, req: HttpRequest) -> impl Responder { let info = req.match_info(); let link = find_links(format!("{}/contacts", config.locations.data_dir)) .iter() // Find requested service .filter(|&x| x.service == *info.query("service")) // Search for a potential scope .filter(|&x| match (info.get("scope"), x.scope.to_owned()) { // The right scope is accepted (Some(str_value), Some(string_value)) if str_value == string_value.as_str() => true, // No scope provided is accepted (None, None) => true, // Else we reject _ => false, }) // Returns the link .map(|data| data.link.clone()) .collect::>(); // This shouldn't be more than one link here match link.len() { // Redirect to the desired service 1 => actix_web::web::Redirect::to(link[0].clone()), // By default, returns to the contact page _ => actix_web::web::Redirect::to("/contact"), } } #[derive(Content, Debug)] struct NetworksTemplate { navbar: NavBar, socials_exists: bool, socials: Vec, forges_exists: bool, forges: Vec, others_exists: bool, others: Vec, } fn remove_paragraphs(list: &mut [File]) { list.iter_mut() .for_each(|file| file.content = file.content.replace("

", "").replace("

", "")); } #[once(time = 60)] fn build_page(config: Config) -> String { let contacts_dir = format!("{}/contacts", config.locations.data_dir); let ext = ".md"; let socials_dir = "socials"; let mut socials = glob(&format!("{contacts_dir}/{socials_dir}/*{ext}")) .unwrap() .map(|e| read_file(&e.unwrap().to_string_lossy(), TypeFileMetadata::Contact).unwrap()) .collect::>(); let forges_dir = "forges"; let mut forges = glob(&format!("{contacts_dir}/{forges_dir}/*{ext}")) .unwrap() .map(|e| read_file(&e.unwrap().to_string_lossy(), TypeFileMetadata::Contact).unwrap()) .collect::>(); let others_dir = "others"; let mut others = glob(&format!("{contacts_dir}/{others_dir}/*{ext}")) .unwrap() .map(|e| read_file(&e.unwrap().to_string_lossy(), TypeFileMetadata::Contact).unwrap()) .collect::>(); // Remove paragraphs in custom statements [&mut socials, &mut forges, &mut others] .iter_mut() .for_each(|it| remove_paragraphs(it)); config.tmpl.render( "contact/index.html", NetworksTemplate { navbar: NavBar { contact: true, ..NavBar::default() }, socials_exists: !socials.is_empty(), socials, forges_exists: !forges.is_empty(), forges, others_exists: !others.is_empty(), others, }, Infos { page_title: Some("Contacts".into()), page_desc: Some(format!("Réseaux d'{}", config.fc.name.unwrap_or_default())), page_kw: make_kw(&["réseaux sociaux", "email", "contact", "linktree"]), }, ) }