use rama::{
Context, Layer,
http::{
layer::{compression::CompressionLayer, trace::TraceLayer},
matcher::HttpMatcher,
server::HttpServer,
service::web::WebService,
service::web::response::{Html, Redirect},
},
net::stream::{SocketInfo, matcher::SocketMatcher},
rt::Executor,
};
use std::sync::Arc;
use std::sync::atomic::AtomicU64;
use std::sync::atomic::Ordering;
use tracing::level_filters::LevelFilter;
use tracing_subscriber::layer::SubscriberExt;
use tracing_subscriber::util::SubscriberInitExt;
use tracing_subscriber::{EnvFilter, fmt};
#[derive(Debug, Default)]
struct AppState {
counter: AtomicU64,
}
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(fmt::layer())
.with(
EnvFilter::builder()
.with_default_directive(LevelFilter::DEBUG.into())
.from_env_lossy(),
)
.init();
let addr = "0.0.0.0:62013";
tracing::info!("running service at: {addr}");
let exec = Executor::default();
HttpServer::auto(exec)
.listen_with_state(
Arc::new(AppState::default()),
addr,
(TraceLayer::new_for_http(), CompressionLayer::new()).into_layer(
WebService::default()
.not_found(Redirect::temporary("/error.html"))
.get("/coin", coin_page)
.post("/coin", async |ctx: Context<Arc<AppState>>| {
ctx.state().counter.fetch_add(1, Ordering::AcqRel);
coin_page(ctx).await
})
.on(
HttpMatcher::get("/home").and_socket(SocketMatcher::loopback()),
Html("Home Sweet Home!".to_owned()),
)
.dir("/", "test-files/examples/webservice"),
),
)
.await
.unwrap();
}
async fn coin_page(ctx: Context<Arc<AppState>>) -> Html<String> {
let emoji = if ctx
.get::<SocketInfo>()
.unwrap()
.peer_addr()
.ip()
.is_loopback()
{
r#"<a href="/home">🏠</a>"#
} else {
"🌍"
};
let count = ctx.state().counter.load(Ordering::Acquire);
Html(format!(
r#"
<!DOCTYPE html>
<html>
<head>
<title>Coin Clicker</title>
<link rel="stylesheet" href="/style/reset.css">
<link rel="icon" href="/favicon.png" type="image/x-icon">
<style>
body {{
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
flex-direction: column;
text-align: center;
}}
footer {{
position: absolute;
bottom: 0;
width: 100%;
text-align: center;
}}
</style>
</head>
<body>
<h2>{emoji} Coin Clicker</h2>
<h1 id="coinCount">{count}</h1>
<p>Click the button for more coins.</p>
<form action="/coin" method="post">
<button type="submit">💰 Click</button>
</form>
<footer>
<p>
See <a href="/legal.html">the legal page</a> for more information on your rights.
</p>
</footer>
</body>
</html>
"#
))
}