code: upgrade to axum 0.7, working on repo info system

This commit is contained in:
2024-01-26 11:26:47 +01:00
parent 0adae154c8
commit 9f4067e94c
11 changed files with 389 additions and 82 deletions

View File

@@ -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<String>,
}
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<Value>;
fn get_repo_info(&self) -> RepositoryInfo;
fn get_list(&self) -> RepositoryList;
}
}

View File

@@ -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<Value>,
}
impl AdminRepository for MyStaticRepository {
fn get_item(&self, id: usize) -> Option<Value> {
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<Value> {
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(),

View File

@@ -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<AdminApp>,
pub item_info: Option<RepositoryInfo>,
pub item_list: RepositoryList,
pub item: Option<Value>,
}
@@ -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<Value> 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()
}

View File

@@ -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<templates::Templates>) -> 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() {

View File

@@ -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())

View File

@@ -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 {
value
}
}
fn tpl_table(values: Vec<Value>) -> Value {
Value::from_safe_string(format!(
"Output: {}",
values.first().expect("No values found.").kind()
))
}
mod helper {
use std::collections::HashSet;
pub struct OrderedSet<T> {
set: HashSet<T>,
vec: Vec<T>,
}
impl<T: Eq + std::hash::Hash + Clone> OrderedSet<T> {
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<I: IntoIterator<Item = T>>(&mut self, iter: I) {
for item in iter {
self.insert(item);
}
}
pub fn iter(&self) -> std::slice::Iter<T> {
self.vec.iter()
}
}
impl<T> IntoIterator for OrderedSet<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
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<String> = OrderedSet::new();
if let Ok(vec) = Vec::<serde_json::Value>::deserialize(values) {
for value in vec.iter() {
if let Some(dict) = value.as_object() {
let keys_vec: Vec<String> = dict.keys().map(|s| s.to_owned()).collect();
unique_keys.extend(keys_vec);
}
}
}
let keys_list = unique_keys.into_iter().collect::<Vec<_>>();
Value::from(keys_list)
}