Switch over to WebAssembly, Rust and Yew #35
@ -14,11 +14,11 @@ struct Template {
|
|||||||
|
|
||||||
impl Template {
|
impl Template {
|
||||||
async fn load(path: impl AsRef<Path>) -> std::io::Result<Self> {
|
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 content = tokio::fs::read_to_string(path).await?;
|
||||||
|
|
||||||
let Some(index) = content.find("</body>") else {
|
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"));
|
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 {
|
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 });
|
let render = ServerRenderer::<App>::with_props(move || AppProps { url });
|
||||||
template.render(render).await
|
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 {
|
struct RenderRoute {
|
||||||
@ -59,25 +97,33 @@ fn collect_routes() -> Vec<RenderRoute> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn copy_resources(
|
async fn render_routes(env: &Env) -> std::io::Result<()> {
|
||||||
dist_dir: impl AsRef<Path>,
|
println!("Rendering routes ...");
|
||||||
out_dir: impl AsRef<Path>,
|
for RenderRoute { url, path } in collect_routes() {
|
||||||
) -> std::io::Result<()> {
|
println!("Rendering route: {url}");
|
||||||
let mut stack: Vec<(PathBuf, PathBuf)> = vec![(
|
|
||||||
dist_dir.as_ref().to_path_buf(),
|
|
||||||
out_dir.as_ref().to_path_buf(),
|
|
||||||
)];
|
|
||||||
|
|
||||||
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() {
|
if !out_dir.exists() {
|
||||||
log::info!("Creaing output directory: {out_dir:?}");
|
|
||||||
tokio::fs::create_dir(&out_dir).await?;
|
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?;
|
let mut resources = tokio::fs::read_dir(&dist_dir).await?;
|
||||||
while let Some(entry) = resources.next_entry().await? {
|
while let Some(entry) = resources.next_entry().await? {
|
||||||
let Ok(file_type) = entry.file_type().await else {
|
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;
|
continue;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -88,24 +134,31 @@ async fn copy_resources(
|
|||||||
.expect("valid filename")
|
.expect("valid filename")
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
if file_name == "index.html" {
|
if file_name == "index.html" || file_name.starts_with(".") {
|
||||||
continue;
|
println!("Skipping resource: {:?}", dist_prefix.join(file_name));
|
||||||
}
|
|
||||||
|
|
||||||
if file_name.starts_with('.') {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = entry.path();
|
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?;
|
tokio::fs::copy(path, out_dir.join(file_name)).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if file_type.is_dir() {
|
if file_type.is_dir() {
|
||||||
let name = entry.file_name();
|
let name = entry.file_name();
|
||||||
let out_dir = out_dir.join(name.clone());
|
let new_dist_prefix = dist_prefix.join(&name);
|
||||||
let dist_dir = dist_dir.join(name.clone());
|
let new_out_prefix = out_prefix.join(&name);
|
||||||
stack.push((dist_dir, out_dir));
|
stack.push((new_dist_prefix, new_out_prefix));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,39 +168,17 @@ async fn copy_resources(
|
|||||||
|
|
||||||
#[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
|
||||||
env_logger::init();
|
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");
|
// Render all the routes
|
||||||
log::info!("Established distribution directory: {dist_dir:?}");
|
render_routes(&env).await?;
|
||||||
let template = Template::load(dist_dir.join("index.html")).await?;
|
|
||||||
|
|
||||||
let out_dir = root_dir.join("output");
|
// Copy over all the other resources.
|
||||||
|
copy_resources(&env).await?;
|
||||||
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?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user