feat: allow subdirs in posts
directory #86
9 changed files with 38 additions and 52 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1095,6 +1095,7 @@ dependencies = [
|
||||||
"serde_yml",
|
"serde_yml",
|
||||||
"toml",
|
"toml",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
|
"walkdir",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -32,6 +32,7 @@ mime_guess = "2.0"
|
||||||
urlencoding = "2.1"
|
urlencoding = "2.1"
|
||||||
regex = "1.10"
|
regex = "1.10"
|
||||||
cyborgtime = "2.1.1"
|
cyborgtime = "2.1.1"
|
||||||
|
walkdir = "2.5"
|
||||||
|
|
||||||
[lints.clippy]
|
[lints.clippy]
|
||||||
pedantic = "warn"
|
pedantic = "warn"
|
||||||
|
|
|
@ -13,8 +13,8 @@ use crate::routes::{
|
||||||
mod config;
|
mod config;
|
||||||
mod template;
|
mod template;
|
||||||
|
|
||||||
mod utils;
|
|
||||||
mod routes;
|
mod routes;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::{config::Config, utils::misc::get_url, template::InfosPage};
|
use crate::{config::Config, template::InfosPage, utils::misc::get_url};
|
||||||
use actix_web::{get, http::header::ContentType, routes, web, HttpResponse, Responder};
|
use actix_web::{get, http::header::ContentType, routes, web, HttpResponse, Responder};
|
||||||
use cached::proc_macro::once;
|
use cached::proc_macro::once;
|
||||||
use ramhorns::Content;
|
use ramhorns::Content;
|
||||||
|
|
|
@ -67,7 +67,7 @@ struct BlogPostTemplate {
|
||||||
toc: String,
|
toc: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/blog/p/{id}")]
|
#[get("/blog/p/{id:.*}")]
|
||||||
pub async fn page(path: web::Path<(String,)>, config: web::Data<Config>) -> impl Responder {
|
pub async fn page(path: web::Path<(String,)>, config: web::Data<Config>) -> impl Responder {
|
||||||
Html(build_post(
|
Html(build_post(
|
||||||
&path.into_inner().0,
|
&path.into_inner().0,
|
||||||
|
|
|
@ -4,8 +4,8 @@ use ramhorns::Content;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
utils::misc::{get_url, Html},
|
|
||||||
template::{InfosPage, NavBar},
|
template::{InfosPage, NavBar},
|
||||||
|
utils::misc::{get_url, Html},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn page(config: web::Data<Config>) -> impl Responder {
|
pub async fn page(config: web::Data<Config>) -> impl Responder {
|
||||||
|
|
|
@ -36,21 +36,12 @@ fn build_page(config: Config) -> String {
|
||||||
let ext = ".md";
|
let ext = ".md";
|
||||||
|
|
||||||
// Get about
|
// Get about
|
||||||
let about = read_file(
|
let about = read_file(format!("{projects_dir}/about.md"), MType::Generic);
|
||||||
format!("{projects_dir}/about.md"),
|
|
||||||
MType::Generic,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Get apps
|
// Get apps
|
||||||
let apps = glob(&format!("{apps_dir}/*{ext}"))
|
let apps = glob(&format!("{apps_dir}/*{ext}"))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.map(|e| {
|
.map(|e| read_file(e.unwrap().to_string_lossy().to_string(), MType::Portfolio).unwrap())
|
||||||
read_file(
|
|
||||||
e.unwrap().to_string_lossy().to_string(),
|
|
||||||
MType::Portfolio,
|
|
||||||
)
|
|
||||||
.unwrap()
|
|
||||||
})
|
|
||||||
.collect::<Vec<File>>();
|
.collect::<Vec<File>>();
|
||||||
|
|
||||||
let appdata = if apps.is_empty() {
|
let appdata = if apps.is_empty() {
|
||||||
|
@ -62,13 +53,7 @@ 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| {
|
.map(|e| read_file(e.unwrap().to_string_lossy().to_string(), MType::Portfolio).unwrap())
|
||||||
read_file(
|
|
||||||
e.unwrap().to_string_lossy().to_string(),
|
|
||||||
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() {
|
||||||
|
|
|
@ -3,8 +3,8 @@ use cached::proc_macro::once;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
utils::misc::{make_kw, Html},
|
|
||||||
template::InfosPage,
|
template::InfosPage,
|
||||||
|
utils::misc::{make_kw, Html},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[get("/web3")]
|
#[get("/web3")]
|
||||||
|
|
|
@ -12,6 +12,7 @@ use chrono::{DateTime, Datelike, Local, NaiveDateTime, Utc};
|
||||||
use chrono_tz::Europe;
|
use chrono_tz::Europe;
|
||||||
use comrak::{parse_document, Arena};
|
use comrak::{parse_document, Arena};
|
||||||
use ramhorns::Content;
|
use ramhorns::Content;
|
||||||
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
|
@ -59,46 +60,44 @@ impl Hash for Post {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_posts(location: &str) -> Vec<Post> {
|
pub fn get_posts(location: &str) -> Vec<Post> {
|
||||||
std::fs::read_dir(location)
|
WalkDir::new(location)
|
||||||
.map_or_else(
|
.into_iter()
|
||||||
|_| vec![],
|
.filter_map(Result::ok)
|
||||||
|res| {
|
.filter(|entry| {
|
||||||
res.flatten()
|
entry.file_type().is_file() && entry.path().extension().is_some_and(|s| s == "md")
|
||||||
.filter(|f| f.path().extension().map_or(false, |ext| ext == "md"))
|
})
|
||||||
.collect::<Vec<std::fs::DirEntry>>()
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.iter()
|
|
||||||
.filter_map(|f| {
|
.filter_map(|f| {
|
||||||
let fname = f.file_name();
|
let fname = f.file_name();
|
||||||
let filename = fname.to_string_lossy();
|
let filename = fname.to_string_lossy();
|
||||||
let file_without_ext = filename.split_at(filename.len() - 3).0;
|
let file_without_ext = filename.split_at(filename.len() - 3).0;
|
||||||
|
|
||||||
let file_metadata = std::fs::read_to_string(format!("{location}/{filename}"))
|
let file_metadata = std::fs::read_to_string(f.path()).map_or_else(
|
||||||
.map_or_else(
|
|_| FileMetadataBlog {
|
||||||
|_| FileMetadataBlog {
|
title: Some(file_without_ext.into()),
|
||||||
title: Some(file_without_ext.into()),
|
..FileMetadataBlog::default()
|
||||||
..FileMetadataBlog::default()
|
},
|
||||||
},
|
|text| {
|
||||||
|text| {
|
let arena = Arena::new();
|
||||||
let arena = Arena::new();
|
|
||||||
|
|
||||||
let options = get_options();
|
let options = get_options();
|
||||||
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();
|
||||||
|
|
||||||
// Always have a title
|
// Always have a title
|
||||||
metadata.title = metadata
|
metadata.title = metadata
|
||||||
.title
|
.title
|
||||||
.map_or_else(|| Some(file_without_ext.into()), Some);
|
.map_or_else(|| Some(file_without_ext.into()), Some);
|
||||||
|
|
||||||
metadata
|
metadata
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
if file_metadata.publish == Some(true) && file_metadata.draft != Some(true) {
|
if file_metadata.publish == Some(true) && file_metadata.draft != Some(true) {
|
||||||
|
let url =
|
||||||
|
f.path().to_string_lossy().strip_prefix(location).unwrap()[1..].to_owned();
|
||||||
|
|
||||||
Some(Post {
|
Some(Post {
|
||||||
url: file_without_ext.into(),
|
url: url[..url.len() - 3].to_owned(),
|
||||||
title: file_metadata.title.unwrap(),
|
title: file_metadata.title.unwrap(),
|
||||||
date: file_metadata.date.unwrap_or({
|
date: file_metadata.date.unwrap_or({
|
||||||
let m = f.metadata().unwrap();
|
let m = f.metadata().unwrap();
|
||||||
|
|
Loading…
Reference in a new issue