Basic cours support #44

Merged
Anri merged 67 commits from cours into main 2024-04-01 18:11:49 +02:00
6 changed files with 88 additions and 25 deletions
Showing only changes of commit d025981f0e - Show all commits

1
Cargo.lock generated
View file

@ -930,6 +930,7 @@ dependencies = [
"reqwest", "reqwest",
"rss", "rss",
"serde", "serde",
"serde_json",
"serde_yaml", "serde_yaml",
"toml", "toml",
] ]

View file

@ -17,6 +17,7 @@ ramhorns = "0.14"
toml = "0.8" toml = "0.8"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.9" serde_yaml = "0.9"
serde_json = "1.0"
minify-html = "0.11" minify-html = "0.11"
minify-js = "0.5" minify-js = "0.5"
glob = "0.3" glob = "0.3"

View file

@ -6,7 +6,11 @@ use ramhorns::Content;
use serde::{Deserialize, Deserializer}; use serde::{Deserialize, Deserializer};
use std::fs; use std::fs;
#[derive(Default, Deserialize, Content, Debug)] /// Regular markdown files, no metadata
#[derive(Content, Debug, Default, Deserialize)]
pub struct FileNoMetadata {}
#[derive(Content, Debug, Default, Deserialize)]
pub struct FileMetadataBlog { pub struct FileMetadataBlog {
pub title: Option<String>, pub title: Option<String>,
pub date: Option<Date>, pub date: Option<Date>,
@ -16,7 +20,7 @@ pub struct FileMetadataBlog {
pub toc: Option<bool>, pub toc: Option<bool>,
} }
#[derive(Default, Deserialize, Content, Debug)] #[derive(Content, Debug, Default, Deserialize)]
pub struct FileMetadataContact { pub struct FileMetadataContact {
pub title: String, pub title: String,
pub custom: Option<bool>, pub custom: Option<bool>,
@ -26,7 +30,7 @@ pub struct FileMetadataContact {
pub description: Option<String>, pub description: Option<String>,
} }
#[derive(Default, Deserialize, Content, Debug)] #[derive(Content, Debug, Default, Deserialize)]
pub struct FileMetadataPortfolio { pub struct FileMetadataPortfolio {
pub title: Option<String>, pub title: Option<String>,
pub link: Option<String>, pub link: Option<String>,
@ -34,9 +38,6 @@ pub struct FileMetadataPortfolio {
pub language: Option<String>, pub language: Option<String>,
} }
#[derive(Default, Deserialize, Content, Debug)]
pub struct FileMetadataCours {}
pub enum TypeFileMetadata { pub enum TypeFileMetadata {
Blog, Blog,
Contact, Contact,
@ -44,12 +45,12 @@ pub enum TypeFileMetadata {
Cours, Cours,
} }
#[derive(Default, Deserialize, Content, Debug)] #[derive(Content, Debug, Default, Deserialize)]
pub struct FileMetadata { pub struct FileMetadata {
pub blog: Option<FileMetadataBlog>, pub blog: Option<FileMetadataBlog>,
pub contact: Option<FileMetadataContact>, pub contact: Option<FileMetadataContact>,
pub portfolio: Option<FileMetadataPortfolio>, pub portfolio: Option<FileMetadataPortfolio>,
pub cours: Option<FileMetadataCours>, pub cours: Option<FileNoMetadata>,
} }
#[derive(Content, Debug, Clone)] #[derive(Content, Debug, Clone)]
@ -254,7 +255,7 @@ pub fn get_metadata<'a>(root: &'a AstNode<'a>, mtype: TypeFileMetadata) -> FileM
..FileMetadata::default() ..FileMetadata::default()
}, },
TypeFileMetadata::Cours => FileMetadata { TypeFileMetadata::Cours => FileMetadata {
cours: Some(FileMetadataCours::default()), cours: Some(FileNoMetadata::default()),
..FileMetadata::default() ..FileMetadata::default()
}, },
}, },

View file

