mylloon.fr/src/routes/cours.rs

177 lines
4.5 KiB
Rust
Raw Normal View History

use std::path::Path;
use actix_web::{get, web, Responder};
use cached::proc_macro::cached;
use ramhorns::Content;
use regex::Regex;
use serde::{Deserialize, Serialize};
use crate::{
config::Config,
2024-11-09 16:36:04 +01:00
template::{InfosPage, NavBar},
utils::{
markdown::{File, TypeFileMetadata},
utils::{make_kw, read_file, Html},
},
};
#[derive(Debug, Deserialize)]
pub struct PathRequest {
q: Option<String>,
}
2023-04-16 13:18:15 +02:00
#[get("/cours")]
2024-06-17 15:56:57 +02:00
pub async fn page(info: web::Query<PathRequest>, config: web::Data<Config>) -> impl Responder {
2024-05-28 20:26:58 +02:00
Html(build_page(&info, config.get_ref().to_owned()))
}
#[derive(Content, Debug)]
struct CoursTemplate {
navbar: NavBar,
filetree: String,
content: Option<File>,
}
#[derive(Clone, Debug, Serialize)]
struct FileNode {
name: String,
is_dir: bool,
children: Vec<FileNode>,
}
#[cached]
fn compile_patterns(exclusion_list: Vec<String>) -> Vec<Regex> {
exclusion_list
.iter()
.map(|pattern| Regex::new(pattern).unwrap())
.collect()
}
2024-11-09 16:34:51 +01:00
fn get_filetree(
dir_path: &str,
exclusion_list: &[String],
exclusion_patterns: &[Regex],
) -> FileNode {
let children = std::fs::read_dir(dir_path)
.unwrap()
.filter_map(Result::ok)
.filter_map(|entry| {
let entry_path = entry.path();
let entry_name = entry_path.file_name()?.to_string_lossy().to_string();
2024-11-09 16:34:51 +01:00
// Exclusion checks
if exclusion_list
.iter()
.any(|excluded_term| entry_name.contains(excluded_term.as_str()))
{
return None;
}
if exclusion_patterns.iter().any(|re| re.is_match(&entry_name)) {
return None;
}
if entry_path.is_file() {
Some(FileNode {
name: entry_name,
is_dir: false,
children: vec![],
})
} else {
// Exclude empty directories
2024-11-09 16:34:51 +01:00
let children_of_children = get_filetree(
entry_path.to_str().unwrap(),
exclusion_list,
exclusion_patterns,
);
if children_of_children.is_dir && children_of_children.children.is_empty() {
None
} else {
Some(children_of_children)
}
}
})
.collect();
FileNode {
name: Path::new(dir_path)
.file_name()
.unwrap()
.to_string_lossy()
.to_string(),
is_dir: true,
children,
}
}
/// Get a page content
fn get_content(
cours_dir: &str,
path: &web::Query<PathRequest>,
2024-05-28 20:26:58 +02:00
exclusion_list: &[String],
2024-11-09 16:34:51 +01:00
exclusion_patterns: &[Regex],
) -> Option<File> {
2024-05-28 20:58:41 +02:00
let filename = path.q.as_ref().map_or("index.md", |q| q);
2024-11-09 16:34:51 +01:00
// Exclusion checks
if exclusion_list
.iter()
.any(|excluded_term| filename.contains(excluded_term.as_str()))
{
return None;
}
2024-11-09 16:34:51 +01:00
if exclusion_patterns.iter().any(|re| re.is_match(filename)) {
return None;
}
read_file(
&format!("{cours_dir}/{filename}"),
2024-05-28 20:26:58 +02:00
&TypeFileMetadata::Generic,
)
}
2024-05-28 20:26:58 +02:00
fn build_page(info: &web::Query<PathRequest>, config: Config) -> String {
let cours_dir = "data/cours";
2024-11-09 16:34:51 +01:00
let (ep, el): (_, Vec<String>) = config
.fc
.exclude_courses
.unwrap()
.into_iter()
.partition(|item| item.starts_with('/'));
let exclusion_list = {
let mut base = vec!["../".to_owned()];
base.extend(el);
base
};
let exclusion_patterns: Vec<Regex> =
compile_patterns(ep.iter().map(|r| r[1..r.len() - 1].to_owned()).collect());
let filetree = get_filetree(cours_dir, &exclusion_list, &exclusion_patterns);
config.tmpl.render(
"cours.html",
CoursTemplate {
navbar: NavBar {
cours: true,
..NavBar::default()
},
filetree: serde_json::to_string(&filetree).unwrap(),
2024-11-09 16:34:51 +01:00
content: get_content(cours_dir, info, &exclusion_list, &exclusion_patterns),
},
2024-05-28 20:26:58 +02:00
InfosPage {
title: Some("Cours".into()),
desc: Some("Cours à l'univ".into()),
kw: Some(make_kw(&[
"cours",
"études",
"université",
"licence",
"master",
"notes",
"digital garden",
2024-05-28 20:26:58 +02:00
])),
},
)
2023-04-16 13:18:15 +02:00
}