Switch over to WebAssembly, Rust and Yew #35

Merged
BlakeRain merged 87 commits from yew-static into main 2023-08-30 18:01:40 +00:00
3 changed files with 474 additions and 8 deletions
Showing only changes of commit 731d13a9b8 - Show all commits

231
Cargo.lock generated
View File

@ -26,6 +26,21 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "android-tzdata"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
[[package]]
name = "android_system_properties"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "anymap2" name = "anymap2"
version = "0.13.0" version = "0.13.0"
@ -43,6 +58,19 @@ dependencies = [
"syn 2.0.29", "syn 2.0.29",
] ]
[[package]]
name = "atom_syndication"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "571832dcff775e26562e8e6930cd483de5587301d40d3a3b85d532b6383e15a7"
dependencies = [
"chrono",
"derive_builder",
"diligent-date-parser",
"never",
"quick-xml 0.30.0",
]
[[package]] [[package]]
name = "atomic-polyfill" name = "atomic-polyfill"
version = "0.1.11" version = "0.1.11"
@ -145,6 +173,21 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chrono"
version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5"
dependencies = [
"android-tzdata",
"iana-time-zone",
"js-sys",
"num-traits",
"time 0.1.45",
"wasm-bindgen",
"winapi",
]
[[package]] [[package]]
name = "cobs" name = "cobs"
version = "0.2.3" version = "0.2.3"
@ -192,6 +235,41 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216" checksum = "7059fff8937831a9ae6f0fe4d658ffabf58f2ca96aa9dec1c889f936f705f216"
[[package]]
name = "darling"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
dependencies = [
"darling_core",
"darling_macro",
]
[[package]]
name = "darling_core"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
dependencies = [
"fnv",
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 1.0.109",
]
[[package]]
name = "darling_macro"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
dependencies = [
"darling_core",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "deranged" name = "deranged"
version = "0.3.8" version = "0.3.8"
@ -201,6 +279,46 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "derive_builder"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d67778784b508018359cbc8696edb3db78160bab2c2a28ba7f56ef6932997f8"
dependencies = [
"derive_builder_macro",
]
[[package]]
name = "derive_builder_core"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f"
dependencies = [
"darling",
"proc-macro2",
"quote",
"syn 1.0.109",
]
[[package]]
name = "derive_builder_macro"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebcda35c7a396850a55ffeac740804b40ffec779b98fffbb1738f4033f0ee79e"
dependencies = [
"derive_builder_core",
"syn 1.0.109",
]
[[package]]
name = "diligent-date-parser"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6cf7fe294274a222363f84bcb63cdea762979a0443b4cf1f4f8fd17c86b1182"
dependencies = [
"chrono",
]
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.33" version = "0.8.33"
@ -914,6 +1032,35 @@ dependencies = [
"tokio-native-tls", "tokio-native-tls",
] ]
[[package]]
name = "iana-time-zone"
version = "0.1.57"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613"
dependencies = [
"android_system_properties",
"core-foundation-sys",
"iana-time-zone-haiku",
"js-sys",
"wasm-bindgen",
"windows",
]
[[package]]
name = "iana-time-zone-haiku"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
dependencies = [
"cc",
]
[[package]]
name = "ident_case"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
[[package]] [[package]]
name = "idna" name = "idna"
version = "0.4.0" version = "0.4.0"
@ -1069,7 +1216,7 @@ dependencies = [
"syn 2.0.29", "syn 2.0.29",
"syntect", "syntect",
"thiserror", "thiserror",
"time", "time 0.3.26",
"words-count", "words-count",
] ]
@ -1107,7 +1254,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2"
dependencies = [ dependencies = [
"libc", "libc",
"wasi", "wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys", "windows-sys",
] ]
@ -1120,7 +1267,7 @@ dependencies = [
"quote", "quote",
"serde", "serde",
"thiserror", "thiserror",
"time", "time 0.3.26",
] ]
[[package]] [[package]]
@ -1141,6 +1288,12 @@ dependencies = [
"tempfile", "tempfile",
] ]
[[package]]
name = "never"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c96aba5aa877601bb3f6dd6a63a969e1f82e60646e81e71b14496995e9853c91"
[[package]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -1151,6 +1304,15 @@ dependencies = [
"minimal-lexical", "minimal-lexical",
] ]
[[package]]
name = "num-traits"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "num_cpus" name = "num_cpus"
version = "1.16.0" version = "1.16.0"
@ -1338,9 +1500,9 @@ dependencies = [
"base64", "base64",
"indexmap 1.9.3", "indexmap 1.9.3",
"line-wrap", "line-wrap",
"quick-xml", "quick-xml 0.29.0",
"serde", "serde",
"time", "time 0.3.26",
] ]
[[package]] [[package]]
@ -1445,6 +1607,16 @@ dependencies = [
"memchr", "memchr",
] ]
[[package]]
name = "quick-xml"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956"
dependencies = [
"encoding_rs",
"memchr",
]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.33"
@ -1535,6 +1707,18 @@ version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746" checksum = "afab94fb28594581f62d981211a9a4d53cc8130bbcbbb89a0440d9b8e81a7746"
[[package]]
name = "rss"
version = "2.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7e6c0ea0e621c2a3aa34850ebd711526f0ac7385921f57d2430a47cecc7b9cbc"
dependencies = [
"atom_syndication",
"derive_builder",
"never",
"quick-xml 0.30.0",
]
[[package]] [[package]]
name = "rustc-demangle" name = "rustc-demangle"
version = "0.1.23" version = "0.1.23"
@ -1702,6 +1886,8 @@ name = "site"
version = "2.0.0" version = "2.0.0"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"atom_syndication",
"chrono",
"enum-iterator", "enum-iterator",
"env_logger", "env_logger",
"gloo 0.10.0", "gloo 0.10.0",
@ -1710,10 +1896,11 @@ dependencies = [
"macros", "macros",
"model", "model",
"reqwest", "reqwest",
"rss",
"serde", "serde",
"serde_json", "serde_json",
"thiserror", "thiserror",
"time", "time 0.3.26",
"tokio", "tokio",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
@ -1775,6 +1962,12 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "strsim"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.109" version = "1.0.109"
@ -1860,6 +2053,17 @@ dependencies = [
"syn 2.0.29", "syn 2.0.29",
] ]
[[package]]
name = "time"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a"
dependencies = [
"libc",
"wasi 0.10.0+wasi-snapshot-preview1",
"winapi",
]
[[package]] [[package]]
name = "time" name = "time"
version = "0.3.26" version = "0.3.26"
@ -2122,6 +2326,12 @@ dependencies = [
"try-lock", "try-lock",
] ]
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.11.0+wasi-snapshot-preview1" version = "0.11.0+wasi-snapshot-preview1"
@ -2246,6 +2456,15 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.48.0" version = "0.48.0"

