fix: recursive bloat in html
the meta, title and style tags are being replicated in the list on '/'
This commit is contained in:
parent
76d27dfcec
commit
aaaed7ce62
@ -10,6 +10,7 @@ chrono = { version = "0.4.41", features = ["serde"] }
|
|||||||
serde = { version = "1.0.219", features = ["derive", "rc"] }
|
serde = { version = "1.0.219", features = ["derive", "rc"] }
|
||||||
serde_json = "1.0.140"
|
serde_json = "1.0.140"
|
||||||
tokio = { version = "1.45.1", features = ["full", "tracing"] }
|
tokio = { version = "1.45.1", features = ["full", "tracing"] }
|
||||||
|
tower-http = { version = "0.6.6", features = ["trace"] }
|
||||||
tracing = "0.1.41"
|
tracing = "0.1.41"
|
||||||
tracing-subscriber = "0.3.19"
|
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
|
||||||
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
uuid = { version = "1.17.0", features = ["serde", "v4"] }
|
||||||
|
54
src/lib.rs
54
src/lib.rs
@ -3,39 +3,75 @@ use askama::Template;
|
|||||||
use axum::{
|
use axum::{
|
||||||
Router,
|
Router,
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
|
http::HeaderMap,
|
||||||
http::StatusCode,
|
http::StatusCode,
|
||||||
response::{Html, IntoResponse},
|
response::{Html, IntoResponse},
|
||||||
routing::get,
|
routing::get,
|
||||||
serve,
|
serve,
|
||||||
};
|
};
|
||||||
use person::PersonID;
|
use person::{PersonID, PersonTemplate, PersonTemplateFull};
|
||||||
use stammbaum::Stammbaum;
|
use stammbaum::{Stammbaum, StammbaumFull};
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
use tower_http::trace::TraceLayer;
|
||||||
|
use tracing::{error, instrument, warn};
|
||||||
|
|
||||||
mod person;
|
mod person;
|
||||||
mod stammbaum;
|
mod stammbaum;
|
||||||
|
#[instrument(skip(stammbaum))]
|
||||||
async fn get_person(
|
async fn get_person(
|
||||||
Path(id): Path<String>,
|
Path(id): Path<String>,
|
||||||
|
headers: HeaderMap,
|
||||||
State(stammbaum): State<Stammbaum>,
|
State(stammbaum): State<Stammbaum>,
|
||||||
) -> Result<impl IntoResponse, impl IntoResponse> {
|
) -> Result<impl IntoResponse, impl IntoResponse> {
|
||||||
let id = match PersonID::try_from(id) {
|
let id = match PersonID::try_from(id) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(_) => return Err(StatusCode::INTERNAL_SERVER_ERROR),
|
Err(e) => {
|
||||||
|
error!(error = ?e);
|
||||||
|
return Err(StatusCode::INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
match stammbaum.get(id) {
|
match stammbaum.get(id) {
|
||||||
None => Err(StatusCode::NOT_FOUND),
|
None => {
|
||||||
Some(p) => Ok(Html(
|
warn!(id = ?id, "not_found");
|
||||||
p.render().map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?,
|
Err(StatusCode::NOT_FOUND)
|
||||||
|
}
|
||||||
|
Some(p) => match headers.get("HX-Request") {
|
||||||
|
Some(_) => Ok(Html(PersonTemplate::from(p.to_owned()).render().map_err(
|
||||||
|
|e| {
|
||||||
|
error!(error=?e, id = ?id);
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
},
|
||||||
|
)?)),
|
||||||
|
_ => Ok(Html(
|
||||||
|
PersonTemplateFull::from(p.to_owned())
|
||||||
|
.render()
|
||||||
|
.map_err(|e| {
|
||||||
|
error!(error=?e, id = ?id);
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR
|
||||||
|
})?,
|
||||||
)),
|
)),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument]
|
||||||
async fn list_stammbaum(
|
async fn list_stammbaum(
|
||||||
|
headers: HeaderMap,
|
||||||
State(stammbaum): State<Stammbaum>,
|
State(stammbaum): State<Stammbaum>,
|
||||||
) -> Result<impl IntoResponse, impl IntoResponse> {
|
) -> Result<impl IntoResponse, impl IntoResponse> {
|
||||||
Ok::<axum::response::Html<std::string::String>, StatusCode>(Html(stammbaum.render()))
|
match headers.get("HX-Request") {
|
||||||
|
Some(_) => Ok::<axum::response::Html<std::string::String>, StatusCode>(Html(
|
||||||
|
stammbaum
|
||||||
|
.render()
|
||||||
|
.expect("should have rendered the template"),
|
||||||
|
)),
|
||||||
|
None => Ok::<axum::response::Html<std::string::String>, StatusCode>(Html(
|
||||||
|
StammbaumFull::from(stammbaum)
|
||||||
|
.render()
|
||||||
|
.expect("should have rendered the template"),
|
||||||
|
)),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run() -> Result<(), String> {
|
pub fn run() -> Result<(), String> {
|
||||||
@ -50,7 +86,9 @@ pub fn run() -> Result<(), String> {
|
|||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(list_stammbaum))
|
.route("/", get(list_stammbaum))
|
||||||
.route("/{id}", get(get_person))
|
.route("/{id}", get(get_person))
|
||||||
|
.layer(TraceLayer::new_for_http())
|
||||||
.with_state(stammbaum);
|
.with_state(stammbaum);
|
||||||
|
|
||||||
let addr = tokio::net::TcpListener::bind("0.0.0.0:3000")
|
let addr = tokio::net::TcpListener::bind("0.0.0.0:3000")
|
||||||
.await
|
.await
|
||||||
.expect("faild to bind port");
|
.expect("faild to bind port");
|
||||||
|
10
src/main.rs
10
src/main.rs
@ -1,5 +1,13 @@
|
|||||||
use stammbaum::run;
|
use stammbaum::run;
|
||||||
fn main() {
|
use tracing_subscriber::EnvFilter;
|
||||||
#[allow(clippy::unwrap_used)]
|
#[allow(clippy::unwrap_used)]
|
||||||
|
fn main() {
|
||||||
|
tracing_subscriber::fmt()
|
||||||
|
.with_env_filter(
|
||||||
|
EnvFilter::try_from_default_env()
|
||||||
|
.or_else(|_| EnvFilter::try_new("stammbaum=debug"))
|
||||||
|
.unwrap(),
|
||||||
|
)
|
||||||
|
.init();
|
||||||
run().unwrap();
|
run().unwrap();
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,30 @@ use uuid::Uuid;
|
|||||||
|
|
||||||
pub type PersonID = Uuid;
|
pub type PersonID = Uuid;
|
||||||
|
|
||||||
#[derive(Template, Clone, Debug, Serialize, Deserialize)]
|
#[derive(Template)]
|
||||||
#[template(path = "person.html")]
|
#[template(path = "person.html")]
|
||||||
|
pub struct PersonTemplate {
|
||||||
|
person: Person,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Person> for PersonTemplate {
|
||||||
|
fn from(value: Person) -> Self {
|
||||||
|
Self { person: value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "person_full.html")]
|
||||||
|
pub struct PersonTemplateFull {
|
||||||
|
person: Person,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Person> for PersonTemplateFull {
|
||||||
|
fn from(value: Person) -> Self {
|
||||||
|
Self { person: value }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
pub struct Person {
|
pub struct Person {
|
||||||
id: PersonID,
|
id: PersonID,
|
||||||
first_name: Arc<str>,
|
first_name: Arc<str>,
|
||||||
@ -44,6 +66,29 @@ impl Person {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn sex(&self) -> &Sex {
|
||||||
|
&self.sex
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn date_of_birth(&self) -> DateTime<Utc> {
|
||||||
|
self.date_of_birth
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn maiden_name(&self) -> Option<Arc<str>> {
|
||||||
|
self.maiden_name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn last_name(&self) -> Arc<str> {
|
||||||
|
self.last_name.clone()
|
||||||
|
}
|
||||||
|
pub fn first_name(&self) -> Arc<str> {
|
||||||
|
self.first_name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parents(&self) -> &Vec<PersonID> {
|
||||||
|
&self.parents
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_parent(&mut self, parent: PersonID) {
|
pub fn add_parent(&mut self, parent: PersonID) {
|
||||||
self.parents.push(parent);
|
self.parents.push(parent);
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,29 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::person::{Person, PersonID};
|
use crate::person::{Person, PersonID, Sex};
|
||||||
|
|
||||||
use askama::Template;
|
use askama::Template;
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
#[derive(Clone, Debug, Serialize, Deserialize, Template)]
|
||||||
|
#[template(path = "stammbaum.html")]
|
||||||
pub struct Stammbaum {
|
pub struct Stammbaum {
|
||||||
members: Vec<Person>,
|
members: Vec<Person>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Template)]
|
||||||
|
#[template(path = "stammbaum_full.html")]
|
||||||
|
pub struct StammbaumFull {
|
||||||
|
members: Vec<Person>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Stammbaum> for StammbaumFull {
|
||||||
|
fn from(value: Stammbaum) -> Self {
|
||||||
|
Self {
|
||||||
|
members: value.members,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
impl Stammbaum {
|
impl Stammbaum {
|
||||||
pub fn new(members: Vec<Person>) -> Self {
|
pub fn new(members: Vec<Person>) -> Self {
|
||||||
@ -18,13 +33,6 @@ impl Stammbaum {
|
|||||||
pub fn get(&self, id: PersonID) -> Option<&Person> {
|
pub fn get(&self, id: PersonID) -> Option<&Person> {
|
||||||
self.members.iter().find(|p| p.id() == id)
|
self.members.iter().find(|p| p.id() == id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&self) -> String {
|
|
||||||
self.members
|
|
||||||
.iter()
|
|
||||||
.flat_map(|p: &Person| p.render())
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mod test {
|
mod test {
|
||||||
|
@ -1,31 +1,28 @@
|
|||||||
{% extends "_layout.html" %}
|
|
||||||
|
|
||||||
{%- block content -%}
|
|
||||||
<div class="family-node">
|
<div class="family-node">
|
||||||
<div class="name">
|
<div class="name">
|
||||||
<span class="first-name">{{ first_name }}</span>
|
<span class="first-name">{{ person.first_name }}</span>
|
||||||
<span class="last-name">{{ last_name }}</span>
|
<span class="last-name">{{ person.last_name }}</span>
|
||||||
{% if maiden_name.is_some() %}
|
{% if person.maiden_name.is_some() %}
|
||||||
<span class="maiden-name">({{ maiden_name.as_ref().unwrap() }})</span>
|
<span class="maiden-name">({{ person.maiden_name.as_ref().unwrap() }})</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<div>Sex:
|
<div>Sex:
|
||||||
{% match sex %}
|
{% match person.sex %}
|
||||||
{% when Sex::Male %}Male
|
{% when Sex::Male %}Male
|
||||||
{% when Sex::Female %}Female
|
{% when Sex::Female %}Female
|
||||||
{% when Sex::Other(desc) %}{{ desc }}
|
{% when Sex::Other(desc) %}{{ desc }}
|
||||||
{% endmatch %}
|
{% endmatch %}
|
||||||
</div>
|
</div>
|
||||||
<div>Birthday: {{ date_of_birth.format("%Y-%m-%d") }}</div>
|
<div>Birthday: {{ person.date_of_birth.format("%Y-%m-%d") }}</div>
|
||||||
<a href="/{{ id }}">ID: {{ id }}</a>
|
<a href="/{{ person.id }}">ID: {{ person.id }}</a>
|
||||||
<div class="parents">
|
<div class="parents">
|
||||||
Parents:
|
Parents:
|
||||||
{% if parents.is_empty() %}
|
{% if person.parents.is_empty() %}
|
||||||
<ul></ul>
|
<ul></ul>
|
||||||
{% else %}
|
{% else %}
|
||||||
<ul>
|
<ul>
|
||||||
{% for parent_id in &parents %}
|
{% for parent_id in &person.parents %}
|
||||||
<li>
|
<li>
|
||||||
<a href="/{{ parent_id }}">{{ parent_id }}</a>
|
<a href="/{{ parent_id }}">{{ parent_id }}</a>
|
||||||
|
|
||||||
@ -35,4 +32,3 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{%- endblock -%}
|
|
||||||
|
38
templates/person_full.html
Normal file
38
templates/person_full.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{% extends "_layout.html" %}
|
||||||
|
|
||||||
|
{%- block content -%}
|
||||||
|
<div class="family-node">
|
||||||
|
<div class="name">
|
||||||
|
<span class="first-name">{{ person.first_name }}</span>
|
||||||
|
<span class="last-name">{{ person.last_name }}</span>
|
||||||
|
{% if person.maiden_name.is_some() %}
|
||||||
|
<span class="maiden-name">({{ person.maiden_name.as_ref().unwrap() }})</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="details">
|
||||||
|
<div>Sex:
|
||||||
|
{% match person.sex %}
|
||||||
|
{% when Sex::Male %}Male
|
||||||
|
{% when Sex::Female %}Female
|
||||||
|
{% when Sex::Other(desc) %}{{ desc }}
|
||||||
|
{% endmatch %}
|
||||||
|
</div>
|
||||||
|
<div>Birthday: {{ person.date_of_birth.format("%Y-%m-%d") }}</div>
|
||||||
|
<a href="/{{ person.id }}">ID: {{ person.id }}</a>
|
||||||
|
<div class="parents">
|
||||||
|
Parents:
|
||||||
|
{% if person.parents.is_empty() %}
|
||||||
|
<ul></ul>
|
||||||
|
{% else %}
|
||||||
|
<ul>
|
||||||
|
{% for parent_id in &person.parents %}
|
||||||
|
<li>
|
||||||
|
<a href="/{{ parent_id }}">{{ parent_id }}</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{%- endblock -%}
|
36
templates/stammbaum.html
Normal file
36
templates/stammbaum.html
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{% for person in members %}
|
||||||
|
<div class="family-node">
|
||||||
|
<div class="name">
|
||||||
|
<span class="first-name">{{ person.first_name() }}</span>
|
||||||
|
<span class="last-name">{{ person.last_name() }}</span>
|
||||||
|
{% if person.maiden_name().is_some() %}
|
||||||
|
<span class="maiden-name">({{ person.maiden_name().as_ref().unwrap() }})</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="details">
|
||||||
|
<div>Sex:
|
||||||
|
{% match person.sex() %}
|
||||||
|
{% when Sex::Male %}Male
|
||||||
|
{% when Sex::Female %}Female
|
||||||
|
{% when Sex::Other(desc) %}{{ desc }}
|
||||||
|
{% endmatch %}
|
||||||
|
</div>
|
||||||
|
<div>Birthday: {{ person.date_of_birth().format("%Y-%m-%d") }}</div>
|
||||||
|
<a href="/{{ person.id() }}">ID: {{ person.id() }}</a>
|
||||||
|
<div class="parents">
|
||||||
|
Parents:
|
||||||
|
{% if person.parents().is_empty() %}
|
||||||
|
<ul></ul>
|
||||||
|
{% else %}
|
||||||
|
<ul>
|
||||||
|
{% for parent_id in &person.parents() %}
|
||||||
|
<li>
|
||||||
|
<a href="/{{ parent_id }}">{{ parent_id }}</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
40
templates/stammbaum_full.html
Normal file
40
templates/stammbaum_full.html
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{% extends "_layout.html" %}
|
||||||
|
|
||||||
|
{%- block content -%}
|
||||||
|
{% for person in members %}
|
||||||
|
<div class="family-node">
|
||||||
|
<div class="name">
|
||||||
|
<span class="first-name">{{ person.first_name() }}</span>
|
||||||
|
<span class="last-name">{{ person.last_name() }}</span>
|
||||||
|
{% if person.maiden_name().is_some() %}
|
||||||
|
<span class="maiden-name">({{ person.maiden_name().as_ref().unwrap() }})</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="details">
|
||||||
|
<div>Sex:
|
||||||
|
{% match person.sex() %}
|
||||||
|
{% when Sex::Male %}Male
|
||||||
|
{% when Sex::Female %}Female
|
||||||
|
{% when Sex::Other(desc) %}{{ desc }}
|
||||||
|
{% endmatch %}
|
||||||
|
</div>
|
||||||
|
<div>Birthday: {{ person.date_of_birth().format("%Y-%m-%d") }}</div>
|
||||||
|
<a href="/{{ person.id() }}">ID: {{ person.id() }}</a>
|
||||||
|
<div class="parents">
|
||||||
|
Parents:
|
||||||
|
{% if person.parents().is_empty() %}
|
||||||
|
<ul></ul>
|
||||||
|
{% else %}
|
||||||
|
<ul>
|
||||||
|
{% for parent_id in &person.parents() %}
|
||||||
|
<li>
|
||||||
|
<a href="/{{ parent_id }}">{{ parent_id }}</a>
|
||||||
|
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{%- endblock -%}
|
Loading…
x
Reference in New Issue
Block a user