use crate::{State, helper::Css}; use axum::extract::State as S; use axum::response::Html; use markup::doctype; use serde_json::{Map, Value}; use std::sync::Arc; use tokio::{fs::read_to_string, sync::RwLock}; pub(crate) async fn webui_style() -> Css { Css(if cfg!(debug_assertions) { read_to_string("src/style.css").await.unwrap() } else { include_str!("style.css").to_string() }) } pub(crate) async fn webui(S(state): S>>) -> Html { let g = state.read().await; let doc = markup::new! { @doctype() html { head { meta[charset="UTF-8"]; link[rel="stylesheet", href="/style.css"]; title { "Queue-Server" } } body { section { h2 { "Workers"} ul { @for (id, w) in &g.workers { li { @Worker { id: *id, w } } }} } section.tasks { div { h2 { "Queued" } p.count { @g.queue.len() " tasks" } ul { @for key in &g.queue { li { @Task { key, data: g.metadata.get(key).unwrap(), class: "task queue" } } }} } div { h2 { "Loading" } p.count { @g.loading.len() " tasks" } ul { @for key in &g.loading { li { @Task { key, data: g.metadata.get(key).unwrap(), class: "task loading" } } }} } div { h2 { "Completed" } p.count { @g.complete.len() " tasks" } ul { @for key in &g.complete { li { @Task { key, data: g.metadata.get(key).unwrap(), class: "task complete" } } }} } } } } }; Html(doc.to_string()) } markup::define!( Worker<'a>(id: u64, w: &'a crate::Worker) { div[class=worker_class(w)] { h3 { @w.name } span { "ID: " @id } " " @if w.accept > 0 { span { "Accepting Jobs (" @w.accept ")" } } else { span { "Idle" } } } } Task<'a>(key: &'a str, data: &'a Map, class: &'a str) { div[class=class] { h3 { @data["title"].as_str().unwrap_or(key) } span.key { @key } } } ); fn worker_class(w: &crate::Worker) -> &'static str { if w.accept > 0 { "worker accepting" } else { "worker busy" } }