Skip to content

Commit

Permalink
use maud, remove context from being needed in threads
Browse files Browse the repository at this point in the history
  • Loading branch information
Austionian committed Sep 13, 2024
1 parent 9c1fa19 commit 381cdfa
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 85 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
/target
tailwindcss
mutants*
48 changes: 48 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ default-run = "gathering_surf"

[dependencies]
anyhow = "1.0.81"
axum = { version = "0.7", features= ["ws"]}
axum = { version = "0.7", features = ["ws"] }
chrono = "0.4"
chrono-tz = "0.9"
config = { git = "https://github.com/mehcode/config-rs.git", rev = "e3c1d0b452639478662a44f15ef6d5b6d969bf9b", default-features = false, features = ["yaml"] }
config = { git = "https://github.com/mehcode/config-rs.git", rev = "e3c1d0b452639478662a44f15ef6d5b6d969bf9b", default-features = false, features = [
"yaml",
] }
hyper = { version = "1", features = ["full"] }
maud = { version = "0.26.0", features = ["axum"] }
notify = "6.1.1"
reqwest = { version = "0.12", features = ["json"] }
serde = { version = "1", features = ["derive"] }
Expand All @@ -25,7 +28,7 @@ tokio-stream = "0.1.15"
tower = "0.5"
tower-http = { version = "0.5", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

[dev-dependencies]
insta = { version = "1", features = ["yaml"] }
Expand Down
2 changes: 1 addition & 1 deletion assets/styles.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const observerCallback = (mutationList) => {
if (mutation.target instanceof HTMLElement) {
try {
parseForecast(JSON.parse(mutation.target.innerText));
} catch(e) {
} catch (e) {
forecastFailed(e);
}
}
Expand Down
45 changes: 45 additions & 0 deletions src/quality.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// Contains quality text and associated color.
#[derive(Debug)]
pub struct Quality(pub &'static str, pub &'static str);

pub const GOOD: Quality = Quality("Good", "#0bd674");
Expand Down Expand Up @@ -102,3 +103,47 @@ impl Quality {
&POOR
}
}

#[cfg(test)]
mod tests {
use super::*;

const HIGH_WIND: f64 = 33.3;
const LOW_WIND: f64 = 5.5;

const HIGH_WAVES: f64 = 5.7;
const SMALL_WAVES: f64 = 0.3;

const NORTH_WIND: f64 = 180.0;
const SOUTH_WIND: f64 = 0.0;

#[test]
fn basic_wave_check_works_when_its_not_flat() {
assert!(Quality::basic_wave_check(2.1).is_none());
}

#[test]
fn basic_wave_check_works_when_it_is_flat() {
assert!(Quality::basic_wave_check(0.1).is_some());
assert_eq!(Quality::basic_wave_check(0.1).unwrap().0, "Flat");
assert_eq!(Quality::basic_wave_check(0.1).unwrap().1, "#a8a29e");
}

#[test]
fn a_north_beach_should_be_good_in_some_condition() {
assert_eq!(Quality::north(HIGH_WAVES, LOW_WIND, SOUTH_WIND).0, "Good");
}

#[test]
fn a_north_beach_shoud_be_bad_in_some_condition() {
assert_eq!(
Quality::north(HIGH_WAVES, HIGH_WIND, NORTH_WIND).0,
"Very Poor"
);
}

#[test]
fn a_north_beach_shoud_be_flat_in_some_condition() {
assert_eq!(Quality::north(SMALL_WAVES, HIGH_WIND, NORTH_WIND).0, "Flat");
}
}
129 changes: 69 additions & 60 deletions src/routes/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,36 @@ use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use maud::{html, Markup, PreEscaped};
use std::{convert::Infallible, sync::Arc};
use tokio::sync::{mpsc, Mutex};
use tokio::sync::mpsc;

/// This is ugly. Might be worth reverting to tera to create the
/// inline JS. Allows not having to pass the context around in a
/// mutex to the other threads though.
fn error_markup(error_type: &str, e: anyhow::Error) -> Markup {
html! {
script {
(PreEscaped("document.querySelector('#"))
(error_type)
(PreEscaped(r#"-container').classList.add("hidden");
document.querySelector('#"#))
(error_type)
(PreEscaped(r#"-error').innerHTML = `
<div class='p-12 flex flex-col items-center align-middle justify-center text-center'>
<h2 class='text-xl'>
Error loading "#))
(error_type)
(PreEscaped(r#" data - please refresh the page or try again later.
</h2>
<p>"#))
span class="font-mono" {
"Error: " (e)
}
(PreEscaped("</p></div>`;"))
}
}
}

/// Handler to return the website's index
pub async fn root(
Expand All @@ -17,83 +45,65 @@ pub async fn root(
// Create a channel to stream content to client as we get it
let (tx, rx) = mpsc::channel::<Result<String, Infallible>>(2);

let context = Arc::new(Mutex::new(tera::Context::new()));
let mut context = tera::Context::new();

let spot: Arc<Spot> = Arc::new(selected_spot.0.into());

// Add the initial context to the page for the loading state
{
let mut context = context.lock().await;

// Update with inital values.
context.insert("spot", &*spot);
context.insert("breaks", &state.breaks);
#[cfg(debug_assertions)]
context.insert("live_reload", &true);
#[cfg(not(debug_assertions))]
context.insert("live_reload", &false);

tx.send(Ok(TEMPLATES.render("index.html", &context)?))
.await?;
}
context.insert("spot", &*spot);
context.insert("breaks", &state.breaks);
#[cfg(debug_assertions)]
context.insert("live_reload", &true);
#[cfg(not(debug_assertions))]
context.insert("live_reload", &false);

tx.send(Ok(TEMPLATES.render("index.html", &context)?))
.await?;

let realtime_tx = tx.clone();
let realtime_context = context.clone();
let realtime_spot = spot.clone();
let realtime_state = state.clone();
tokio::spawn(async move {
match Realtime::try_get(realtime_spot, realtime_state.realtime_url).await {
Ok(realtime) => {
let mut context = realtime_context.lock().await;
context.insert("realtime_json", &serde_json::to_string(&realtime).unwrap());

realtime_tx
.send(Ok(TEMPLATES.render("realtime.html", &context).unwrap()))
.await
.unwrap();
let html = html!(
script type="application/json" id="realtime-data" {(
PreEscaped(
serde_json::to_string(&realtime).unwrap())
)}
)
.into();

realtime_tx.send(Ok(html)).await.unwrap();
}
Err(e) => {
let mut context = realtime_context.lock().await;
context.insert("error", &e.to_string());
context.insert("error_type", &"latest");
context.insert("container", &"latest-container");
context.insert("error_container", &"latest-error");
realtime_tx
.send(Ok(TEMPLATES.render("error.html", &context).unwrap()))
.send(Ok(html!((error_markup("latest", e))).into()))
.await
.unwrap();
}
}
});

let water_quality_tx = tx.clone();
let water_quality_context = context.clone();
let water_quality_spot = spot.clone();
let water_quality_state = state.clone();
tokio::spawn(async move {
match WaterQuality::try_get(water_quality_spot, water_quality_state.quality_url).await {
Ok(water_quality) => {
let mut context = water_quality_context.lock().await;
context.insert(
"water_quality_json",
&serde_json::to_string(&water_quality).unwrap(),
);

water_quality_tx
.send(Ok(TEMPLATES
.render("water_quality.html", &context)
.unwrap()))
.await
.unwrap();
let html = html!(
script type="application/json" id="water-quality-data" {(
PreEscaped(
serde_json::to_string(&water_quality).unwrap())
)}
)
.into();

water_quality_tx.send(Ok(html)).await.unwrap();
}
Err(e) => {
let mut context = water_quality_context.lock().await;
context.insert("error", &e.to_string());
context.insert("error_type", &"beach status");
context.insert("container", &"water-quality-container");
context.insert("error_container", &"water-quality-error");
water_quality_tx
.send(Ok(TEMPLATES.render("error.html", &context).unwrap()))
.send(Ok(html!((error_markup("water quality", e))).into()))
.await
.unwrap();
}
Expand All @@ -103,22 +113,21 @@ pub async fn root(
tokio::spawn(async move {
match Forecast::try_get(&spot, state.forecast_url).await {
Ok(forecast) => {
let mut context = context.lock().await;
context.insert("forecast_json", &serde_json::to_string(&forecast).unwrap());
let html = html!(
script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js" {}
script type="application/json" id="forecast-data" {(
PreEscaped(
serde_json::to_string(&forecast).unwrap())
)}
)
.into();

tx.send(Ok(TEMPLATES.render("forecast.html", &context).unwrap()))
.await
.unwrap();
tx.send(Ok(html)).await.unwrap();

Ok(())
}
Err(e) => {
let mut context = context.lock().await;
context.insert("error", &e.to_string());
context.insert("error_type", &"forecast");
context.insert("container", &"forecast-container");
context.insert("error_container", &"forecast-error");
tx.send(Ok(TEMPLATES.render("error.html", &context).unwrap()))
tx.send(Ok(html!((error_markup("forecast", e))).into()))
.await
.unwrap();

Expand Down
2 changes: 1 addition & 1 deletion tailwind.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/** @type {import('tailwindcss').Config} */

module.exports = {
content: ["./templates/**/*.{html,js}"],
content: ["./templates/**/*.{html,js}", "./src/routes/**"],
};
8 changes: 4 additions & 4 deletions templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@
<link href="https://api.fontshare.com/css?f[]=satoshi&display=swap" rel="stylesheet">
<link href="https://api.fontshare.com/css?f[]=array&display=swap" rel="stylesheet">

<link rel="preload" href="/assets/styles.css?version=93" as="style" />
<link rel="preload" href="/assets/styles.css?version=101" as="style" />
<link
rel="preload"
href="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"
as="script"
/>
<link
rel="preload"
href="/assets/static/index.min.js?version=96"
href="/assets/static/index.min.js?version=104"
as="script"
/>

<link
href="/assets/styles.css?version=93"
href="/assets/styles.css?version=101"
rel="stylesheet"
type="text/css"
/>
Expand All @@ -46,7 +46,7 @@
@keyup.escape="showLiveFeed = false; showNav = false;"
:class="{ 'overflow-hidden': showNav || showLiveFeed }"
>
<script src="/assets/static/index.min.js?version=96"></script>
<script src="/assets/static/index.min.js?version=104"></script>
{% block body %} {% endblock %} {% include "includes/footer.html" %} {% if
live_reload %}
<script>
Expand Down
11 changes: 0 additions & 11 deletions templates/error.html

This file was deleted.

Loading

0 comments on commit 381cdfa

Please sign in to comment.