site_footer and post listing order
This commit is contained in:
parent
e0ad3f29ae
commit
c9704a20f6
1
axum_server/src/components/mod.rs
Normal file
1
axum_server/src/components/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod site_footer;
|
21
axum_server/src/components/site_footer.rs
Normal file
21
axum_server/src/components/site_footer.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
use askama::Template;
|
||||||
|
|
||||||
|
use crate::{pages::post::PostMetadata, post_list::get_post_list, post_parser::ParseResult};
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "site_footer.html")]
|
||||||
|
pub struct SiteFooter {
|
||||||
|
pub latest_posts: Vec<ParseResult<PostMetadata>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn render_site_footer() -> SiteFooter {
|
||||||
|
let mut post_list = get_post_list::<PostMetadata>().await.unwrap_or(vec![]);
|
||||||
|
post_list.sort_by_key(|post| post.metadata.date);
|
||||||
|
post_list.reverse();
|
||||||
|
|
||||||
|
let latest_posts = post_list
|
||||||
|
.into_iter()
|
||||||
|
.take(6)
|
||||||
|
.collect::<Vec<ParseResult<PostMetadata>>>();
|
||||||
|
SiteFooter { latest_posts }
|
||||||
|
}
|
@ -1,8 +1,9 @@
|
|||||||
use askama::filters::format;
|
|
||||||
use axum;
|
use axum;
|
||||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
|
||||||
|
mod components;
|
||||||
mod pages;
|
mod pages;
|
||||||
|
mod post_list;
|
||||||
mod post_parser;
|
mod post_parser;
|
||||||
mod router;
|
mod router;
|
||||||
// mod template;
|
// mod template;
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
|
||||||
|
use crate::components::site_footer::{render_site_footer, SiteFooter};
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
#[template(path = "index.html")]
|
#[template(path = "index.html")]
|
||||||
pub struct IndexTemplate {}
|
pub struct IndexTemplate {
|
||||||
|
site_footer: SiteFooter,
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn render_index() -> IndexTemplate {
|
pub async fn render_index() -> IndexTemplate {
|
||||||
IndexTemplate {}
|
let site_footer = render_site_footer().await;
|
||||||
|
IndexTemplate { site_footer }
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,10 @@ use axum::{extract::Path, http::StatusCode};
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::post_parser::{deserialize_date, parse_post};
|
use crate::{
|
||||||
|
components::site_footer::{render_site_footer, SiteFooter},
|
||||||
|
post_parser::{deserialize_date, parse_post},
|
||||||
|
};
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct PostMetadata {
|
pub struct PostMetadata {
|
||||||
@ -22,13 +25,20 @@ pub struct PostMetadata {
|
|||||||
pub struct PostTemplate {
|
pub struct PostTemplate {
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub body: String,
|
pub body: String,
|
||||||
|
pub site_footer: SiteFooter,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn render_post(Path(post_id): Path<String>) -> Result<PostTemplate, StatusCode> {
|
pub async fn render_post(Path(post_id): Path<String>) -> Result<PostTemplate, StatusCode> {
|
||||||
|
let site_footer = tokio::spawn(render_site_footer());
|
||||||
let path = format!("../_posts/blog/{}.md", post_id);
|
let path = format!("../_posts/blog/{}.md", post_id);
|
||||||
let parsed = parse_post::<PostMetadata>(&path).await?;
|
let parsed = parse_post::<PostMetadata>(&path).await?;
|
||||||
|
|
||||||
|
let site_footer = site_footer
|
||||||
|
.await
|
||||||
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||||
Ok(PostTemplate {
|
Ok(PostTemplate {
|
||||||
title: parsed.metadata.title,
|
title: parsed.metadata.title,
|
||||||
body: parsed.body,
|
body: parsed.body,
|
||||||
|
site_footer,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
use askama::Template;
|
use askama::Template;
|
||||||
use axum::{extract::Path, http::StatusCode};
|
use axum::{extract::Path, http::StatusCode};
|
||||||
use tokio::fs::read_dir;
|
|
||||||
use tracing::info;
|
|
||||||
|
|
||||||
use crate::post_parser::{parse_post, ParseResult};
|
use crate::{
|
||||||
|
components::site_footer::{render_site_footer, SiteFooter},
|
||||||
|
post_list::get_post_list,
|
||||||
|
post_parser::ParseResult,
|
||||||
|
};
|
||||||
|
|
||||||
use super::post::PostMetadata;
|
use super::post::PostMetadata;
|
||||||
|
|
||||||
@ -13,32 +15,20 @@ pub struct PostListTemplate {
|
|||||||
pub title: String,
|
pub title: String,
|
||||||
pub posts: Vec<ParseResult<PostMetadata>>,
|
pub posts: Vec<ParseResult<PostMetadata>>,
|
||||||
pub tag: Option<String>,
|
pub tag: Option<String>,
|
||||||
|
pub site_footer: SiteFooter,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn render_post_list(tag: Option<Path<String>>) -> Result<PostListTemplate, StatusCode> {
|
pub async fn render_post_list(tag: Option<Path<String>>) -> Result<PostListTemplate, StatusCode> {
|
||||||
// I will forget what happens here in a week. But essentially it's pattern matching and shadowing
|
// I will forget what happens here in a week. But essentially it's pattern matching and shadowing
|
||||||
let tag = tag.map(|Path(tag)| tag);
|
let tag = tag.map(|Path(tag)| tag);
|
||||||
|
|
||||||
let path = "../_posts/blog/";
|
let site_footer = tokio::spawn(render_site_footer());
|
||||||
let mut dir = read_dir(path)
|
let mut post_list = get_post_list::<PostMetadata>().await?;
|
||||||
.await
|
post_list.sort_by_key(|post| post.metadata.date);
|
||||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
post_list.reverse();
|
||||||
let mut posts: Vec<ParseResult<PostMetadata>> = Vec::new();
|
|
||||||
|
|
||||||
while let Some(file) = dir
|
let posts = match &tag {
|
||||||
.next_entry()
|
Some(tag) => post_list
|
||||||
.await
|
|
||||||
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
|
||||||
{
|
|
||||||
let file_path = file.path();
|
|
||||||
let file_path_str = file_path.to_str().unwrap();
|
|
||||||
info!(":{}", file_path_str);
|
|
||||||
let post = parse_post::<PostMetadata>(file_path_str).await?;
|
|
||||||
posts.push(post);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut posts = match &tag {
|
|
||||||
Some(tag) => posts
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|post| {
|
.filter(|post| {
|
||||||
post.metadata
|
post.metadata
|
||||||
@ -49,23 +39,18 @@ pub async fn render_post_list(tag: Option<Path<String>>) -> Result<PostListTempl
|
|||||||
.contains(&tag.to_lowercase())
|
.contains(&tag.to_lowercase())
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
None => posts,
|
None => post_list,
|
||||||
};
|
};
|
||||||
|
|
||||||
if std::env::var("TARGET")
|
let site_footer = site_footer
|
||||||
.unwrap_or_else(|_| "DEV".to_owned())
|
.await
|
||||||
.eq("PROD")
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||||
{
|
|
||||||
posts = posts
|
|
||||||
.into_iter()
|
|
||||||
.filter(|post| !post.slug.starts_with("dev"))
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(PostListTemplate {
|
Ok(PostListTemplate {
|
||||||
title: "Posts".to_owned(),
|
title: "Posts".to_owned(),
|
||||||
posts,
|
posts,
|
||||||
tag,
|
tag,
|
||||||
|
site_footer,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
39
axum_server/src/post_list.rs
Normal file
39
axum_server/src/post_list.rs
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
use axum::http::StatusCode;
|
||||||
|
use serde::de::DeserializeOwned;
|
||||||
|
use tokio::fs::read_dir;
|
||||||
|
use tracing::info;
|
||||||
|
|
||||||
|
use crate::post_parser::{parse_post, ParseResult};
|
||||||
|
|
||||||
|
pub async fn get_post_list<'de, Metadata: DeserializeOwned>(
|
||||||
|
) -> Result<Vec<ParseResult<Metadata>>, StatusCode> {
|
||||||
|
let path = "../_posts/blog/";
|
||||||
|
let mut dir = read_dir(path)
|
||||||
|
.await
|
||||||
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
|
||||||
|
let mut posts: Vec<ParseResult<Metadata>> = Vec::new();
|
||||||
|
|
||||||
|
while let Some(file) = dir
|
||||||
|
.next_entry()
|
||||||
|
.await
|
||||||
|
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?
|
||||||
|
{
|
||||||
|
let file_path = file.path();
|
||||||
|
let file_path_str = file_path.to_str().unwrap();
|
||||||
|
info!(":{}", file_path_str);
|
||||||
|
let post = parse_post::<Metadata>(file_path_str).await?;
|
||||||
|
posts.push(post);
|
||||||
|
}
|
||||||
|
|
||||||
|
if std::env::var("TARGET")
|
||||||
|
.unwrap_or_else(|_| "DEV".to_owned())
|
||||||
|
.eq("PROD")
|
||||||
|
{
|
||||||
|
posts = posts
|
||||||
|
.into_iter()
|
||||||
|
.filter(|post| !post.slug.starts_with("dev"))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(posts);
|
||||||
|
}
|
@ -31,7 +31,8 @@
|
|||||||
<link rel="icon" type="image/png" href="/m-logo-192.png" />
|
<link rel="icon" type="image/png" href="/m-logo-192.png" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
{% block content %} Placeholder {% endblock %}
|
{% block content %} Placeholder {% endblock %} {# footer, should be not
|
||||||
{# footer, should be not dependant on the each individual handler but it should have it's own handler #}
|
dependant on the each individual handler but it should have it's own handler
|
||||||
|
#} {{ site_footer|safe }}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
7
axum_server/templates/site_footer.html
Normal file
7
axum_server/templates/site_footer.html
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<footer>
|
||||||
|
{% for post in latest_posts %}
|
||||||
|
|
||||||
|
<article>{{post.metadata.title}}</article>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
</footer>
|
Loading…
Reference in New Issue
Block a user