mirror of
https://github.com/zyllian/zyllian.github.io.git
synced 2025-01-18 03:32:30 -08:00
Add development server
This commit is contained in:
parent
04bddee78c
commit
2032d86daa
11 changed files with 1497 additions and 42 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
/site/build
|
||||||
|
|
2
.ignore
Normal file
2
.ignore
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
/docs
|
||||||
|
/site
|
11
.vscode/tasks.json
vendored
11
.vscode/tasks.json
vendored
|
@ -3,13 +3,20 @@
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"type": "cargo",
|
"type": "cargo",
|
||||||
"command": "run",
|
"command": "watch",
|
||||||
|
"args": ["-x", "run -- serve"],
|
||||||
"problemMatcher": ["$rustc"],
|
"problemMatcher": ["$rustc"],
|
||||||
"label": "rust: cargo run",
|
"label": "dev serve site",
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
"isDefault": true
|
"isDefault": true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "cargo",
|
||||||
|
"command": "run",
|
||||||
|
"problemMatcher": ["$rustc"],
|
||||||
|
"label": "build site"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
1117
Cargo.lock
generated
1117
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -7,10 +7,18 @@ edition = "2018"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
extract-frontmatter = "2.1"
|
extract-frontmatter = "2.1"
|
||||||
fs_extra = "1.2"
|
fs_extra = "1.2"
|
||||||
|
futures = { version = "0.3", optional = true }
|
||||||
gray_matter = "0.1"
|
gray_matter = "0.1"
|
||||||
handlebars = "4.1"
|
handlebars = "4.1"
|
||||||
|
hotwatch = { version = "0.4", optional = true }
|
||||||
lol_html = "0.3"
|
lol_html = "0.3"
|
||||||
pulldown-cmark = { version = "0.8", default-features = false, features = ["simd"] }
|
pulldown-cmark = { version = "0.8", default-features = false, features = ["simd"] }
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_yaml = "0.8"
|
serde_yaml = "0.8"
|
||||||
|
tokio = { version = "1.10", features = ["macros", "rt-multi-thread"], optional = true }
|
||||||
walkdir = "2"
|
walkdir = "2"
|
||||||
|
warp = { version = "0.3", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["serve"]
|
||||||
|
serve = ["futures", "hotwatch", "tokio", "warp"]
|
||||||
|
|
15
docs/404.html
Normal file
15
docs/404.html
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head><meta charset="utf-8">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="/static/site.css">
|
||||||
|
<title>zoey.dev</title><base href="https://zoey.dev"></head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1>404 Not Found</h1>
|
||||||
|
<p>The page or resource requested does not exist. <a href="/">Back to home</a></p>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
3
site/pages/404.md
Normal file
3
site/pages/404.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
# 404 Not Found
|
||||||
|
|
||||||
|
The page or resource requested does not exist. [Back to home](/)
|
65
src/lib.rs
65
src/lib.rs
|
@ -1,4 +1,8 @@
|
||||||
#![feature(path_try_exists)]
|
#![feature(path_try_exists)]
|
||||||
|
#![feature(async_closure)]
|
||||||
|
|
||||||
|
#[cfg(feature = "serve")]
|
||||||
|
pub mod serving;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
|
@ -13,6 +17,10 @@ use pulldown_cmark::{Options, Parser};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
const PAGES_PATH: &str = "pages";
|
||||||
|
const TEMPLATES_PATH: &str = "templates";
|
||||||
|
const STATIC_PATH: &str = "static";
|
||||||
|
|
||||||
/// Struct for the site's configuration.
|
/// Struct for the site's configuration.
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct SiteConfig {
|
pub struct SiteConfig {
|
||||||
|
@ -69,7 +77,7 @@ impl Site {
|
||||||
.context("Failed to parse site config")?;
|
.context("Failed to parse site config")?;
|
||||||
|
|
||||||
let mut template_index = HashMap::new();
|
let mut template_index = HashMap::new();
|
||||||
let templates_path = site_path.join("templates");
|
let templates_path = site_path.join(TEMPLATES_PATH);
|
||||||
for entry in WalkDir::new(&templates_path).into_iter() {
|
for entry in WalkDir::new(&templates_path).into_iter() {
|
||||||
let entry = entry.context("Failed to read template entry")?;
|
let entry = entry.context("Failed to read template entry")?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
@ -89,7 +97,7 @@ impl Site {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut page_index = HashMap::new();
|
let mut page_index = HashMap::new();
|
||||||
let pages_path = site_path.join("pages");
|
let pages_path = site_path.join(PAGES_PATH);
|
||||||
for entry in WalkDir::new(&pages_path).into_iter() {
|
for entry in WalkDir::new(&pages_path).into_iter() {
|
||||||
let entry = entry.context("Failed to read page entry")?;
|
let entry = entry.context("Failed to read page entry")?;
|
||||||
let path = entry.path();
|
let path = entry.path();
|
||||||
|
@ -117,9 +125,16 @@ impl Site {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the site once.
|
/// Builds the site once.
|
||||||
pub fn build_once(&self) -> anyhow::Result<()> {
|
pub fn build_once(self) -> anyhow::Result<()> {
|
||||||
let builder = SiteBuilder::new(self, None).prepare()?;
|
let builder = SiteBuilder::new(self, false).prepare()?;
|
||||||
|
|
||||||
|
builder.site.build_all_pages(&builder)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper method to build all available pages.
|
||||||
|
fn build_all_pages(&self, builder: &SiteBuilder) -> anyhow::Result<()> {
|
||||||
for page_name in self.page_index.keys() {
|
for page_name in self.page_index.keys() {
|
||||||
builder.build_page(page_name)?;
|
builder.build_page(page_name)?;
|
||||||
}
|
}
|
||||||
|
@ -135,27 +150,30 @@ struct SiteBuilder<'a> {
|
||||||
/// The Handlebars registry used to render templates.
|
/// The Handlebars registry used to render templates.
|
||||||
reg: Handlebars<'a>,
|
reg: Handlebars<'a>,
|
||||||
/// The site info used to build the site.
|
/// The site info used to build the site.
|
||||||
site: &'a Site,
|
site: Site,
|
||||||
/// The path to the build directory.
|
/// The path to the build directory.
|
||||||
build_path: PathBuf,
|
build_path: PathBuf,
|
||||||
/// Whether the site should be build for viewing locally without a server.
|
/// Whether the site is going to be served locally with the dev server.
|
||||||
local_mode: Option<String>,
|
serving: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> SiteBuilder<'a> {
|
impl<'a> SiteBuilder<'a> {
|
||||||
/// Creates a new site builder.
|
/// Creates a new site builder.
|
||||||
pub fn new(site: &'a Site, local_mode: Option<String>) -> Self {
|
pub fn new(site: Site, serving: bool) -> Self {
|
||||||
let build_path = match &site.config.build {
|
let mut build_path = match &site.config.build {
|
||||||
Some(build) => site.site_path.join(build),
|
Some(build) => site.site_path.join(build),
|
||||||
_ => site.site_path.join("build"),
|
_ => site.site_path.join("build"),
|
||||||
};
|
};
|
||||||
|
if serving {
|
||||||
|
build_path = site.site_path.join("build");
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
matter: Matter::new(),
|
matter: Matter::new(),
|
||||||
reg: Handlebars::new(),
|
reg: Handlebars::new(),
|
||||||
site,
|
site,
|
||||||
build_path,
|
build_path,
|
||||||
local_mode,
|
serving,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -164,7 +182,7 @@ impl<'a> SiteBuilder<'a> {
|
||||||
if std::fs::try_exists(&self.build_path)
|
if std::fs::try_exists(&self.build_path)
|
||||||
.context("Failed check if build directory exists")?
|
.context("Failed check if build directory exists")?
|
||||||
{
|
{
|
||||||
std::fs::remove_dir_all(self.build_path.join("static"))
|
std::fs::remove_dir_all(self.build_path.join(STATIC_PATH))
|
||||||
.context("Failed to remove static directory")?;
|
.context("Failed to remove static directory")?;
|
||||||
for entry in WalkDir::new(&self.build_path) {
|
for entry in WalkDir::new(&self.build_path) {
|
||||||
let entry = entry?;
|
let entry = entry?;
|
||||||
|
@ -188,12 +206,19 @@ impl<'a> SiteBuilder<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fs_extra::copy_items(
|
fs_extra::copy_items(
|
||||||
&[self.site.site_path.join("static")],
|
&[self.site.site_path.join(STATIC_PATH)],
|
||||||
&self.build_path,
|
&self.build_path,
|
||||||
&fs_extra::dir::CopyOptions::default(),
|
&fs_extra::dir::CopyOptions::default(),
|
||||||
)
|
)
|
||||||
.context("Failed to copy static directory")?;
|
.context("Failed to copy static directory")?;
|
||||||
|
|
||||||
|
if self.serving {
|
||||||
|
std::fs::write(
|
||||||
|
self.build_path.join(format!("{}/_dev.js", STATIC_PATH)),
|
||||||
|
include_str!("./refresh_websocket.js"),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,11 +250,17 @@ impl<'a> SiteBuilder<'a> {
|
||||||
element_content_handlers: vec![element!("head", |el| {
|
element_content_handlers: vec![element!("head", |el| {
|
||||||
el.prepend(r#"<meta charset="utf-8">"#, ContentType::Html);
|
el.prepend(r#"<meta charset="utf-8">"#, ContentType::Html);
|
||||||
el.append(&format!("<title>{}</title>", title), ContentType::Html);
|
el.append(&format!("<title>{}</title>", title), ContentType::Html);
|
||||||
let base = self
|
if self.serving {
|
||||||
.local_mode
|
el.append(
|
||||||
.as_ref()
|
&format!(r#"<script src="/{}/_dev.js"></script>"#, STATIC_PATH),
|
||||||
.unwrap_or(&self.site.config.base_url);
|
ContentType::Html,
|
||||||
el.append(&format!(r#"<base href="{}">"#, base), ContentType::Html);
|
);
|
||||||
|
} else {
|
||||||
|
el.append(
|
||||||
|
&format!(r#"<base href="{}">"#, &self.site.config.base_url),
|
||||||
|
ContentType::Html,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})],
|
})],
|
||||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -2,9 +2,40 @@ use std::path::Path;
|
||||||
|
|
||||||
use zoey::Site;
|
use zoey::Site;
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
#[cfg(feature = "serve")]
|
||||||
let builder = Site::new(Path::new("site"))?;
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
builder.build_once()?;
|
enum Mode {
|
||||||
|
Build,
|
||||||
|
Serve,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "serve")]
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() -> anyhow::Result<()> {
|
||||||
|
let site = Site::new(Path::new("site"))?;
|
||||||
|
|
||||||
|
let mut mode = Mode::Build;
|
||||||
|
for arg in std::env::args() {
|
||||||
|
if arg == "serve" {
|
||||||
|
mode = Mode::Serve;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match mode {
|
||||||
|
Mode::Build => site.build_once()?,
|
||||||
|
Mode::Serve => site.serve().await?,
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("Build complete!");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "serve"))]
|
||||||
|
fn main() -> anyhow::Result<()> {
|
||||||
|
let site = Site::new(Path::new("site"))?;
|
||||||
|
site.build_once()?;
|
||||||
|
|
||||||
println!("Build complete!");
|
println!("Build complete!");
|
||||||
|
|
||||||
|
|
36
src/refresh_websocket.js
Normal file
36
src/refresh_websocket.js
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
console.log("Connecting...");
|
||||||
|
|
||||||
|
let socket;
|
||||||
|
|
||||||
|
function start(reload) {
|
||||||
|
socket = new WebSocket("ws://127.0.0.1:8080");
|
||||||
|
let reloading = false;
|
||||||
|
socket.onmessage = function (ev) {
|
||||||
|
if (ev.data === "reload") {
|
||||||
|
reloading = true;
|
||||||
|
console.log("Reloading...");
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
socket.onclose = function () {
|
||||||
|
if (!reloading) {
|
||||||
|
console.error("Connection closed.");
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log("Retrying connection...");
|
||||||
|
start(true);
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
socket.onopen = function () {
|
||||||
|
console.log("Connected!");
|
||||||
|
if (reload) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
start(false);
|
||||||
|
})();
|
244
src/serving.rs
Normal file
244
src/serving.rs
Normal file
|
@ -0,0 +1,244 @@
|
||||||
|
//! Module containing code to serve the dev site.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
net::SocketAddr,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
use futures::SinkExt;
|
||||||
|
use hotwatch::{Event, Hotwatch};
|
||||||
|
use warp::{
|
||||||
|
path::FullPath,
|
||||||
|
reply::Response,
|
||||||
|
ws::{Message, WebSocket},
|
||||||
|
Filter,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{Site, SiteBuilder, PAGES_PATH, TEMPLATES_PATH};
|
||||||
|
|
||||||
|
fn with_build_path(
|
||||||
|
build_path: PathBuf,
|
||||||
|
) -> impl Filter<Extract = (PathBuf,), Error = std::convert::Infallible> + Clone {
|
||||||
|
warp::any().map(move || build_path.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to get the "name" of a path.
|
||||||
|
fn get_name(path: &Path) -> (PathBuf, String) {
|
||||||
|
let name = path.with_extension("");
|
||||||
|
let name_str = name.display().to_string();
|
||||||
|
(name, name_str)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to make a path relative.
|
||||||
|
fn rel(path: &Path, prefix: &Path) -> Result<PathBuf, std::path::StripPrefixError> {
|
||||||
|
Ok(path.strip_prefix(prefix)?.to_owned())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates or updates a resource.
|
||||||
|
fn create(
|
||||||
|
builder: &mut SiteBuilder,
|
||||||
|
path: &Path,
|
||||||
|
relative_path: &Path,
|
||||||
|
build: bool,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
if let Ok(page_path) = relative_path.strip_prefix(PAGES_PATH) {
|
||||||
|
let (_page_name, page_name_str) = get_name(page_path);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.site
|
||||||
|
.page_index
|
||||||
|
.insert(page_name_str.clone(), path.to_owned());
|
||||||
|
if build {
|
||||||
|
builder.build_page(&page_name_str)?;
|
||||||
|
}
|
||||||
|
} else if let Ok(template_path) = relative_path.strip_prefix(TEMPLATES_PATH) {
|
||||||
|
let (_template_name, template_name_str) = get_name(template_path);
|
||||||
|
builder.refresh_template(&template_name_str, path)?;
|
||||||
|
if build {
|
||||||
|
builder.site.build_all_pages(builder)?;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
anyhow::anyhow!("ahh");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes an existing resource.
|
||||||
|
fn remove(builder: &mut SiteBuilder, _path: &Path, relative_path: &Path) -> anyhow::Result<()> {
|
||||||
|
if let Ok(page_path) = relative_path.strip_prefix(PAGES_PATH) {
|
||||||
|
let (page_name, page_name_str) = get_name(page_path);
|
||||||
|
|
||||||
|
builder.site.page_index.remove(&page_name_str);
|
||||||
|
std::fs::remove_file(builder.build_path.join(page_name.with_extension("html")))?;
|
||||||
|
} else if let Ok(template_path) = relative_path.strip_prefix(TEMPLATES_PATH) {
|
||||||
|
let (_template_name, template_name_str) = get_name(template_path);
|
||||||
|
builder.site.template_index.remove(&template_name_str);
|
||||||
|
builder.reg.unregister_template(&template_name_str);
|
||||||
|
builder.site.build_all_pages(builder)?;
|
||||||
|
} else {
|
||||||
|
anyhow::anyhow!("ahh");
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Site {
|
||||||
|
/// Serves the site for development. Don't use this in production.
|
||||||
|
pub async fn serve(self) -> anyhow::Result<()> {
|
||||||
|
let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
|
||||||
|
|
||||||
|
let mut builder = SiteBuilder::new(self, true).prepare()?;
|
||||||
|
let site = &builder.site;
|
||||||
|
let build_path = builder.build_path.clone();
|
||||||
|
|
||||||
|
// Perform initial build
|
||||||
|
for page_name in site.page_index.keys() {
|
||||||
|
if let Err(e) = builder.build_page(page_name) {
|
||||||
|
eprintln!("Failed to build page {}: {}", page_name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map of websocket connections
|
||||||
|
let peers: Arc<Mutex<HashMap<SocketAddr, WebSocket>>> =
|
||||||
|
Arc::new(Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
|
// Watch for changes to the site
|
||||||
|
let mut hotwatch = Hotwatch::new().expect("Hotwatch failed to initialize");
|
||||||
|
let hw_peers = peers.clone();
|
||||||
|
hotwatch
|
||||||
|
.watch(site.site_path.clone(), move |event| {
|
||||||
|
let site_path = builder.site.site_path.canonicalize().unwrap();
|
||||||
|
let peers = hw_peers.clone();
|
||||||
|
|
||||||
|
match (|| match event {
|
||||||
|
Event::Write(path) => {
|
||||||
|
let rel = rel(&path, &site_path)?;
|
||||||
|
println!("CHANGED - {:?}", rel);
|
||||||
|
create(&mut builder, &path, &rel, true)?;
|
||||||
|
Ok::<_, anyhow::Error>(true)
|
||||||
|
}
|
||||||
|
Event::Create(path) => {
|
||||||
|
let rel = rel(&path, &site_path)?;
|
||||||
|
println!("CREATED - {:?}", rel);
|
||||||
|
create(&mut builder, &path, &rel, true)?;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
Event::Remove(path) => {
|
||||||
|
let rel = rel(&path, &site_path)?;
|
||||||
|
println!("REMOVED - {:?}", rel);
|
||||||
|
remove(&mut builder, &path, &rel)?;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
Event::Rename(old, new) => {
|
||||||
|
let old_rel = rel(&old, &site_path)?;
|
||||||
|
let new_rel = rel(&new, &site_path)?;
|
||||||
|
println!("RENAMED - {:?} -> {:?}", old_rel, new_rel);
|
||||||
|
create(&mut builder, &new, &new_rel, false)?;
|
||||||
|
remove(&mut builder, &old, &old_rel)?;
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
_ => Ok(false),
|
||||||
|
})() {
|
||||||
|
Ok(reload) => {
|
||||||
|
if reload {
|
||||||
|
// Send reload event to connected websockets
|
||||||
|
let mut peers = peers.lock().unwrap();
|
||||||
|
let mut to_remove = Vec::new();
|
||||||
|
for (addr, peer) in peers.iter_mut() {
|
||||||
|
let task = async {
|
||||||
|
peer.send(Message::text("reload".to_string())).await?;
|
||||||
|
Ok::<_, anyhow::Error>(())
|
||||||
|
};
|
||||||
|
to_remove.push(*addr);
|
||||||
|
if let Err(e) = futures::executor::block_on(task) {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for addr in &to_remove {
|
||||||
|
peers.remove(addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(e) => eprintln!("Failed to update: {}", e),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.expect("Failed to watch file");
|
||||||
|
|
||||||
|
let routes = warp::any()
|
||||||
|
.and(warp::ws())
|
||||||
|
.and(warp::filters::addr::remote())
|
||||||
|
.and_then(move |ws: warp::ws::Ws, addr| {
|
||||||
|
let peers = peers.clone();
|
||||||
|
async move {
|
||||||
|
// Add websocket connection to peers list
|
||||||
|
if let Some(addr) = addr {
|
||||||
|
let peers = peers.clone();
|
||||||
|
return Ok(ws.on_upgrade(move |websocket| async move {
|
||||||
|
peers.lock().unwrap().insert(addr, websocket);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
Err(warp::reject())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.or(warp::any().and(warp::get()).and(
|
||||||
|
warp::path::full()
|
||||||
|
.and(with_build_path(build_path.clone()))
|
||||||
|
.and_then(
|
||||||
|
async move |path: FullPath,
|
||||||
|
build_path: PathBuf|
|
||||||
|
-> Result<Response, warp::Rejection> {
|
||||||
|
// Serve static files
|
||||||
|
let p = &path.as_str()[1..];
|
||||||
|
let mut p = build_path.join(p);
|
||||||
|
|
||||||
|
if !p.exists() {
|
||||||
|
p = p.with_extension("html");
|
||||||
|
}
|
||||||
|
if p.is_dir() {
|
||||||
|
p = p.join("index.html");
|
||||||
|
}
|
||||||
|
|
||||||
|
if p.exists() {
|
||||||
|
let body = std::fs::read_to_string(&p).unwrap();
|
||||||
|
let res = Response::new(body.into());
|
||||||
|
return Ok(res);
|
||||||
|
}
|
||||||
|
Err(warp::reject())
|
||||||
|
},
|
||||||
|
),
|
||||||
|
))
|
||||||
|
.or(warp::any()
|
||||||
|
.and(warp::path::full())
|
||||||
|
.and_then(move |path: FullPath| {
|
||||||
|
let build_path = build_path.clone();
|
||||||
|
async move {
|
||||||
|
// Handle missing files
|
||||||
|
println!("404 - {}", path.as_str());
|
||||||
|
Ok::<_, std::convert::Infallible>(warp::reply::html(
|
||||||
|
std::fs::read_to_string(build_path.join("404.html")).unwrap(),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
println!("Starting server at http://{}", addr);
|
||||||
|
warp::serve(routes).run(addr).await;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SiteBuilder<'a> {
|
||||||
|
/// Refreshes a template to ensure it's up to date.
|
||||||
|
pub fn refresh_template(
|
||||||
|
&mut self,
|
||||||
|
template_name: &str,
|
||||||
|
template_path: &Path,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
self.reg
|
||||||
|
.register_template_file(template_name, template_path)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue