hello world

This commit is contained in:
thatscringebro
2026-05-05 21:21:30 -04:00
parent 26d57f742d
commit 990c10c75f
4 changed files with 112 additions and 0 deletions

2
.gitignore vendored
View File

@@ -16,3 +16,5 @@ target/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
Cargo.lock

12
Cargo.toml Normal file
View File

@@ -0,0 +1,12 @@
[package]
name = "git-ui"
version = "0.1.0"
edition = "2024"
[dependencies]
askama = "0.16.0"
axum = "0.8.9"
tokio = { version = "1.52.2", features = ["full"] }
tower-http = { version = "0.6.9", features = ["trace"] }
tracing = "0.1.44"
tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }

96
src/main.rs Normal file
View File

@@ -0,0 +1,96 @@
use askama::Template;
use axum::{
Router,
body::Bytes,
extract::{self, MatchedPath},
http::{HeaderMap, Request, StatusCode},
response::{Html, IntoResponse, Response},
routing::get,
};
use tokio::time::Duration;
use tower_http::{classify::ServerErrorsFailureClass, trace::TraceLayer};
use tracing::{Span, info_span};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() {
tracing_subscriber::registry()
.with(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| format!("{}=debug", env!("CARGO_CRATE_NAME")).into()),
)
.with(tracing_subscriber::fmt::layer())
.init();
let app = app();
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
tracing::debug!("listening on {}", listener.local_addr().unwrap());
let _ = axum::serve(listener, app).await;
}
fn app() -> Router {
Router::new().route("/greet/{name}", get(greet)).layer(
TraceLayer::new_for_http()
.make_span_with(|request: &Request<_>| {
let matched_path = request
.extensions()
.get::<MatchedPath>()
.map(MatchedPath::as_str);
let path = &request.uri().to_string();
info_span!(
"http_request",
method = ?request.method(),
matched_path,
path,
some_other_field = tracing::field::Empty,
)
})
.on_request(|_request: &Request<_>, _span: &Span| {
tracing::debug!("started processing request")
})
.on_response(|_response: &Response, _latency: Duration, _span: &Span| {
tracing::debug!("finished processing request")
})
.on_body_chunk(|_chunk: &Bytes, _latency: Duration, _span: &Span| {
tracing::debug!("sending body chunk")
})
.on_eos(
|_trailers: Option<&HeaderMap>, _stream_duration: Duration, _span: &Span| {
tracing::debug!("stream closed")
},
)
.on_failure(
|_error: ServerErrorsFailureClass, _latency: Duration, _span: &Span| {
tracing::error!("something went wrong")
},
),
)
}
async fn greet(extract::Path(name): extract::Path<String>) -> impl IntoResponse {
let template = HelloTemplate { name };
HtmlTemplate(template)
}
#[derive(Template)]
#[template(path = "hello.html")]
struct HelloTemplate {
name: String,
}
struct HtmlTemplate<T>(T);
impl<T> IntoResponse for HtmlTemplate<T>
where
T: Template,
{
fn into_response(self) -> Response {
match self.0.render() {
Ok(html) => Html(html).into_response(),
Err(err) => (
StatusCode::INTERNAL_SERVER_ERROR,
format!("Failed to render template. Error: {err}"),
)
.into_response(),
}
}
}

2
templates/hello.html Normal file
View File

@@ -0,0 +1,2 @@
<h1>Hello, {{ name }}!</h1>