Switch over to WebAssembly, Rust and Yew #35
@ -120,26 +120,6 @@ async fn copy_resources(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn write_data(out_dir: impl AsRef<Path>) -> std::io::Result<()> {
|
||||
log::info!("Writing tags.json ...");
|
||||
let tags = site::model::source::get_tags();
|
||||
tokio::fs::write(
|
||||
out_dir.as_ref().join("tags.json"),
|
||||
serde_json::to_string(&tags)?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
log::info!("Writing posts.json ...");
|
||||
let posts = site::model::source::get_posts();
|
||||
tokio::fs::write(
|
||||
out_dir.as_ref().join("posts.json"),
|
||||
serde_json::to_string(&posts)?,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
env_logger::init();
|
||||
@ -176,8 +156,5 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
tokio::fs::write(path, html).await?;
|
||||
}
|
||||
|
||||
log::info!("Writing data");
|
||||
write_data(&out_dir).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -1,18 +1,87 @@
|
||||
#[cfg(feature = "hydration")]
|
||||
mod hydrating;
|
||||
|
||||
#[cfg(feature = "hydration")]
|
||||
pub use hydrating::*;
|
||||
|
||||
#[cfg(not(feature = "hydration"))]
|
||||
mod filesystem;
|
||||
|
||||
use include_dir::{include_dir, Dir, File};
|
||||
use std::collections::HashMap;
|
||||
use yew::{function_component, html, Children, ContextProvider, Html, Properties};
|
||||
|
||||
#[cfg(not(feature = "hydration"))]
|
||||
pub use filesystem::*;
|
||||
|
||||
use super::{PostInfo, Tag};
|
||||
use super::{frontmatter::parse_front_matter, PostInfo, Tag};
|
||||
|
||||
pub type TagsContext = HashMap<String, Tag>;
|
||||
pub type PostsContext = Vec<PostInfo>;
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ProvideTagsProps {
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[function_component(ProvideTags)]
|
||||
pub fn provide_tags(props: &ProvideTagsProps) -> Html {
|
||||
let tags = get_tags();
|
||||
|
||||
html! {
|
||||
<ContextProvider<TagsContext> context={tags}>
|
||||
{props.children.clone()}
|
||||
</ContextProvider<TagsContext>>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tags() -> TagsContext {
|
||||
let file = CONTENT_DIR.get_file("tags.yaml").expect("tags.yaml");
|
||||
match serde_yaml::from_slice::<Vec<Tag>>(file.contents()) {
|
||||
Ok(tags) => tags
|
||||
.into_iter()
|
||||
.map(|tag| (tag.slug.clone(), tag))
|
||||
.collect::<TagsContext>(),
|
||||
Err(err) => {
|
||||
panic!("Failed to parse tags.yaml: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ProvidePostsProps {
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[function_component(ProvidePosts)]
|
||||
pub fn provide_posts(props: &ProvidePostsProps) -> Html {
|
||||
let posts = get_posts();
|
||||
|
||||
html! {
|
||||
<ContextProvider<PostsContext> context={posts}>
|
||||
{props.children.clone()}
|
||||
</ContextProvider<PostsContext>>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_posts() -> PostsContext {
|
||||
let files = CONTENT_DIR.get_dir("posts").expect("posts dir").files();
|
||||
let mut posts = files.filter_map(load_post_info).collect::<Vec<_>>();
|
||||
posts.sort_by(|a, b| b.doc_info.published.cmp(&a.doc_info.published));
|
||||
posts
|
||||
}
|
||||
|
||||
fn load_post_info(file: &File) -> Option<PostInfo> {
|
||||
let (Some(front_matter), matter) = parse_front_matter(file.contents()) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let slug = file
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("filename")
|
||||
.to_str()
|
||||
.expect("valid file name")
|
||||
.to_string();
|
||||
|
||||
let reading_time = words_count::count(&matter.content).words / 200;
|
||||
|
||||
Some(PostInfo::from_front_matter(
|
||||
slug,
|
||||
Some(reading_time),
|
||||
matter.excerpt,
|
||||
front_matter,
|
||||
))
|
||||
}
|
||||
|
||||
static CONTENT_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/content");
|
||||
|
@ -1,87 +0,0 @@
|
||||
use include_dir::{include_dir, Dir, File};
|
||||
use yew::{function_component, html, Children, ContextProvider, Html, Properties};
|
||||
|
||||
use crate::model::{
|
||||
frontmatter::parse_front_matter,
|
||||
source::{PostsContext, TagsContext},
|
||||
PostInfo, Tag,
|
||||
};
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ProvideTagsProps {
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[function_component(ProvideTags)]
|
||||
pub fn provide_tags(props: &ProvideTagsProps) -> Html {
|
||||
let tags = get_tags();
|
||||
|
||||
html! {
|
||||
<ContextProvider<TagsContext> context={tags}>
|
||||
{props.children.clone()}
|
||||
</ContextProvider<TagsContext>>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_tags() -> TagsContext {
|
||||
let file = CONTENT_DIR.get_file("tags.yaml").expect("tags.yaml");
|
||||
match serde_yaml::from_slice::<Vec<Tag>>(file.contents()) {
|
||||
Ok(tags) => tags
|
||||
.into_iter()
|
||||
.map(|tag| (tag.slug.clone(), tag))
|
||||
.collect::<TagsContext>(),
|
||||
Err(err) => {
|
||||
panic!("Failed to parse tags.yaml: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ProvidePostsProps {
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[function_component(ProvidePosts)]
|
||||
pub fn provide_posts(props: &ProvidePostsProps) -> Html {
|
||||
let posts = get_posts();
|
||||
|
||||
html! {
|
||||
<ContextProvider<PostsContext> context={posts}>
|
||||
{props.children.clone()}
|
||||
</ContextProvider<PostsContext>>
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_posts() -> PostsContext {
|
||||
let files = CONTENT_DIR.get_dir("posts").expect("posts dir").files();
|
||||
let mut posts = files.filter_map(load_post_info).collect::<Vec<_>>();
|
||||
posts.sort_by(|a, b| b.doc_info.published.cmp(&a.doc_info.published));
|
||||
posts
|
||||
}
|
||||
|
||||
fn load_post_info(file: &File) -> Option<PostInfo> {
|
||||
let (Some(front_matter), matter) = parse_front_matter(file.contents()) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let slug = file
|
||||
.path()
|
||||
.file_stem()
|
||||
.expect("filename")
|
||||
.to_str()
|
||||
.expect("valid file name")
|
||||
.to_string();
|
||||
|
||||
let reading_time = words_count::count(&matter.content).words / 200;
|
||||
|
||||
Some(PostInfo::from_front_matter(
|
||||
slug,
|
||||
Some(reading_time),
|
||||
matter.excerpt,
|
||||
front_matter,
|
||||
))
|
||||
}
|
||||
|
||||
static CONTENT_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/content");
|
@ -1,94 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
use thiserror::Error;
|
||||
use yew::{function_component, html, use_state, Children, ContextProvider, Html, Properties};
|
||||
use yew_hooks::{use_async_with_options, UseAsyncOptions};
|
||||
|
||||
use crate::model::source::{PostsContext, TagsContext};
|
||||
|
||||
#[derive(Debug, Error, Clone, PartialEq)]
|
||||
pub enum ApiError {
|
||||
#[error("Unable to complete request")]
|
||||
RequestError,
|
||||
#[error("Unable to deserialize response")]
|
||||
DeserializeError,
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ProvideTagsProps {
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[function_component(ProvideTags)]
|
||||
pub fn provide_tags(props: &ProvideTagsProps) -> Html {
|
||||
let tags = use_state(HashMap::new);
|
||||
|
||||
{
|
||||
let tags = tags.clone();
|
||||
use_async_with_options::<_, (), ApiError>(
|
||||
async move {
|
||||
let res = reqwest::get("/tags.json")
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log::error!("Failed to get '/tags.json': {err:?}");
|
||||
ApiError::RequestError
|
||||
})?
|
||||
.json()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log::error!("Failed to deserialize '/tags.json': {err:?}");
|
||||
ApiError::DeserializeError
|
||||
})?;
|
||||
tags.set(res);
|
||||
Ok(())
|
||||
},
|
||||
UseAsyncOptions::enable_auto(),
|
||||
);
|
||||
}
|
||||
|
||||
html! {
|
||||
<ContextProvider<TagsContext> context={(*tags).clone()}>
|
||||
{props.children.clone()}
|
||||
</ContextProvider<TagsContext>>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Properties, PartialEq)]
|
||||
pub struct ProvidePostsProps {
|
||||
#[prop_or_default]
|
||||
pub children: Children,
|
||||
}
|
||||
|
||||
#[function_component(ProvidePosts)]
|
||||
pub fn provide_posts(props: &ProvidePostsProps) -> Html {
|
||||
let posts = use_state(Vec::new);
|
||||
|
||||
{
|
||||
let posts = posts.clone();
|
||||
use_async_with_options::<_, (), ApiError>(
|
||||
async move {
|
||||
let res = reqwest::get("/posts.json")
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log::error!("Failed to get '/posts.json': {err:?}");
|
||||
ApiError::RequestError
|
||||
})?
|
||||
.json()
|
||||
.await
|
||||
.map_err(|err| {
|
||||
log::error!("Failed to deserialize '/posts.json': {err:?}");
|
||||
ApiError::DeserializeError
|
||||
})?;
|
||||
posts.set(res);
|
||||
Ok(())
|
||||
},
|
||||
UseAsyncOptions::enable_auto(),
|
||||
);
|
||||
}
|
||||
|
||||
html! {
|
||||
<ContextProvider<PostsContext> context={(*posts).clone()}>
|
||||
{props.children.clone()}
|
||||
</ContextProvider<PostsContext>>
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user