From 9f4067e94cd574f9ccf10e9c93534d74a026bcfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabor=20K=C3=B6rber?= Date: Fri, 26 Jan 2024 11:26:47 +0100 Subject: [PATCH] code: upgrade to axum 0.7, working on repo info system --- Cargo.lock | 238 ++++++++++++++++++++----- Cargo.toml | 8 +- src/admin/domain.rs | 27 ++- src/admin/example.rs | 49 +++-- src/admin/views.rs | 6 +- src/main.rs | 9 +- src/service/handlers.rs | 4 +- src/service/templates.rs | 71 ++++++++ templates/admin/items/item_list.jinja | 40 +++-- templates/{base.html.j2 => base.jinja} | 3 + templates/index.html | 16 +- 11 files changed, 389 insertions(+), 82 deletions(-) rename templates/{base.html.j2 => base.jinja} (92%) diff --git a/Cargo.lock b/Cargo.lock index 6ede3c5..079d20e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.5" +version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d664a92ecae85fd0a7392615844904654d1d5f5514837f471ddef4a057aba1b6" +checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5" dependencies = [ "anstyle", "anstyle-parse", @@ -364,13 +364,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" dependencies = [ "async-trait", - "axum-core", + "axum-core 0.3.4", "bitflags 1.3.2", "bytes", "futures-util", - "http", - "http-body", - "hyper", + "http 0.2.9", + "http-body 0.4.5", + "hyper 0.14.27", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "axum" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1236b4b292f6c4d6dc34604bb5120d85c3fe1d1aa596bd5cc52ca054d13e7b9e" +dependencies = [ + "async-trait", + "axum-core 0.4.3", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "hyper 1.1.0", + "hyper-util", "itoa", "matchit", "memchr", @@ -387,6 +417,7 @@ dependencies = [ "tower", "tower-layer", "tower-service", + "tracing", ] [[package]] @@ -398,14 +429,35 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http", - "http-body", + "http 0.2.9", + "http-body 0.4.5", "mime", "rustversion", "tower-layer", "tower-service", ] +[[package]] +name = "axum-core" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15c63fd72d41492dc4f497196f5da1fb04fb7529e631d73630d1b491e47a2e3" +dependencies = [ + "async-trait", + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] + [[package]] name = "backtrace" version = "0.3.69" @@ -821,16 +873,26 @@ dependencies = [ ] [[package]] -name = "env_logger" -version = "0.10.1" +name = "env_filter" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" dependencies = [ - "humantime", - "is-terminal", "log", "regex", - "termcolor", +] + +[[package]] +name = "env_logger" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eeb342678d785662fd2514be38c459bb925f02b68dd2a3e0f21d7ef82d979dd" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "humantime", + "log", ] [[package]] @@ -1158,6 +1220,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "h2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d030e59af851932b72ceebadf4a2b5986dba4c3b99dd2493f8273a0f151943" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 1.0.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.12.3" @@ -1245,6 +1326,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.5" @@ -1252,7 +1344,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", - "http", + "http 0.2.9", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" +dependencies = [ + "bytes", + "http 1.0.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cb79eb393015dadd30fc252023adb0b2400a0caee0fa2a077e6e21a551e840" +dependencies = [ + "bytes", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", "pin-project-lite", ] @@ -1284,8 +1399,8 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http", - "http-body", + "http 0.2.9", + "http-body 0.4.5", "httparse", "httpdate", "itoa", @@ -1297,6 +1412,43 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5aa53871fc917b1a9ed87b683a5d86db645e23acb32c2e0785a353e522fb75" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http 1.0.0", + "http-body 1.0.0", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "hyper-util" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdea9aac0dbe5a9240d68cfd9501e2db94222c6dc06843e06640b9e07f0fdc67" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.0.0", + "http-body 1.0.0", + "hyper 1.1.0", + "pin-project-lite", + "socket2 0.5.4", + "tokio", + "tracing", +] + [[package]] name = "iana-time-zone" version = "0.1.58" @@ -1391,17 +1543,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "is-terminal" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" -dependencies = [ - "hermit-abi", - "rustix 0.38.28", - "windows-sys 0.52.0", -] - [[package]] name = "itertools" version = "0.11.0" @@ -1581,9 +1722,9 @@ dependencies = [ [[package]] name = "minijinja" -version = "1.0.8" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80084fa3099f58b7afab51e5f92e24c2c2c68dcad26e96ad104bd6011570461d" +checksum = "431d72874542d43aba1ca605870eacab134fdeb0c8fe27666ecf4b2662239df2" dependencies = [ "memo-map", "percent-encoding", @@ -1612,7 +1753,7 @@ name = "miniweb" version = "0.1.0" dependencies = [ "anyhow", - "axum", + "axum 0.7.4", "barrel", "dotenvy", "dunce", @@ -1630,6 +1771,7 @@ dependencies = [ "serde_json", "slug", "sqlformat", + "strinto", "tokio", ] @@ -2279,7 +2421,7 @@ version = "8.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1e7d90385b59f0a6bf3d3b757f3ca4ece2048265d70db20a2016043d4509a40" dependencies = [ - "axum", + "axum 0.6.20", "rust-embed-impl", "rust-embed-utils", "tokio", @@ -3013,6 +3155,15 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strinto" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.37", +] + [[package]] name = "strsim" version = "0.10.0" @@ -3090,15 +3241,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "termcolor" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449" -dependencies = [ - "winapi-util", -] - [[package]] name = "thiserror" version = "1.0.51" @@ -3214,6 +3356,20 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-util" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + [[package]] name = "toml_datetime" version = "0.6.3" diff --git a/Cargo.toml b/Cargo.toml index 3c82a50..523a659 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ use_barrel = ["barrel", "sqlformat"] default = ["use_barrel"] [dependencies] +strinto = { path = "./strinto" } entity = { path = "./entity" } sea-orm = { version = "0.12.10", features = [ "runtime-tokio-native-tls", @@ -24,14 +25,15 @@ sea-orm = { version = "0.12.10", features = [ ] } sqlformat = { version = "0.2.2", optional = true } anyhow = "1.0.75" -axum = "0.6.20" +axum = "0.7" barrel = { version = "0.7.0", optional = true, features = ["pg"] } dotenvy = "0.15.7" mime_guess = "2.0.4" -minijinja = { version = "1.0.8", features = [ +minijinja = { version = "1.0.11", features = [ "loader", "builtins", "urlencode", + "deserialization", ] } minijinja-autoreload = "1.0.8" once_cell = "1.18.0" @@ -45,6 +47,6 @@ serde = { version = "1.0.188", features = ["derive"] } tokio = { version = "1.32.0", features = ["full"] } dunce = "1.0.4" log = "0.4.20" -env_logger = "0.10.1" +env_logger = "0.11" serde_json = "1.0.108" slug = "0.1.5" diff --git a/src/admin/domain.rs b/src/admin/domain.rs index 86df10b..f908211 100644 --- a/src/admin/domain.rs +++ b/src/admin/domain.rs @@ -1,7 +1,7 @@ pub use config::AdminModelConfig; pub use dto::AdminApp; pub use dto::AdminModel; -pub use repository::{AdminRepository, RepositoryList}; +pub use repository::{AdminRepository, RepositoryInfo, RepositoryList}; mod auth { @@ -95,8 +95,33 @@ pub mod repository { } } + // each repository has to implement a repo info. + #[derive(Serialize)] + pub struct RepositoryInfo { + name: String, + lookup_key: String, + display_list: Vec, + } + + impl RepositoryInfo { + pub fn new(name: &str, lookup_key: &str) -> Self { + RepositoryInfo { + name: name.to_owned(), + lookup_key: lookup_key.to_owned(), + display_list: vec![], + } + } + + pub fn display_list(mut self, display_list: &[&str]) -> RepositoryInfo { + self.display_list = display_list.iter().map(|&e| e.to_string()).collect(); + self + } + } + pub trait AdminRepository: Send + Sync { fn get_item(&self, id: usize) -> Option; + + fn get_repo_info(&self) -> RepositoryInfo; fn get_list(&self) -> RepositoryList; } } diff --git a/src/admin/example.rs b/src/admin/example.rs index b8e629d..8f8236a 100644 --- a/src/admin/example.rs +++ b/src/admin/example.rs @@ -1,32 +1,49 @@ // implementation of static repository -use super::domain::{AdminModelConfig, AdminRepository, RepositoryList}; +use super::domain::{AdminModelConfig, AdminRepository, RepositoryInfo, RepositoryList}; use super::state::AdminRegistry; use serde_json::{json, Value}; -struct MyStaticRepository {} +struct MyStaticRepository { + content: Vec, +} -impl AdminRepository for MyStaticRepository { - fn get_item(&self, id: usize) -> Option { - Some(json!({ - "name": "Adam", - "age": id, - })) - } - - fn get_list(&self) -> RepositoryList { - RepositoryList::List { - values: vec![ - json!({"name": "Strange", "age": 150 }), - json!({"name": "Adam", "age": 12}), +impl MyStaticRepository { + pub fn new() -> Self { + MyStaticRepository { + content: vec![ + json!({"id": 1, "name": "Strange", "age": 150, "level": "master" }), + json!({"id": 2, "name": "Adam", "age": 12, "powers": 8}), + json!({"id": 3, "name": "Tony", "age": 42, "powers": 0}), + json!({"id": 4, "name": "Rex", "age": 72}), + json!({"id": 5, "name": "Justin", "age": 46}), + json!({"id": 6, "name": "Reacher", "age": 39, "level": "adept", "powers": 0}), + json!({"id": 7, "name": "Arnold", "age": 64}), ], } } } +impl AdminRepository for MyStaticRepository { + fn get_item(&self, id: usize) -> Option { + self.content.get(id).cloned() + } + + fn get_list(&self) -> RepositoryList { + RepositoryList::List { + values: self.content.clone(), + } + } + + fn get_repo_info(&self) -> RepositoryInfo { + RepositoryInfo::new("My Static Repository", "id") + .display_list(&["id", "name", "age", "level"]) + } +} + pub fn register_example(registry: &mut AdminRegistry) { let app_key = registry.register_app("Example App"); - let repo = MyStaticRepository {}; + let repo = MyStaticRepository::new(); let model_config = AdminModelConfig { app_key: app_key, name: "ExampleModel".to_owned(), diff --git a/src/admin/views.rs b/src/admin/views.rs index 919ff56..6dd448c 100644 --- a/src/admin/views.rs +++ b/src/admin/views.rs @@ -10,7 +10,7 @@ use crate::service::templates; use serde::{Deserialize, Serialize}; use serde_json::Value; -use super::domain::RepositoryList; +use super::domain::{RepositoryInfo, RepositoryList}; #[derive(Deserialize)] pub struct Question { @@ -43,6 +43,7 @@ pub struct AdminContext { pub request: AdminRequest, pub available_apps: Vec, + pub item_info: Option, pub item_list: RepositoryList, pub item: Option, } @@ -64,6 +65,7 @@ impl Default for AdminContext { request: AdminRequest { path: "".to_owned(), }, + item_info: None, item_list: RepositoryList::Empty, item: None, } @@ -106,6 +108,8 @@ pub async fn list_item_collection( // we should consider using Vec instead in get_list. AdminContext { available_apps: registry.get_apps(), + content: model_key.to_owned(), + item_info: Some(repo.get_repo_info()), item_list: repo.get_list(), ..Default::default() } diff --git a/src/main.rs b/src/main.rs index c31452f..e956f69 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,7 @@ use log::info; use std::env; use std::net::SocketAddr; use std::sync::Arc; +use tokio::net::TcpListener; async fn home(templates: State) -> impl IntoResponse { templates.render_html("index.html", ()) @@ -70,11 +71,13 @@ async fn main() { }; info!("listening on {}", listen_addr); info!("admin on: http://{}/admin", server_addr); - axum::Server::bind(&listen_addr) - .serve(app.into_make_service()) + let listener = TcpListener::bind(&listen_addr) + .await + .expect("Could not bind TCP Listener."); + axum::serve(listener, app.into_make_service()) .with_graceful_shutdown(shutdown_signal()) .await - .unwrap(); + .expect("Could not start serving Axum"); } async fn shutdown_signal() { diff --git a/src/service/handlers.rs b/src/service/handlers.rs index 4be456c..8f0a1ab 100644 --- a/src/service/handlers.rs +++ b/src/service/handlers.rs @@ -1,7 +1,7 @@ /* Default handlers */ use crate::service::templates::Templates; use axum::{ - body::{boxed, Full}, + body::Body, extract::State, http::{header, StatusCode, Uri}, response::{IntoResponse, Response}, @@ -55,7 +55,7 @@ where let path: &str = self.path.as_ref(); match E::get(path) { Some(content) => { - let body = boxed(Full::from(content.data)); + let body = Body::from(content.data); let mime = mime_guess::from_path(path).first_or_octet_stream(); Response::builder() .header(header::CONTENT_TYPE, mime.as_ref()) diff --git a/src/service/templates.rs b/src/service/templates.rs index db16069..9118de3 100644 --- a/src/service/templates.rs +++ b/src/service/templates.rs @@ -1,8 +1,10 @@ use crate::service::error::Error; use axum::response::Html; +use minijinja::value::ValueKind; use minijinja::{path_loader, Environment, Value}; use minijinja_autoreload::AutoReloader; use pulldown_cmark::Event; +use serde::Deserializer; use std::sync::Arc; #[derive(Clone)] @@ -19,6 +21,7 @@ impl Templates { environment.add_filter("none", none); environment.add_filter("markdown", markdown); environment.add_filter("yesno", filter_yesno); + environment.add_filter("table_keys", collect_unique_keys); environment.add_function("static", tpl_static); environment.add_function("url", tpl_url); environment.add_function("csrf_token", tpl_to_be_implemented); @@ -93,3 +96,71 @@ pub fn none(value: Value, other: Option) -> Value { value } } + +fn tpl_table(values: Vec) -> Value { + Value::from_safe_string(format!( + "Output: {}", + values.first().expect("No values found.").kind() + )) +} + +mod helper { + use std::collections::HashSet; + + pub struct OrderedSet { + set: HashSet, + vec: Vec, + } + + impl OrderedSet { + pub fn new() -> Self { + OrderedSet { + set: HashSet::new(), + vec: Vec::new(), + } + } + + pub fn insert(&mut self, value: T) { + if self.set.insert(value.clone()) { + self.vec.push(value); + } + } + + pub fn extend>(&mut self, iter: I) { + for item in iter { + self.insert(item); + } + } + + pub fn iter(&self) -> std::slice::Iter { + self.vec.iter() + } + } + + impl IntoIterator for OrderedSet { + type Item = T; + type IntoIter = std::vec::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.vec.into_iter() + } + } +} + +fn collect_unique_keys(values: Value) -> Value { + use helper::OrderedSet; + use serde::Deserialize; + let mut unique_keys: OrderedSet = OrderedSet::new(); + + if let Ok(vec) = Vec::::deserialize(values) { + for value in vec.iter() { + if let Some(dict) = value.as_object() { + let keys_vec: Vec = dict.keys().map(|s| s.to_owned()).collect(); + unique_keys.extend(keys_vec); + } + } + } + + let keys_list = unique_keys.into_iter().collect::>(); + Value::from(keys_list) +} diff --git a/templates/admin/items/item_list.jinja b/templates/admin/items/item_list.jinja index 4572249..57f3cea 100644 --- a/templates/admin/items/item_list.jinja +++ b/templates/admin/items/item_list.jinja @@ -10,23 +10,42 @@ {% endblock %} {% if item_list %} +{% set item_keys = item_info.display_list or item_list|table_keys %} + +{% if item_info.lookup_key in item_keys %} +{% set primary_key = item_info.lookup_key %} +{% endif %} +
- + - - - + {% for key in item_keys %} + {% if key==primary_key %} + + {% else %} + + {% endif %} + + {% endfor %} {% for item in item_list %} - - - + {% for key in item_keys %} + {% if key==primary_key %} + + {% else %} + + {% endif %} + {% endfor %} {% endfor %} @@ -34,13 +53,6 @@
Table Caption{{ item_info.name|none(content) }}
Header1Header2Header3 + {{ key|capitalize }} + {{ key|capitalize }}
{{ item.name}}{{ item.age }}{{ item }} + {{ item[key] }} + {{ item[key] }}
-{% set x="name" %} - - - -
  • {{ item[x] }}
  • - - {% else %} No Items found. {% endif %} diff --git a/templates/base.html.j2 b/templates/base.jinja similarity index 92% rename from templates/base.html.j2 rename to templates/base.jinja index f94df14..227e824 100644 --- a/templates/base.html.j2 +++ b/templates/base.jinja @@ -24,6 +24,9 @@ {% include "fomantic.html" %} {% endblock %} + {% block extrahead %} + {% endblock extrahead %} + diff --git a/templates/index.html b/templates/index.html index b1f3a27..894058d 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,4 +1,18 @@ -{% extends "base.html.j2" %} +{% extends "base.jinja" %} + +{% block extrahead %} + +{% endblock extrahead %} + {% block content %}