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
Showing only changes of commit 1e13c899e2 - Show all commits

View File

@ -14,11 +14,11 @@ struct Template {
impl Template {
async fn load(path: impl AsRef<Path>) -> std::io::Result<Self> {
log::info!("Loading template from: {:?}", path.as_ref());
println!("Loading template from: {:?}", path.as_ref());
let content = tokio::fs::read_to_string(path).await?;
let Some(index) = content.find("</body>") else {
log::error!("Failed to find index of '</body>' close tag in 'dist/index.html'");
eprintln!("error: Failed to find index of '</body>' close tag in 'dist/index.html'");
return Err(std::io::Error::new(std::io::ErrorKind::Other, "Malformed index.html"));
};
@ -34,9 +34,47 @@ impl Template {
}
}
async fn render_route(template: &Template, url: String) -> String {
let render = ServerRenderer::<App>::with_props(move || AppProps { url });
template.render(render).await
struct Env {
dist_dir: PathBuf,
out_dir: PathBuf,
template: Template,
}
impl Env {
async fn new() -> std::io::Result<Self> {
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let dist_dir = root_dir.clone().join("dist");
let out_dir = root_dir.clone().join("output");
let template = Template::load(dist_dir.join("index.html")).await?;
if out_dir.exists() {
println!("Removing existing output directory");
tokio::fs::remove_dir_all(&out_dir).await?;
}
println!("Creating output directory");
tokio::fs::create_dir_all(&out_dir).await?;
Ok(Self {
dist_dir,
out_dir,
template,
})
}
async fn render_route(&self, url: String) -> String {
let render = ServerRenderer::<App>::with_props(move || AppProps { url });
self.template.render(render).await
}
async fn write_str<P: AsRef<Path>>(&self, path: P, s: &str) -> 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, s).await
}
}
struct RenderRoute {
@ -59,25 +97,33 @@ fn collect_routes() -> Vec<RenderRoute> {
.collect()
}
async fn copy_resources(
dist_dir: impl AsRef<Path>,
out_dir: impl AsRef<Path>,
) -> std::io::Result<()> {
let mut stack: Vec<(PathBuf, PathBuf)> = vec![(
dist_dir.as_ref().to_path_buf(),
out_dir.as_ref().to_path_buf(),
)];
async fn render_routes(env: &Env) -> std::io::Result<()> {
println!("Rendering routes ...");
for RenderRoute { url, path } in collect_routes() {
println!("Rendering route: {url}");
while let Some((dist_dir, out_dir)) = stack.pop() {
let html = env.render_route(url).await;
env.write_str(path, &html).await?;
}
Ok(())
}
async fn copy_resources(env: &Env) -> std::io::Result<()> {
println!("Copyying resources ...");
let mut stack: Vec<(PathBuf, PathBuf)> = vec![(PathBuf::from("."), PathBuf::from("."))];
while let Some((dist_prefix, out_prefix)) = stack.pop() {
let out_dir = env.out_dir.join(&out_prefix);
if !out_dir.exists() {
log::info!("Creaing output directory: {out_dir:?}");
tokio::fs::create_dir(&out_dir).await?;
}
let dist_dir = env.dist_dir.join(&dist_prefix);
let mut resources = tokio::fs::read_dir(&dist_dir).await?;
while let Some(entry) = resources.next_entry().await? {
let Ok(file_type) = entry.file_type().await else {
log::error!("Could not get file type for: {:?}", entry.path());
eprintln!("error: could not get file type for: {:?}", entry.path());
continue;
};
@ -88,24 +134,31 @@ async fn copy_resources(
.expect("valid filename")
.to_string();
if file_name == "index.html" {
continue;
}
if file_name.starts_with('.') {
if file_name == "index.html" || file_name.starts_with(".") {
println!("Skipping resource: {:?}", dist_prefix.join(file_name));
continue;
}
let path = entry.path();
log::info!("Copying resource: {:?}", path);
println!("Copying resource: {:?}", out_prefix.join(&file_name));
let out_path = out_dir.join(&file_name);
if out_path.exists() {
eprintln!("error: output file already exists: {:?}", out_path);
return Err(std::io::Error::new(
std::io::ErrorKind::AlreadyExists,
"Already exists",
));
}
tokio::fs::copy(path, out_dir.join(file_name)).await?;
}
if file_type.is_dir() {
let name = entry.file_name();
let out_dir = out_dir.join(name.clone());
let dist_dir = dist_dir.join(name.clone());
stack.push((dist_dir, out_dir));
let new_dist_prefix = dist_prefix.join(&name);
let new_out_prefix = out_prefix.join(&name);
stack.push((new_dist_prefix, new_out_prefix));
}
}
}
@ -115,39 +168,17 @@ async fn copy_resources(
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialize the logger for the site application
env_logger::init();
let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
// Create our environment.
let env = Env::new().await?;
let dist_dir = root_dir.clone().join("dist");
log::info!("Established distribution directory: {dist_dir:?}");
let template = Template::load(dist_dir.join("index.html")).await?;
// Render all the routes
render_routes(&env).await?;
let out_dir = root_dir.join("output");
if out_dir.exists() {
log::info!("Removing existing output directory: {out_dir:?}");
tokio::fs::remove_dir_all(&out_dir).await?;
}
log::info!("Creating output directory: {out_dir:?}");
tokio::fs::create_dir_all(&out_dir).await?;
log::info!("Copying resources to output directory");
copy_resources(&dist_dir, &out_dir).await?;
for RenderRoute { url, path } in collect_routes() {
log::info!("Rendering route: {url}");
let html = render_route(&template, url).await;
let path = out_dir.clone().join(path);
if let Some(parent) = path.parent() {
tokio::fs::create_dir_all(parent).await?;
}
log::info!("Writing route to file: {path:?}");
tokio::fs::write(path, html).await?;
}
// Copy over all the other resources.
copy_resources(&env).await?;
Ok(())
}