@ -1,43 +1,91 @@
use std::path::Path;
use actix_web::{get, web, Responder}; use actix_web::{get, web, Responder};
use glob::glob;
use ramhorns::Content; use ramhorns::Content;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
config::Config, config::Config,
misc::{ misc::{
markdown::{read_file, TypeFileMetadata}, markdown::{read_file, File, TypeFileMetadata},
utils::{make_kw, Html}, utils::{make_kw, Html},
}, },
template::{Infos, NavBar}, template::{Infos, NavBar},
}; };
#[derive(Debug, Deserialize)]
pub struct PathRequest {
q: Option<String>,
}
#[get("/cours")] #[get("/cours")]
async fn page(config: web::Data<Config>) -> impl Responder { async fn page(info: web::Query<PathRequest>, config: web::Data<Config>) -> impl Responder {
Html(build_page(config.get_ref().to_owned())) Html(build_page(info, config.get_ref().to_owned()))
} }
#[derive(Content, Debug)] #[derive(Content, Debug)]
struct CoursTemplate { struct CoursTemplate {
navbar: NavBar, navbar: NavBar,
/* filetree: Truc, */ filetree: String,
content: Option<File>,
} }
/// Get the filetree #[derive(Serialize)]
fn get_filetree() { struct FileNode {
let cours_dir = "data/cours"; name: String,
glob(&format!("{cours_dir}/*")).unwrap(); is_dir: bool,
children: Vec<FileNode>,
} }
/// Get a page /// Build the filetree
fn get_page(filename: &str) { fn get_filetree(dir_path: &str, exclusion_list: &[&str]) -> FileNode {
let cours_dir = "data/cours"; let entries = std::fs::read_dir(dir_path).unwrap();
let post = read_file(&format!("{cours_dir}/{filename}"), TypeFileMetadata::Cours); let mut children = Vec::new();
for entry in entries.filter_map(Result::ok) {
let entry_path = entry.path();
let entry_name = entry_path.file_name().and_then(|n| n.to_str()).unwrap();
if !exclusion_list.contains(&entry_name) {
let filename = entry_name.to_string();
if entry_path.is_file() {
children.push(FileNode {
name: filename,
is_dir: false,
children: vec![],
});
} else {
children.push(get_filetree(entry_path.to_str().unwrap(), exclusion_list));
}
}
}
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>) -> Option<File> {
let filename = match &path.q {
Some(q) => q,
None => "index.md",
};
read_file(&format!("{cours_dir}/{filename}"), TypeFileMetadata::Cours)
} }
// #[once(time = 60)] // #[once(time = 60)]
// TODO: Uncomment before release // TODO: Uncomment before release
fn build_page(config: Config) -> String { fn build_page(info: web::Query<PathRequest>, config: Config) -> String {
let cours_dir = "data/cours";
let filetree = get_filetree(cours_dir, &[]);
config.tmpl.render( config.tmpl.render(
"cours.html", "cours.html",
CoursTemplate { CoursTemplate {
@ -45,6 +93,8 @@ fn build_page(config: Config) -> String {
cours: true, cours: true,
..NavBar::default() ..NavBar::default()
}, },
filetree: serde_json::to_string(&filetree).unwrap(),
content: get_content(cours_dir, &info),
}, },
Infos { Infos {
page_title: Some("Cours".into()), page_title: Some("Cours".into()),

4
static/js/cours.js Normal file
View file

@ -0,0 +1,4 @@
window.addEventListener("load", () => {
/* Ici on va récuperer le JSON envoyer par le serveur et le transformer
* en un jolie filetree */
});

View file

@ -9,9 +9,15 @@
<main> <main>
{{#data}} {{#data}}
<p>Coucou</p> <aside>
<div id="data" data-json="{{filetree}} "></div>
</aside>
{{/data}} {{^content}}
<p>Fichier introuvable</p>
{{/content}} {{#content}}
<article>{{&content}}</article>
{{/content}} {{/data}}
</main> </main>
</body> </body>
</html> </html>