Projects moved and finished to showcase on index
This commit is contained in:
@ -1,4 +1,8 @@
|
||||
use crate::{pages::project::ProjectMetadata, post_list::get_post_list, post_parser::ParseResult};
|
||||
use crate::{
|
||||
pages::project::ProjectMetadata,
|
||||
post_list::get_post_list,
|
||||
post_parser::{parse_html, ParseResult},
|
||||
};
|
||||
use axum::http::StatusCode;
|
||||
|
||||
pub async fn get_featured_projects() -> Result<Vec<ParseResult<ProjectMetadata>>, StatusCode> {
|
||||
@ -7,6 +11,10 @@ pub async fn get_featured_projects() -> Result<Vec<ParseResult<ProjectMetadata>>
|
||||
let featured_projects = project_list
|
||||
.into_iter()
|
||||
.filter(|post| post.metadata.featured)
|
||||
.map(|mut post| {
|
||||
post.metadata.description = parse_html(&post.metadata.description);
|
||||
post
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(featured_projects)
|
||||
|
@ -9,4 +9,15 @@ pub struct ProjectMetadata {
|
||||
pub cover_image: Option<String>,
|
||||
pub tags: Vec<String>,
|
||||
pub featured: bool,
|
||||
pub link: Option<String>,
|
||||
}
|
||||
|
||||
pub fn translate_classification(classification: &str) -> &str {
|
||||
match classification {
|
||||
"webapp" => "Web application",
|
||||
"website" => "Web site",
|
||||
"presentation" => "Presentation",
|
||||
"videogame" => "Video game",
|
||||
any => any,
|
||||
}
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ pub async fn parse_post<'de, Metadata: DeserializeOwned>(
|
||||
.parse_with_struct::<Metadata>(&file_contents)
|
||||
.ok_or_else(|| {
|
||||
tracing::error!("Failed to parse metadata");
|
||||
return StatusCode::INTERNAL_SERVER_ERROR;
|
||||
StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
|
||||
let body = parse_html(&metadata.content);
|
||||
@ -52,14 +52,14 @@ pub async fn parse_post<'de, Metadata: DeserializeOwned>(
|
||||
.ok_or(StatusCode::INTERNAL_SERVER_ERROR)?
|
||||
.to_owned();
|
||||
|
||||
return Ok(ParseResult {
|
||||
Ok(ParseResult {
|
||||
body,
|
||||
metadata: metadata.data,
|
||||
slug: filename,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_html(markdown: &str) -> String {
|
||||
pub fn parse_html(markdown: &str) -> String {
|
||||
let mut options = Options::empty();
|
||||
options.insert(Options::ENABLE_TABLES);
|
||||
options.insert(Options::ENABLE_FOOTNOTES);
|
||||
@ -68,45 +68,45 @@ fn parse_html(markdown: &str) -> String {
|
||||
options.insert(Options::ENABLE_SMART_PUNCTUATION);
|
||||
options.insert(Options::ENABLE_HEADING_ATTRIBUTES);
|
||||
|
||||
let parser = Parser::new_ext(&markdown, options).map(|event| match event {
|
||||
Event::Start(ref tag) => match tag {
|
||||
// Parsing images considers `alt` attribute as inner `Text` event
|
||||
// Therefore the `[alt]` is rendered in html as subtitle
|
||||
// and the `[](url "title")` `title` is rendered as `alt` attribute
|
||||
Tag::Image {
|
||||
link_type,
|
||||
dest_url,
|
||||
title,
|
||||
id,
|
||||
} => {
|
||||
println!(
|
||||
"Image link_type: {:?} url: {} title: {} id: {}",
|
||||
link_type, dest_url, title, id
|
||||
);
|
||||
// TODO src set
|
||||
Event::Html(
|
||||
format!(
|
||||
r#"<figure>
|
||||
let parser = Parser::new_ext(markdown, options).map(|event| match event {
|
||||
/*
|
||||
Parsing images considers `alt` attribute as inner `Text` event
|
||||
Therefore the `[alt]` is rendered in html as subtitle
|
||||
and the `[](url "title")` `title` is rendered as `alt` attribute
|
||||
*/
|
||||
Event::Start(Tag::Image {
|
||||
link_type,
|
||||
dest_url,
|
||||
title,
|
||||
id,
|
||||
}) => {
|
||||
println!(
|
||||
"Image link_type: {:?} url: {} title: {} id: {}",
|
||||
link_type, dest_url, title, id
|
||||
);
|
||||
// TODO src set
|
||||
Event::Html(
|
||||
format!(
|
||||
r#"<figure>
|
||||
<img
|
||||
alt="{alt}"
|
||||
src="{src}"
|
||||
/>
|
||||
<figcaption>
|
||||
"#,
|
||||
alt = title,
|
||||
src = dest_url,
|
||||
)
|
||||
.into(),
|
||||
alt = title,
|
||||
src = dest_url,
|
||||
)
|
||||
}
|
||||
_ => event,
|
||||
},
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
Event::Start(_) => event,
|
||||
Event::End(TagEnd::Image) => Event::Html("</figcaption></figure>".into()),
|
||||
_ => event,
|
||||
});
|
||||
|
||||
// Write to String buffer.
|
||||
// Write to String buffer
|
||||
let mut html = String::new();
|
||||
pulldown_cmark::html::push_html(&mut html, parser);
|
||||
return html;
|
||||
html
|
||||
}
|
||||
|
@ -1,42 +0,0 @@
|
||||
use std::path::Path;
|
||||
|
||||
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>(
|
||||
path: &Path,
|
||||
) -> 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);
|
||||
}
|
@ -624,6 +624,16 @@ video {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.mx-1 {
|
||||
margin-left: 0.25rem;
|
||||
margin-right: 0.25rem;
|
||||
}
|
||||
|
||||
.my-1 {
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.mb-1 {
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
@ -644,6 +654,10 @@ video {
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.mt-1 {
|
||||
margin-top: 0.25rem;
|
||||
}
|
||||
|
||||
.block {
|
||||
display: block;
|
||||
}
|
||||
@ -892,6 +906,11 @@ video {
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
|
||||
.text-xl {
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.font-bold {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
@ -1,34 +1,45 @@
|
||||
<article>
|
||||
<article class="border rounded-md bg-white m-4 p-4">
|
||||
<header class="px-4 mb-3">
|
||||
<h2 class="text-3xl font-semibold text-blue-900">
|
||||
{{project.metadata.title}}
|
||||
<h2 class="text-xl font-semibold text-blue-900">
|
||||
{% match project.metadata.link %}
|
||||
{% when Some with (href) %}
|
||||
<a href="{{href}}" class="text-blue-900 no-underline">
|
||||
{{project.metadata.title}}
|
||||
</a>
|
||||
{% when None %}
|
||||
{{project.metadata.title}}
|
||||
{% endmatch %}
|
||||
</h2>
|
||||
<p class="px-5 text-gray-800">
|
||||
Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make</p>
|
||||
<p class="text-gray-800 text-lg my-2">
|
||||
{{project.metadata.description|safe}}
|
||||
</p>
|
||||
</header>
|
||||
<!-- <hr class="border-blue-950 my-5"> -->
|
||||
|
||||
{% match project.metadata.cover_image %}
|
||||
{% when Some with (source) %}
|
||||
<figure>
|
||||
<!-- <img src="{{source}}" /> -->
|
||||
<figure class="my-2">
|
||||
{% match project.metadata.link %}
|
||||
{% when Some with (href) %}
|
||||
<a href="{{href}}">
|
||||
<img src="{{source}}" />
|
||||
</a>
|
||||
{% when None %}
|
||||
<img src="{{source}}" />
|
||||
{% endmatch %}
|
||||
<!-- TODO <figure> -->
|
||||
<svg aria-hidden="true" class="h-12 w-12 fill-blue-950">
|
||||
<use xlink:href="/svg/icons-sprite.svg#mail" />
|
||||
</svg>
|
||||
|
||||
</figure>
|
||||
{% when None %}
|
||||
{% endmatch %}
|
||||
|
||||
|
||||
<footer class="text-sm">
|
||||
<h3 class="text-3xl font-semibold text-blue-900">
|
||||
TODO classification
|
||||
<footer class="text-sm px-4">
|
||||
<h3 class="text-xl font-semibold text-blue-900 my-2">
|
||||
{{crate::pages::project::translate_classification(project.metadata.classification)}}
|
||||
</h3>
|
||||
<ul class="inline-block">
|
||||
{% for tag in project.metadata.tags %}
|
||||
<li class="inline-block">
|
||||
<li class="inline-block text-blue-700 italic">
|
||||
{{tag}}
|
||||
</li>
|
||||
{% endfor %}
|
||||
|
Reference in New Issue
Block a user