Refactor posts parsing and begin add tags
This commit is contained in:
parent
9cd7029ede
commit
51f34eb290
13 changed files with 146 additions and 282 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -1797,6 +1797,7 @@ version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
"anyhow",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"console_log",
|
"console_log",
|
||||||
|
@ -1813,6 +1814,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
"simple_logger",
|
"simple_logger",
|
||||||
|
"thiserror",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
leptos = { version = "0.5", features = ["nightly"] }
|
leptos = { version = "0.5", features = ["nightly"] }
|
||||||
leptos_actix = { version = "0.5", optional = true }
|
|
||||||
leptos_meta = { version = "0.5", features = ["nightly"] }
|
leptos_meta = { version = "0.5", features = ["nightly"] }
|
||||||
leptos_router = { version = "0.5", features = ["nightly"] }
|
leptos_router = { version = "0.5", features = ["nightly"] }
|
||||||
gloo-net = { version = "0.4", features = ["http"] }
|
gloo-net = { version = "0.4", features = ["http"] }
|
||||||
|
@ -36,11 +35,14 @@ console_error_panic_hook = { version = "0.1", optional = true }
|
||||||
# dependecies for server (enable when ssr set)
|
# dependecies for server (enable when ssr set)
|
||||||
actix-files = { version = "0.6", optional = true }
|
actix-files = { version = "0.6", optional = true }
|
||||||
actix-web = { version = "4", features = ["macros"], optional = true }
|
actix-web = { version = "4", features = ["macros"], optional = true }
|
||||||
|
leptos_actix = { version = "0.5", optional = true }
|
||||||
futures = { version = "0.3", optional = true }
|
futures = { version = "0.3", optional = true }
|
||||||
simple_logger = { version = "4.2", optional = true }
|
simple_logger = { version = "4.2", optional = true }
|
||||||
pulldown-cmark = { version = "0.9", optional = true } # Markdown parser
|
pulldown-cmark = { version = "0.9", optional = true } # Markdown parser
|
||||||
gray_matter = { version = "0.2", optional = true } # frontmatter parser
|
gray_matter = { version = "0.2", optional = true } # frontmatter parser
|
||||||
serde_yaml = { version = "0.9", optional = true }
|
serde_yaml = { version = "0.9", optional = true }
|
||||||
|
anyhow = { version = "1.0", optional = true }
|
||||||
|
thiserror = { version = "1.0", optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["csr"]
|
default = ["csr"]
|
||||||
|
@ -71,7 +73,9 @@ ssr = [
|
||||||
"dep:simple_logger",
|
"dep:simple_logger",
|
||||||
"dep:pulldown-cmark",
|
"dep:pulldown-cmark",
|
||||||
"dep:gray_matter",
|
"dep:gray_matter",
|
||||||
"dep:serde_yaml"
|
"dep:serde_yaml",
|
||||||
|
"dep:anyhow",
|
||||||
|
"dep:thiserror"
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata.cargo-all-features]
|
[package.metadata.cargo-all-features]
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
---
|
---
|
||||||
image_path: "https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Test-Logo.svg/783px-Test-Logo.svg.png?20150906031702"
|
image_path: "https://upload.wikimedia.org/wikipedia/commons/thumb/1/11/Test-Logo.svg/783px-Test-Logo.svg.png?20150906031702"
|
||||||
|
slug: test_layout
|
||||||
title: Testing layout
|
title: Testing layout
|
||||||
date: 2023-11-26
|
date: 2023-11-26
|
||||||
description: Testing the layout of the site.
|
description: Testing the layout of the site.
|
||||||
project_link: none
|
project_link: none
|
||||||
draft: true
|
draft: true
|
||||||
|
tags:
|
||||||
|
- test
|
||||||
---
|
---
|
||||||
|
|
||||||
# Heading 1
|
# Heading 1
|
||||||
|
|
|
@ -1,168 +0,0 @@
|
||||||
tags:
|
|
||||||
rust:
|
|
||||||
name: Rust
|
|
||||||
url: https://www.rust-lang.org/
|
|
||||||
groups:
|
|
||||||
- lang
|
|
||||||
- backend
|
|
||||||
- system
|
|
||||||
- web
|
|
||||||
java:
|
|
||||||
name: Java
|
|
||||||
url: https://www.java.com/fr/
|
|
||||||
groups:
|
|
||||||
- lang
|
|
||||||
- backend
|
|
||||||
- desktop
|
|
||||||
cpp:
|
|
||||||
name: C++
|
|
||||||
url: https://isocpp.org/
|
|
||||||
groups:
|
|
||||||
- lang
|
|
||||||
- backend
|
|
||||||
- system
|
|
||||||
- game
|
|
||||||
react:
|
|
||||||
name: React
|
|
||||||
url: https://fr.legacy.reactjs.org/
|
|
||||||
groups:
|
|
||||||
- frontend
|
|
||||||
symfony:
|
|
||||||
name: Symfony
|
|
||||||
url: https://symfony.com/
|
|
||||||
groups:
|
|
||||||
- backend
|
|
||||||
- web
|
|
||||||
flutter:
|
|
||||||
name: Flutter
|
|
||||||
url: https://flutter.dev/
|
|
||||||
groups:
|
|
||||||
- mobile
|
|
||||||
- desktop
|
|
||||||
ruby_on_rails:
|
|
||||||
name: Ruby on rails
|
|
||||||
url: https://rubyonrails.org/
|
|
||||||
groups:
|
|
||||||
- backend
|
|
||||||
- frontend
|
|
||||||
- web
|
|
||||||
hotwired:
|
|
||||||
name: Hotwired
|
|
||||||
url: https://hotwired.dev/
|
|
||||||
groups:
|
|
||||||
- frontend
|
|
||||||
- web
|
|
||||||
docker:
|
|
||||||
name: Docker
|
|
||||||
url: https://www.docker.com/
|
|
||||||
groups:
|
|
||||||
- devops
|
|
||||||
steam_api:
|
|
||||||
name: Steam API
|
|
||||||
url: https://partner.steamgames.com/doc/sdk/api/example
|
|
||||||
groups:
|
|
||||||
- game
|
|
||||||
gitlab_ci:
|
|
||||||
name: Gitlab CI
|
|
||||||
url: https://docs.gitlab.com/ee/ci/
|
|
||||||
groups:
|
|
||||||
- devops
|
|
||||||
unity:
|
|
||||||
name: Unity 3D
|
|
||||||
url: https://unity.com/fr
|
|
||||||
groups:
|
|
||||||
- game
|
|
||||||
wordpress:
|
|
||||||
name: Wordpress
|
|
||||||
url: https://wordpress.com/fr/
|
|
||||||
groups:
|
|
||||||
- web
|
|
||||||
cordova:
|
|
||||||
name: Cordova
|
|
||||||
url: https://cordova.apache.org/
|
|
||||||
groups:
|
|
||||||
- mobile
|
|
||||||
electron:
|
|
||||||
name: Electron
|
|
||||||
url: https://www.electronjs.org/
|
|
||||||
groups:
|
|
||||||
- desktop
|
|
||||||
lwjgl:
|
|
||||||
name: LWJGL
|
|
||||||
url: https://www.lwjgl.org/
|
|
||||||
groups:
|
|
||||||
- desktop
|
|
||||||
- game
|
|
||||||
opengl:
|
|
||||||
name: OpenGL
|
|
||||||
url: https://www.opengl.org/
|
|
||||||
groups:
|
|
||||||
- desktop
|
|
||||||
- game
|
|
||||||
vulkan:
|
|
||||||
name: Vulkan
|
|
||||||
url: https://www.vulkan.org/
|
|
||||||
groups:
|
|
||||||
- desktop
|
|
||||||
- game
|
|
||||||
midi:
|
|
||||||
name: MIDI
|
|
||||||
url: https://fr.wikipedia.org/wiki/Musical_Instrument_Digital_Interface
|
|
||||||
groups:
|
|
||||||
- desktop
|
|
||||||
- web
|
|
||||||
- music
|
|
||||||
- game
|
|
||||||
requirejs:
|
|
||||||
name: RequireJS
|
|
||||||
url: https://requirejs.org/
|
|
||||||
groups:
|
|
||||||
- frontend
|
|
||||||
- web
|
|
||||||
webpack:
|
|
||||||
name: Webpack
|
|
||||||
url: https://webpack.js.org/
|
|
||||||
groups:
|
|
||||||
- frontend
|
|
||||||
- web
|
|
||||||
vite:
|
|
||||||
name: Vite
|
|
||||||
url: https://vitejs.dev/
|
|
||||||
groups:
|
|
||||||
- frontend
|
|
||||||
- web
|
|
||||||
maven:
|
|
||||||
name: Maven
|
|
||||||
url: https://maven.apache.org/
|
|
||||||
groups:
|
|
||||||
- desktop
|
|
||||||
gradle:
|
|
||||||
name: Gradle
|
|
||||||
url: https://gradle.org/
|
|
||||||
groups:
|
|
||||||
- desktop
|
|
||||||
- mobile
|
|
||||||
babylonjs:
|
|
||||||
name: BabylonJS
|
|
||||||
url: https://www.babylonjs.com/
|
|
||||||
groups:
|
|
||||||
- game
|
|
||||||
rocket_rs:
|
|
||||||
name: Rocket.rs
|
|
||||||
url: https://rocket.rs/
|
|
||||||
groups:
|
|
||||||
- backend
|
|
||||||
- web
|
|
||||||
actix_web:
|
|
||||||
name: Actix Web
|
|
||||||
url: https://actix.rs/
|
|
||||||
groups:
|
|
||||||
- backend
|
|
||||||
- web
|
|
||||||
leptos:
|
|
||||||
name: Leptos
|
|
||||||
url: https://leptos.dev/
|
|
||||||
groups:
|
|
||||||
- web
|
|
||||||
- backend
|
|
||||||
- frontend
|
|
|
@ -62,7 +62,7 @@ const GRADLE_TAG : Lang<'static> = Lang { lang: "Gradle", url: "https://gradle.o
|
||||||
const BABYLONJS_TAG : Lang<'static> = Lang { lang: "BabylonJS", url: "https://www.babylonjs.com/" };
|
const BABYLONJS_TAG : Lang<'static> = Lang { lang: "BabylonJS", url: "https://www.babylonjs.com/" };
|
||||||
const ROCKET_RS_TAG : Lang<'static> = Lang { lang: "Rocket", url: "https://rocket.rs/" };
|
const ROCKET_RS_TAG : Lang<'static> = Lang { lang: "Rocket", url: "https://rocket.rs/" };
|
||||||
const ACTIX_WEB_TAG : Lang<'static> = Lang { lang: "Actix Web", url: "https://actix.rs/" };
|
const ACTIX_WEB_TAG : Lang<'static> = Lang { lang: "Actix Web", url: "https://actix.rs/" };
|
||||||
const LEPTOS_TAG : Lang<'static> = Lang { lang: "Leptos", url: "https://leptos.dev/" };
|
const _LEPTOS_TAG : Lang<'static> = Lang { lang: "Leptos", url: "https://leptos.dev/" };
|
||||||
|
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
|
|
@ -1,7 +1,12 @@
|
||||||
pub mod components;
|
pub mod components;
|
||||||
|
|
||||||
pub mod models;
|
pub mod models;
|
||||||
|
|
||||||
mod pages;
|
mod pages;
|
||||||
|
|
||||||
|
#[cfg(feature = "ssr")]
|
||||||
|
mod utils;
|
||||||
|
|
||||||
use leptos::*;
|
use leptos::*;
|
||||||
use leptos_meta::*;
|
use leptos_meta::*;
|
||||||
use leptos_router::*;
|
use leptos_router::*;
|
||||||
|
|
|
@ -2,6 +2,46 @@ mod post;
|
||||||
|
|
||||||
pub use post::Post;
|
pub use post::Post;
|
||||||
|
|
||||||
mod tag;
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(feature = "ssr")] {
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub use tag::{Tags, Tag};
|
pub struct Data {
|
||||||
|
pub posts: Vec<Arc<Post>>,
|
||||||
|
pub posts_by_slug: HashMap<String, Arc<Post>>,
|
||||||
|
pub posts_by_tag: HashMap<String, Vec<Arc<Post>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Data {
|
||||||
|
#[allow(dead_code)] // Use in main.rs
|
||||||
|
pub fn new() -> anyhow::Result<Self> {
|
||||||
|
let posts = crate::app::utils::data_src::get_all::<Post>("posts")?
|
||||||
|
.into_iter()
|
||||||
|
.map(|post| Arc::new(post))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let mut posts_by_slug : HashMap<String, Arc<Post>> = HashMap::new();
|
||||||
|
let mut posts_by_tag : HashMap<String, Vec<Arc<Post>>> = HashMap::new();
|
||||||
|
|
||||||
|
for post in &posts {
|
||||||
|
posts_by_slug.insert(post.metadata.slug.clone(), post.clone());
|
||||||
|
|
||||||
|
for tag in &post.metadata.tags {
|
||||||
|
let tag = tag.to_lowercase();
|
||||||
|
let posts = posts_by_tag.entry(tag).or_insert(Vec::new());
|
||||||
|
posts.push(post.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
posts,
|
||||||
|
posts_by_slug,
|
||||||
|
posts_by_tag,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,34 +1,43 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct PostMetadata {
|
pub struct PostMetadata {
|
||||||
|
pub slug: String,
|
||||||
pub image_path: String,
|
pub image_path: String,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
pub date: String,
|
pub date: String,
|
||||||
pub description: String,
|
pub description: String,
|
||||||
pub project_link: String,
|
pub project_link: String,
|
||||||
pub draft: bool
|
pub draft: bool,
|
||||||
|
pub tags: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||||
pub struct Post {
|
pub struct Post {
|
||||||
pub slug : String,
|
|
||||||
pub metadata: PostMetadata,
|
pub metadata: PostMetadata,
|
||||||
pub content: String,
|
pub content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(feature = "ssr")] {
|
if #[cfg(feature = "ssr")] {
|
||||||
impl Post {
|
#[derive(Debug, thiserror::Error)]
|
||||||
fn from_content(content: String) -> Option<Self> {
|
pub enum PostDeserializationError {
|
||||||
|
#[error("Invalid front matter")]
|
||||||
|
InvalidFrontMatter,
|
||||||
|
#[error("Invalid markdown")]
|
||||||
|
InvalidMarkdown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<String> for Post {
|
||||||
|
type Error = PostDeserializationError;
|
||||||
|
|
||||||
|
fn try_from(content: String) -> Result<Self, Self::Error> {
|
||||||
use gray_matter::{Matter, engine::YAML};
|
use gray_matter::{Matter, engine::YAML};
|
||||||
let matter = Matter::<YAML>::new();
|
let matter = Matter::<YAML>::new();
|
||||||
|
|
||||||
let post_data = matter
|
let post_data = matter
|
||||||
.parse_with_struct::<PostMetadata>(&content)?;
|
.parse_with_struct::<PostMetadata>(&content)
|
||||||
|
.ok_or_else(|| PostDeserializationError::InvalidFrontMatter)?;
|
||||||
|
|
||||||
let metadata = post_data.data;
|
|
||||||
let slug = format!("{}_{}", metadata.date, metadata.title.to_lowercase().replace(' ', "_"));
|
|
||||||
let content = post_data.content;
|
let content = post_data.content;
|
||||||
|
|
||||||
use pulldown_cmark::{Parser, Options, html};
|
use pulldown_cmark::{Parser, Options, html};
|
||||||
|
@ -36,49 +45,14 @@ cfg_if::cfg_if! {
|
||||||
let mut html_output = String::new();
|
let mut html_output = String::new();
|
||||||
html::push_html(&mut html_output, parser);
|
html::push_html(&mut html_output, parser);
|
||||||
|
|
||||||
Some(Self {
|
if html_output.is_empty() {
|
||||||
slug,
|
return Err(PostDeserializationError::InvalidMarkdown);
|
||||||
metadata,
|
|
||||||
content: html_output,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_path(path: &std::path::Path) -> Option<Self> {
|
|
||||||
let content = std::fs::read_to_string(path);
|
|
||||||
let content = match content {
|
|
||||||
Ok(content) => content,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("{:?}", e);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::from_content(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_all(folder: &str) -> Result<Vec<Self>, String> {
|
|
||||||
use std::{path::Path, fs::read_dir};
|
|
||||||
|
|
||||||
let mut posts: Vec<Self> = Vec::new();
|
|
||||||
|
|
||||||
let folder_path = Path::new("data_src").join(folder);
|
|
||||||
let paths = read_dir(folder_path).map_err(|e| e.to_string())?;
|
|
||||||
|
|
||||||
for path_result in paths {
|
|
||||||
let path = match path_result {
|
|
||||||
Ok(path) => path.path(),
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("{:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(post) = Self::from_path(&path) {
|
|
||||||
posts.push(post);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(posts)
|
Ok(Self {
|
||||||
|
metadata: post_data.data,
|
||||||
|
content: html_output,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
use std::collections::HashMap;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
||||||
pub struct Tag {
|
|
||||||
pub name : String,
|
|
||||||
pub url : String,
|
|
||||||
pub groups : Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Serialize, Deserialize, Debug)]
|
|
||||||
pub struct Tags {
|
|
||||||
pub tags : HashMap<String, Tag>,
|
|
||||||
}
|
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(feature = "ssr")] {
|
|
||||||
impl Tags {
|
|
||||||
fn from_content(content: String) -> Option<Self> {
|
|
||||||
let tags = serde_yaml::from_str(&content).ok()?;
|
|
||||||
|
|
||||||
Some(Self {
|
|
||||||
tags
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_path(path: &std::path::Path) -> Option<Self> {
|
|
||||||
let content = std::fs::read_to_string(path);
|
|
||||||
let content = match content {
|
|
||||||
Ok(content) => content,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("{:?}", e);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Self::from_content(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_default() -> Option<Self> {
|
|
||||||
let path = std::path::Path::new("data_src").join("tags.yml");
|
|
||||||
Self::from_path(&path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -10,23 +10,38 @@ use crate::app::{
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
pub async fn get_posts() -> Result<Vec<Post>, ServerFnError> {
|
pub async fn get_posts() -> Result<Vec<Post>, ServerFnError> {
|
||||||
let posts = Post::get_all("posts")
|
leptos_actix::extract(
|
||||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
|
|data: actix_web::web::Data<crate::app::models::Data>| async move {
|
||||||
|
let data = data.into_inner();
|
||||||
Ok(posts)
|
data.posts
|
||||||
|
.iter()
|
||||||
|
.map(|post| {
|
||||||
|
Post {
|
||||||
|
metadata: post.metadata.clone(),
|
||||||
|
content: post.content.clone(),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>, >()
|
||||||
|
},
|
||||||
|
).await
|
||||||
}
|
}
|
||||||
|
|
||||||
#[server]
|
#[server]
|
||||||
pub async fn get_post(
|
pub async fn get_post(
|
||||||
slug: String
|
slug: String
|
||||||
) -> Result<Post, ServerFnError> {
|
) -> Result<Post, ServerFnError> {
|
||||||
let posts = Post::get_all("posts")
|
leptos_actix::extract(
|
||||||
.map_err(|e| ServerFnError::ServerError(e.to_string()))?;
|
|data: actix_web::web::Data<crate::app::models::Data>| async move {
|
||||||
|
let data = data.into_inner();
|
||||||
let post = posts.into_iter().find(|post| post.slug == slug)
|
data.posts_by_slug.get(&slug)
|
||||||
.ok_or(ServerFnError::ServerError("Post not found".to_string()))?;
|
.and_then(|post| Some(Post {
|
||||||
|
metadata: post.metadata.clone(),
|
||||||
Ok(post)
|
content: post.content.clone(),
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.and_then(|post| post.ok_or_else(|| ServerFnError::ServerError("Post not found".to_string())))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
|
@ -37,7 +52,7 @@ pub fn PostList() -> impl IntoView {
|
||||||
posts.and_then(|posts| {
|
posts.and_then(|posts| {
|
||||||
posts.iter()
|
posts.iter()
|
||||||
.map(|post| view! {
|
.map(|post| view! {
|
||||||
<a href=format!("posts/{}", post.slug.clone())>
|
<a href=format!("posts/{}", post.metadata.slug.clone())>
|
||||||
<img src={post.metadata.image_path.clone()} alt=format!("Image {}", post.metadata.title)/>
|
<img src={post.metadata.image_path.clone()} alt=format!("Image {}", post.metadata.title)/>
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
33
src/app/utils/data_src.rs
Normal file
33
src/app/utils/data_src.rs
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#[allow(dead_code)] // Use in main.rs
|
||||||
|
pub fn get_all<T>(folder: &str) -> anyhow::Result<Vec<T>>
|
||||||
|
where
|
||||||
|
T: TryFrom<String>,
|
||||||
|
{
|
||||||
|
use std::{path::Path, fs::read_dir};
|
||||||
|
|
||||||
|
let mut datas: Vec<T> = Vec::new();
|
||||||
|
|
||||||
|
let folder_path = Path::new("data_src").join(folder);
|
||||||
|
let paths = read_dir(folder_path)?;
|
||||||
|
|
||||||
|
for path_result in paths {
|
||||||
|
let data : Result<T, String> = path_result
|
||||||
|
.and_then(|path| std::fs::File::open(path.path()))
|
||||||
|
.map_err(|e| format!("Open file Error: {:?}", e))
|
||||||
|
.and_then(|mut file| {
|
||||||
|
use std::io::Read;
|
||||||
|
let mut content = String::new();
|
||||||
|
file.read_to_string(&mut content)
|
||||||
|
.map_err(|e| format!("Read file Error: {:?}", e))?;
|
||||||
|
T::try_from(content)
|
||||||
|
.map_err(|_e| "Parse file Error".to_string())
|
||||||
|
});
|
||||||
|
|
||||||
|
match data {
|
||||||
|
Ok(data) => datas.push(data),
|
||||||
|
Err(e) => eprintln!("{:?}", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(datas)
|
||||||
|
}
|
1
src/app/utils/mod.rs
Normal file
1
src/app/utils/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub(crate) mod data_src;
|
|
@ -24,6 +24,7 @@ cfg_if! {
|
||||||
let site_root = &leptos_options.site_root;
|
let site_root = &leptos_options.site_root;
|
||||||
let routes = &routes;
|
let routes = &routes;
|
||||||
App::new()
|
App::new()
|
||||||
|
.app_data(web::Data::new(app::models::Data::new().unwrap())) // Must panic if data can't be loaded
|
||||||
.route("/api/{tail:.*}", leptos_actix::handle_server_fns())
|
.route("/api/{tail:.*}", leptos_actix::handle_server_fns())
|
||||||
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), || view! { <App/> })
|
.leptos_routes(leptos_options.to_owned(), routes.to_owned(), || view! { <App/> })
|
||||||
.service(Files::new("/", site_root))
|
.service(Files::new("/", site_root))
|
||||||
|
|
Loading…
Reference in a new issue