smol bot
Diffstat (limited to 'src/expose.rs')
-rw-r--r--src/expose.rs164
1 files changed, 164 insertions, 0 deletions
diff --git a/src/expose.rs b/src/expose.rs
new file mode 100644
index 0000000..d6ee7b1
--- /dev/null
+++ b/src/expose.rs
@@ -0,0 +1,164 @@
+use axum::{
+ extract::Path,
+ http::{header::*, StatusCode},
+ response::{AppendHeaders, Html},
+ routing::get,
+ Router, Server as AxumServer,
+};
+
+use std::{net::SocketAddr, sync::LazyLock, time::SystemTime};
+const COMPILED_AT: LazyLock<SystemTime> =
+ LazyLock::new(|| edg::r! { || -> std::time::SystemTime { std::time::SystemTime::now() }});
+static COMPILED: LazyLock<String> = LazyLock::new(|| httpdate::fmt_http_date(*COMPILED_AT));
+
+fn no_bytes(map: HeaderMap) -> (StatusCode, Option<&'static [u8]>) {
+ if let Some(x) = map.get("if-modified-since")
+ && let Ok(x) = x.to_str()
+ && let Ok(x) = httpdate::parse_http_date(x)
+ && x < *COMPILED_AT
+ {
+ (StatusCode::NOT_MODIFIED, Some(&[]))
+ } else {
+ (StatusCode::OK, None)
+ }
+}
+
+macro_rules! html {
+ ($file:expr) => {
+ get(|_map: HeaderMap| async {
+ println!("a wild visitor approaches");
+ #[cfg(debug_assertions)]
+ return Html(std::fs::read(concat!("html-src/", stringify!($file), ".html")).unwrap());
+ #[cfg(not(debug_assertions))]
+ {
+ let (code, bytes) = no_bytes(_map);
+ (
+ code,
+ Html(bytes.unwrap_or(include_bytes!(concat!(
+ "../html/",
+ stringify!($file),
+ ".html"
+ )))),
+ )
+ }
+ })
+ };
+}
+
+macro_rules! png {
+ ($file:expr) => {
+ get(|map: HeaderMap| async {
+ let (code, bytes) = no_bytes(map);
+ let bytes = bytes.unwrap_or(include_bytes!(concat!(
+ "../html/",
+ stringify!($file),
+ ".png"
+ )));
+
+ (
+ code,
+ (
+ AppendHeaders([(CONTENT_TYPE, "image/png"), (LAST_MODIFIED, &*COMPILED)]),
+ bytes,
+ ),
+ )
+ })
+ };
+}
+
+pub struct Server;
+impl Server {
+ pub async fn spawn(addr: SocketAddr) {
+ let router = Router::new()
+ .route("/", html!(index))
+ .route("/fail.png", png!(fail))
+ .route("/bg.png", png!(bg))
+ .route("/border.png", png!(border))
+ .route("/border-active.png", png!(border_active))
+ .route("/border-hover.png", png!(border_hover))
+ .route("/favicon.ico", png!(favicon))
+ .route(
+ "/default.woff",
+ get(|| async {
+ (
+ [(CONTENT_TYPE, "font/woff")],
+ include_bytes!("../html-src/default.woff"),
+ )
+ }),
+ )
+ .route(
+ "/index.js",
+ get(|| async {
+ (
+ [(CONTENT_TYPE, "application/javascript")],
+ if cfg!(debug_assertions) {
+ std::fs::read_to_string("html-src/index.js").unwrap().leak()
+ } else {
+ include_str!("../html-src/index.js")
+ },
+ )
+ }),
+ )
+ .route(
+ "/files",
+ get(|| async {
+ serde_json::to_string(
+ &crate::bot::search::files()
+ .map(|(x, _)| {
+ x.with_extension("")
+ .file_name()
+ .unwrap()
+ .to_string_lossy()
+ .into_owned()
+ })
+ .collect::<Vec<_>>(),
+ )
+ .unwrap()
+ }),
+ )
+ .route(
+ "/blame/:file",
+ get(|Path(file): Path<String>| async move {
+ match crate::bot::search::files().map(|(x, _)| x).find(|x| {
+ x.with_extension("").file_name().unwrap().to_string_lossy() == file
+ }) {
+ Some(x) => (
+ StatusCode::OK,
+ crate::bot::git::whos(
+ &x.components().nth(1).unwrap().as_os_str().to_str().unwrap(),
+ u64::from_str_radix(
+ &x.with_extension("")
+ .file_name()
+ .unwrap()
+ .to_os_string()
+ .to_str()
+ .unwrap(),
+ 16,
+ )
+ .unwrap()
+ .into(),
+ ),
+ ),
+ None => (StatusCode::NOT_FOUND, String::default()),
+ }
+ }),
+ )
+ .route(
+ "/files/:file",
+ get(|Path(file): Path<String>| async move {
+ match crate::bot::search::files().map(|(x, _)| x).find(|x| {
+ x.with_extension("").file_name().unwrap().to_string_lossy() == file
+ }) {
+ Some(x) => (StatusCode::OK, std::fs::read(x).unwrap()),
+ None => (StatusCode::NOT_FOUND, vec![]),
+ }
+ }),
+ );
+ tokio::spawn(async move {
+ AxumServer::bind(&addr)
+ .serve(router.into_make_service())
+ .await
+ .unwrap();
+ });
+ }
+}