View File

@ -14,6 +14,9 @@ hydration = [
] ]
static = [ static = [
"atom_syndication",
"chrono",
"rss",
"yew/ssr" "yew/ssr"
] ]
@ -82,6 +85,9 @@ wasm-bindgen-futures = { version = "0.4" }
wasm-logger = { version = "0.2" } wasm-logger = { version = "0.2" }
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
atom_syndication = { version = "0.12", optional = true }
chrono = { version = "0.4", optional = true, features = ["clock"] }
rss = { version = "2.0", optional = true }
tokio = { version = "1.32", features = ["full"] } tokio = { version = "1.32", features = ["full"] }
env_logger = { version = "0.10" } env_logger = { version = "0.10" }

View File

@ -1,10 +1,15 @@
use std::path::{Path, PathBuf}; use std::{
fmt::Write,
path::{Path, PathBuf},
};
use site::{ use site::{
app::{HeadWriter, StaticApp, StaticAppProps}, app::{HeadWriter, StaticApp, StaticAppProps},
pages::Route, pages::Route,
}; };
use chrono::Datelike;
use time::format_description::well_known::{Rfc2822, Rfc3339};
use yew::ServerRenderer; use yew::ServerRenderer;
use yew_router::Routable; use yew_router::Routable;
@ -97,6 +102,15 @@ impl Env {
tokio::fs::write(path, s).await tokio::fs::write(path, s).await
} }
async fn write_u8<P: AsRef<Path>>(&self, path: P, d: &[u8]) -> std::io::Result<()> {
let path = self.out_dir.clone().join(path);
if let Some(parent) = path.parent() {
tokio::fs::create_dir_all(parent).await?;
}
tokio::fs::write(path, d).await
}
} }
struct RenderRoute { struct RenderRoute {
@ -188,6 +202,226 @@ async fn copy_resources(env: &Env) -> std::io::Result<()> {
Ok(()) Ok(())
} }
async fn generate_sitemap(env: &Env) -> std::io::Result<()> {
println!("Rendering sitemap.xml ...");
let now = time::OffsetDateTime::now_utc()
.format(&Rfc3339)
.expect("formatted time");
let common =
format!("<lastmod>{now}</lastmod><changefreq>daily</changefeq><priority>0.7</priority>");
let mut result = String::new();
write!(result, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>").expect("write to string");
write!(
result,
"<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">"
)
.expect("write to string");
for route in enum_iterator::all::<Route>() {
let url = route.to_path();
write!(
result,
"<url><loc>https://blakerain.com{url}</loc>{common}</url>"
)
.expect("write to string");
}
write!(result, "</urlset>").expect("write to string");
env.write_str("sitemap.xml", &result).await?;
Ok(())
}
async fn generate_rss(env: &Env) -> std::io::Result<()> {
println!("Rendering feed.xml ...");
let now = time::OffsetDateTime::now_utc();
let items = site::model::blog::documents()
.into_iter()
.map(|doc| {
let mut item = rss::ItemBuilder::default();
item.title(doc.summary.title)
.author(Some("blake.rain@blakerain.com (Blake Rain)".to_string()))
.link(format!(
"https://blakerain.com{}",
Route::BlogPost {
doc_id: doc.summary.slug
}
.to_path()
))
.guid(
rss::GuidBuilder::default()
.value(format!(
"https://blakerain.com{}",
Route::BlogPost {
doc_id: doc.summary.slug
}
.to_path()
))
.permalink(true)
.build(),
);
if let Some(published) = doc.summary.published {
item.pub_date(published.format(&Rfc2822).expect("formatted time"));
}
if let Some(excerpt) = doc.summary.excerpt {
item.description(excerpt);
}
if let Some(cover_image) = doc.cover_image {
item.enclosure(
rss::EnclosureBuilder::default()
.url(format!("https://blakerain.com{}", cover_image))
.mime_type("image/jpeg")
.build(),
);
}
item.build()
})
.collect::<Vec<_>>();
let channel = rss::ChannelBuilder::default()
.title("Blake Rain")
.link("https://blakerain.com/")
.description("Feed of blog posts on Blake Rain's website")
.language(Some("en".to_string()))
.copyright(format!("All Rights Reserved {}, Blake Rain", now.year()))
.last_build_date(Some(now.format(&Rfc2822).expect("formatted time")))
.docs(Some(
"https://validator.w3.org/feed/docs/rss2.html".to_string(),
))
.image(Some(
rss::ImageBuilder::default()
.title("Blake Rain")
.url("https://blakerain.com/media/logo-text.png")
.link("https://blakerain.com/")
.build(),
))
.items(items)
.build();
let mut result = Vec::new();
channel.write_to(&mut result).expect("RSS");
env.write_u8("feeds/feed.xml", &result).await
}
async fn generate_atom(env: &Env) -> std::io::Result<()> {
println!("Rendering atom.xml ...");
let now = chrono::offset::Utc::now();
let items = site::model::blog::documents()
.into_iter()
.map(|doc| {
let mut entry = atom_syndication::EntryBuilder::default();
entry
.title(
atom_syndication::TextBuilder::default()
.value(doc.summary.title)
.build(),
)
.id(format!(
"https://blakerain.com{}",
Route::BlogPost {
doc_id: doc.summary.slug
}
.to_path()
))
.link(
atom_syndication::LinkBuilder::default()
.href(format!(
"https://blakerain.com{}",
Route::BlogPost {
doc_id: doc.summary.slug
}
.to_path()
))
.build(),
)
.author(
atom_syndication::PersonBuilder::default()
.name("Blake Rain")
.email(Some("blake.rain@blakerain.com".to_string()))
.uri(Some("https://blakerain.com".to_string()))
.build(),
);
if let Some(published) = doc.summary.published {
let chrono_bullshit = chrono::DateTime::from_utc(
chrono::naive::NaiveDateTime::from_timestamp_opt(published.unix_timestamp(), 0)
.expect("time to be simple"),
chrono::offset::FixedOffset::west_opt(0).expect("time to be simple"),
);
entry.published(Some(chrono_bullshit));
}
if let Some(excerpt) = doc.summary.excerpt {
entry.summary(
atom_syndication::TextBuilder::default()
.value(excerpt.to_string())
.build(),
);
}
entry.build()
})
.collect::<Vec<_>>();
let feed = atom_syndication::FeedBuilder::default()
.title("Blake Rain")
.id("https://blakerain.com")
.subtitle(Some(
atom_syndication::TextBuilder::default()
.value("Feed of blog posts on Blake Rain's website")
.build(),
))
.logo(Some(
"https://blakerain.com/media/logo-text.png".to_string(),
))
.icon(Some("https://blakerain.com/favicon.png".to_string()))
.rights(Some(
atom_syndication::TextBuilder::default()
.value(format!("All Right Reserved {}, Blake Rain", now.year()))
.build(),
))
.updated(now)
.author(
atom_syndication::PersonBuilder::default()
.name("Blake Rain")
.email(Some("blake.rain@blakerain.com".to_string()))
.uri(Some("https://blakerain.com".to_string()))
.build(),
)
.link(
atom_syndication::LinkBuilder::default()
.rel("alternate")
.href("https://blakerain.com")
.build(),
)
.link(
atom_syndication::LinkBuilder::default()
.rel("self")
.href("https://blakerain.com/feeds/atom.xml")
.build(),
)
.entries(items)
.build();
let mut result = Vec::new();
feed.write_to(&mut result).expect("Atom");
env.write_u8("feeds/atom.xml", &result).await
}
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize the logger for the site application // Initialize the logger for the site application
@ -196,11 +430,18 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create our environment. // Create our environment.
let env = Env::new().await?; let env = Env::new().await?;
// Render all the routes // Render all the routes.
render_routes(&env).await?; render_routes(&env).await?;
// Copy over all the other resources. // Copy over all the other resources.
copy_resources(&env).await?; copy_resources(&env).await?;
// Generate the sitemap.
generate_sitemap(&env).await?;
// Generate the feeds.
generate_rss(&env).await?;
generate_atom(&env).await?;
Ok(()) Ok(())
} }