From 7fe704748517c98fc430205c17e3a6e9e6fd6e81 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gabor=20K=C3=B6rber?= <gab@g4b.org>
Date: Mon, 26 Feb 2024 23:05:12 +0100
Subject: [PATCH 1/3] refactor: starting refactor to extract into rear crate

---
 Cargo.lock                             | 30 +++++++++++++++
 Cargo.toml                             |  3 +-
 NOTES.md                               | 15 ++++++++
 rear/Cargo.toml                        | 51 ++++++++++++++++++++++++++
 {src => rear/src}/admin/domain.rs      |  0
 {src => rear/src}/admin/mod.rs         |  4 +-
 {src => rear/src}/admin/state.rs       |  8 +++-
 {src => rear/src}/admin/views.rs       |  0
 {src => rear/src}/auth/mod.rs          |  0
 {src => rear/src}/auth/models.rs       |  0
 rear/src/lib.rs                        |  3 ++
 {src => rear/src}/service/error.rs     |  0
 {src => rear/src}/service/handlers.rs  |  0
 {src => rear/src}/service/mod.rs       |  0
 {src => rear/src}/service/templates.rs |  0
 src/main.rs                            |  4 +-
 src/state.rs                           |  8 ++--
 17 files changed, 114 insertions(+), 12 deletions(-)
 create mode 100644 rear/Cargo.toml
 rename {src => rear/src}/admin/domain.rs (100%)
 rename {src => rear/src}/admin/mod.rs (88%)
 rename {src => rear/src}/admin/state.rs (93%)
 rename {src => rear/src}/admin/views.rs (100%)
 rename {src => rear/src}/auth/mod.rs (100%)
 rename {src => rear/src}/auth/models.rs (100%)
 create mode 100644 rear/src/lib.rs
 rename {src => rear/src}/service/error.rs (100%)
 rename {src => rear/src}/service/handlers.rs (100%)
 rename {src => rear/src}/service/mod.rs (100%)
 rename {src => rear/src}/service/templates.rs (100%)

diff --git a/Cargo.lock b/Cargo.lock
index 531ef91..081dab1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1839,6 +1839,7 @@ dependencies = [
  "minijinja-autoreload",
  "once_cell",
  "pulldown-cmark",
+ "rear",
  "rust-embed",
  "sea-orm",
  "serde",
@@ -2371,6 +2372,35 @@ dependencies = [
  "getrandom",
 ]
 
+[[package]]
+name = "rear"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "axum 0.7.4",
+ "barrel",
+ "derive_builder",
+ "dotenvy",
+ "dunce",
+ "env_logger",
+ "log",
+ "mime_guess",
+ "minijinja",
+ "minijinja-autoreload",
+ "once_cell",
+ "pulldown-cmark",
+ "rust-embed",
+ "sea-orm",
+ "serde",
+ "serde_json",
+ "slug",
+ "sqlformat",
+ "tokio",
+ "tower-http",
+ "tracing",
+]
+
 [[package]]
 name = "redox_syscall"
 version = "0.3.5"
diff --git a/Cargo.toml b/Cargo.toml
index 22cca55..bccec32 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,7 +9,7 @@ default-run = "miniweb"
 
 
 [workspace]
-members = [".", "entity", "migration"]
+members = [".", "entity", "migration", "rear"]
 
 [features]
 #  https://github.com/rust-db/barrel/blob/master/guides/diesel-setup.md
@@ -19,6 +19,7 @@ default = ["use_barrel"]
 [dependencies]
 strinto = { path = "./strinto" }
 entity = { path = "./entity" }
+rear = { path = "./rear" }
 sea-orm = { version = "0.12.10", features = [
     "runtime-tokio-native-tls",
     "sqlx-postgres",
diff --git a/NOTES.md b/NOTES.md
index 2644699..cf215a6 100644
--- a/NOTES.md
+++ b/NOTES.md
@@ -263,3 +263,18 @@ Make axum log requests!
 
 There is another crate, that uses some MIME detection middleware for cache-control, called axum-cc, which could inspire middlewares here.
 
+## Changing Rust Toolchain on Windows
+
+change to GNU:
+
+`rustup toolchain install stable-x86_64-pc-windows-gnu`
+
+change back to MSVC:
+
+`rustup toolchain install stable-x86_64-pc-windows-msvc`
+
+or:
+
+`rustup toolchain install stable-msvc`
+
+activate toolchain with `rustup default`
diff --git a/rear/Cargo.toml b/rear/Cargo.toml
new file mode 100644
index 0000000..38f2083
--- /dev/null
+++ b/rear/Cargo.toml
@@ -0,0 +1,51 @@
+[package]
+name = "rear"
+version = "0.1.0"
+edition = "2021"
+publish = false
+
+[lib]
+name = "rear"
+path = "src/lib.rs"
+
+[features]
+#  https://github.com/rust-db/barrel/blob/master/guides/diesel-setup.md
+use_barrel = ["barrel", "sqlformat"]
+default = ["use_barrel"]
+
+[dependencies]
+sea-orm = { version = "0.12.10", features = [
+    "runtime-tokio-native-tls",
+    "sqlx-postgres",
+] }
+sqlformat = { version = "0.2.2", optional = true }
+anyhow = "1.0.75"
+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.11", features = [
+    "loader",
+    "builtins",
+    "urlencode",
+    "deserialization",
+] }
+minijinja-autoreload = "1.0.8"
+once_cell = "1.18.0"
+pulldown-cmark = "0.9.3"
+rust-embed = { version = "8.0.0", features = [
+    "axum",
+    "tokio",
+    "include-exclude",
+] }
+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.11"
+serde_json = "1.0.108"
+slug = "0.1.5"
+derive_builder = "0.13.0"
+async-trait = "0.1.77"
+tracing = "0.1.40"
+tower-http = { version = "0.5.1", features = ["trace"] }
diff --git a/src/admin/domain.rs b/rear/src/admin/domain.rs
similarity index 100%
rename from src/admin/domain.rs
rename to rear/src/admin/domain.rs
diff --git a/src/admin/mod.rs b/rear/src/admin/mod.rs
similarity index 88%
rename from src/admin/mod.rs
rename to rear/src/admin/mod.rs
index 75d3cec..268f8ee 100644
--- a/src/admin/mod.rs
+++ b/rear/src/admin/mod.rs
@@ -1,12 +1,10 @@
 use axum::{routing::get, routing::post, Router};
 
-use crate::state::AppState;
-
 pub mod domain;
 pub mod state;
 pub mod views;
 
-pub fn routes() -> Router<AppState> {
+pub fn route() -> Router<AppState> {
     Router::new()
         .route("/", get(views::index).post(views::index_action))
         .route("/app/:app", get(views::list_app))
diff --git a/src/admin/state.rs b/rear/src/admin/state.rs
similarity index 93%
rename from src/admin/state.rs
rename to rear/src/admin/state.rs
index a9301c1..2306219 100644
--- a/src/admin/state.rs
+++ b/rear/src/admin/state.rs
@@ -1,10 +1,16 @@
 use tokio::sync::Mutex;
 
 use super::domain::{AdminApp, AdminModel, AdminModelConfig, AdminRepository};
+use crate::service::templates::Templates;
 use std::collections::HashMap;
 use std::sync::Arc;
 
-pub type AdminState = Arc<AdminRegistry>;
+pub trait AdminState {
+    fn get_templates(&self) -> &Templates;
+    fn get_registry(&self) -> SharedRegistry;
+}
+
+pub type SharedRegistry = Arc<AdminRegistry>;
 
 // main registry.
 pub struct AdminRegistry {
diff --git a/src/admin/views.rs b/rear/src/admin/views.rs
similarity index 100%
rename from src/admin/views.rs
rename to rear/src/admin/views.rs
diff --git a/src/auth/mod.rs b/rear/src/auth/mod.rs
similarity index 100%
rename from src/auth/mod.rs
rename to rear/src/auth/mod.rs
diff --git a/src/auth/models.rs b/rear/src/auth/models.rs
similarity index 100%
rename from src/auth/models.rs
rename to rear/src/auth/models.rs
diff --git a/rear/src/lib.rs b/rear/src/lib.rs
new file mode 100644
index 0000000..01b001c
--- /dev/null
+++ b/rear/src/lib.rs
@@ -0,0 +1,3 @@
+pub mod admin;
+pub mod auth;
+pub mod service;
diff --git a/src/service/error.rs b/rear/src/service/error.rs
similarity index 100%
rename from src/service/error.rs
rename to rear/src/service/error.rs
diff --git a/src/service/handlers.rs b/rear/src/service/handlers.rs
similarity index 100%
rename from src/service/handlers.rs
rename to rear/src/service/handlers.rs
diff --git a/src/service/mod.rs b/rear/src/service/mod.rs
similarity index 100%
rename from src/service/mod.rs
rename to rear/src/service/mod.rs
diff --git a/src/service/templates.rs b/rear/src/service/templates.rs
similarity index 100%
rename from src/service/templates.rs
rename to rear/src/service/templates.rs
diff --git a/src/main.rs b/src/main.rs
index 840be3f..a4ab76f 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,10 +1,7 @@
-mod admin;
 mod admin_examples;
 mod howto;
-mod service;
 mod state;
 
-use crate::service::{handlers, templates};
 use crate::state::AppState;
 use admin_examples::static_repository;
 use admin_examples::user_repository;
@@ -19,6 +16,7 @@ use axum::{
 };
 use dotenvy::dotenv;
 use log::info;
+use rear::service::{handlers, templates};
 use std::env;
 use std::net::SocketAddr;
 use std::sync::Arc;
diff --git a/src/state.rs b/src/state.rs
index 904e69f..2b4eb48 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,7 +1,7 @@
 use axum::extract::FromRef;
 
-use crate::admin::state::AdminState;
-use crate::service::templates;
+use rear::admin::state::SharedRegistry;
+use rear::service::templates;
 
 #[derive(Clone)]
 pub struct AppState {
@@ -15,8 +15,8 @@ impl FromRef<AppState> for templates::Templates {
     }
 }
 
-impl FromRef<AppState> for AdminState {
-    fn from_ref(app_state: &AppState) -> AdminState {
+impl FromRef<AppState> for SharedRegistry {
+    fn from_ref(app_state: &AppState) -> SharedRegistry {
         app_state.admin.clone()
     }
 }

From ac9488b299f993c1a2faa427843d9cce89f29c8d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gabor=20K=C3=B6rber?= <gab@g4b.org>
Date: Wed, 28 Feb 2024 14:01:09 +0100
Subject: [PATCH 2/3] feature: extracting admin and services to

---
 .env.sample                  |  3 ++
 rear/src/admin/mod.rs        | 17 +++++----
 rear/src/admin/views.rs      | 71 ++++++++++++++++++++++--------------
 rear/src/service/handlers.rs | 51 --------------------------
 src/embed.rs                 | 58 +++++++++++++++++++++++++++++
 src/howto/views.rs           |  2 +-
 src/lib.rs                   |  3 --
 src/main.rs                  |  4 +-
 src/state.rs                 | 14 ++++++-
 9 files changed, 130 insertions(+), 93 deletions(-)
 create mode 100644 src/embed.rs

diff --git a/.env.sample b/.env.sample
index cc1df28..fa1bd23 100644
--- a/.env.sample
+++ b/.env.sample
@@ -1 +1,4 @@
+APP_HOST=127.0.0.1
+APP_PORT=3000
 DATABASE_URL=postgresql://miniweb:miniweb@localhost:54321/miniweb
+RUST_LOG=info
diff --git a/rear/src/admin/mod.rs b/rear/src/admin/mod.rs
index 268f8ee..fc36eca 100644
--- a/rear/src/admin/mod.rs
+++ b/rear/src/admin/mod.rs
@@ -4,21 +4,24 @@ pub mod domain;
 pub mod state;
 pub mod views;
 
-pub fn route() -> Router<AppState> {
+pub fn routes<S: state::AdminState + Clone + Send + Sync + 'static>() -> Router<S> {
     Router::new()
-        .route("/", get(views::index).post(views::index_action))
-        .route("/app/:app", get(views::list_app))
-        .route("/app/:app/model/:model", get(views::list_item_collection))
+        .route("/", get(views::index::<S>).post(views::index_action::<S>))
+        .route("/app/:app", get(views::list_app::<S>))
+        .route(
+            "/app/:app/model/:model",
+            get(views::list_item_collection::<S>),
+        )
         .route(
             "/app/:app/model/:model/add",
-            get(views::new_item).post(views::create_item),
+            get(views::new_item::<S>).post(views::create_item::<S>),
         )
         .route(
             "/app/:app/model/:model/change/:id",
-            get(views::change_item).patch(views::update_item),
+            get(views::change_item::<S>).patch(views::update_item::<S>),
         )
         .route(
             "/app/:app/model/:model/detail/:id",
-            get(views::view_item_details),
+            get(views::view_item_details::<S>),
         )
 }
diff --git a/rear/src/admin/views.rs b/rear/src/admin/views.rs
index 2f441d2..9b80231 100644
--- a/rear/src/admin/views.rs
+++ b/rear/src/admin/views.rs
@@ -9,6 +9,7 @@ use serde_json::Value;
 
 use crate::admin::domain::{AdminApp, AdminModel};
 use crate::admin::state;
+use crate::admin::state::AdminState;
 use crate::service::templates;
 use serde::{Deserialize, Serialize};
 
@@ -76,11 +77,12 @@ pub fn base_template(headers: &HeaderMap) -> Option<String> {
     }
 }
 
-pub async fn index(
-    templates: State<templates::Templates>,
-    registry: State<Arc<state::AdminRegistry>>,
+pub async fn index<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
     headers: HeaderMap,
 ) -> impl IntoResponse {
+    let templates = admin.get_templates();
+    let registry = admin.get_registry();
     templates.render_html(
         "admin/index.html",
         AdminContext {
@@ -92,26 +94,29 @@ pub async fn index(
 }
 
 // Index Action is POST to the index site. We can anchor some general business code here.
-pub async fn index_action() -> impl IntoResponse {
+pub async fn index_action<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
+) -> impl IntoResponse {
     "There is your answer!".to_owned()
 }
 
-pub async fn list_app(
-    templates: State<templates::Templates>,
+pub async fn list_app<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
     Path(app_key): Path<String>,
 ) -> impl IntoResponse {
+    let templates = admin.get_templates();
     templates.render_html("admin/app_list.jinja", ())
 }
 
 // List Items renders the entire list item page.
-pub async fn list_item_collection(
-    templates: State<templates::Templates>,
-    registry: State<Arc<state::AdminRegistry>>,
+pub async fn list_item_collection<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
     headers: HeaderMap,
     Path((app_key, model_key)): Path<(String, String)>,
 ) -> impl IntoResponse {
     info!("list_item_collection {} for model {}", app_key, model_key);
-
+    let templates = admin.get_templates();
+    let registry = admin.get_registry();
     let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
         let repo = repo.lock().await;
         let admin_model = registry
@@ -136,19 +141,21 @@ pub async fn list_item_collection(
 }
 
 // Items Action is a POST to an item list. By default these are actions, that work on a list of items as input.
-pub async fn item_collection_action(
+pub async fn item_collection_action<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
     Path((app_key, model_key)): Path<(String, String)>,
 ) -> impl IntoResponse {
     "There is your answer!".to_owned()
 }
 
 // Item Details shows one single dataset.
-pub async fn view_item_details(
-    templates: State<templates::Templates>,
-    registry: State<Arc<state::AdminRegistry>>,
+pub async fn view_item_details<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
     headers: HeaderMap,
     Path((app_key, model_key, id)): Path<(String, String, String)>,
 ) -> impl IntoResponse {
+    let templates = admin.get_templates();
+    let registry = admin.get_registry();
     let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
         let repo = repo.lock().await;
         let admin_model = registry
@@ -174,12 +181,13 @@ pub async fn view_item_details(
     templates.render_html("admin/items/item_detail.jinja", context)
 }
 
-pub async fn new_item(
-    templates: State<templates::Templates>,
-    registry: State<Arc<state::AdminRegistry>>,
+pub async fn new_item<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
     headers: HeaderMap,
     Path((app_key, model_key)): Path<(String, String)>,
 ) -> impl IntoResponse {
+    let templates = admin.get_templates();
+    let registry = admin.get_registry();
     let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
         let repo = repo.lock().await;
         let admin_model = registry
@@ -203,13 +211,14 @@ pub async fn new_item(
     templates.render_html("admin/items/item_create.jinja", context)
 }
 
-pub async fn create_item(
-    templates: State<templates::Templates>,
-    registry: State<Arc<state::AdminRegistry>>,
+pub async fn create_item<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
     headers: HeaderMap,
     Path((app_key, model_key)): Path<(String, String)>,
     Form(form): Form<Value>,
 ) -> impl IntoResponse {
+    let templates = admin.get_templates();
+    let registry = admin.get_registry();
     let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
         let mut repo = repo.lock().await;
         let admin_model = registry
@@ -240,12 +249,13 @@ pub async fn create_item(
 }
 
 /// Change is the GET version.
-pub async fn change_item(
-    templates: State<templates::Templates>,
-    registry: State<Arc<state::AdminRegistry>>,
+pub async fn change_item<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
     headers: HeaderMap,
     Path((app_key, model_key, id)): Path<(String, String, String)>,
 ) -> impl IntoResponse {
+    let templates = admin.get_templates();
+    let registry = admin.get_registry();
     let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
         let repo = repo.lock().await;
         let admin_model = registry
@@ -271,13 +281,14 @@ pub async fn change_item(
     templates.render_html("admin/items/item_change.jinja", context)
 }
 
-pub async fn update_item(
-    templates: State<templates::Templates>,
-    registry: State<Arc<state::AdminRegistry>>,
+pub async fn update_item<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
     headers: HeaderMap,
     Path((app_key, model_key, id)): Path<(String, String, String)>,
     Form(form): Form<Value>,
 ) -> impl IntoResponse {
+    let templates = admin.get_templates();
+    let registry = admin.get_registry();
     let context = if let Ok(repo) = registry.get_repository(&app_key, &model_key) {
         let mut repo = repo.lock().await;
         let admin_model = registry
@@ -307,13 +318,17 @@ pub async fn update_item(
 }
 
 // Item Action allows running an action on one single dataset.
-pub async fn item_action(
+pub async fn item_action<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
     Path((app_key, model_key, model_id)): Path<(String, String, String)>,
 ) -> impl IntoResponse {
     "There is your answer!".to_owned()
 }
 
-pub async fn debug_view(Path(data): Path<String>) -> impl IntoResponse {
+pub async fn debug_view<S: AdminState + Clone + Send + Sync + 'static>(
+    admin: State<S>,
+    Path(data): Path<String>,
+) -> impl IntoResponse {
     println!("debug: {}", data);
     "Debug!".to_owned()
 }
diff --git a/rear/src/service/handlers.rs b/rear/src/service/handlers.rs
index 8f0a1ab..01a6be1 100644
--- a/rear/src/service/handlers.rs
+++ b/rear/src/service/handlers.rs
@@ -9,17 +9,6 @@ use axum::{
 use rust_embed::RustEmbed;
 use std::marker::PhantomData;
 
-// usage: .route_service("/static/*file", static_handler.into_service())
-pub async fn static_handler(uri: Uri) -> impl IntoResponse {
-    let mut path = uri.path().trim_start_matches('/').to_string();
-
-    if path.starts_with("static/") {
-        path = path.replace("static/", "");
-    }
-
-    StaticFile::get(path)
-}
-
 // usage: .fallback(not_found_handler)
 pub async fn not_found_handler(
     State(templates): State<Templates>,
@@ -31,43 +20,3 @@ fn not_found(templates: Templates) -> axum::response::Result<impl IntoResponse>
     let body = templates.render_html("http404.html", ());
     Ok((StatusCode::NOT_FOUND, body))
 }
-
-pub struct EmbeddedFile<E, T> {
-    pub path: T,
-    embed: PhantomData<E>,
-}
-
-impl<E, T> EmbeddedFile<E, T> {
-    pub fn get(path: T) -> Self {
-        Self {
-            path,
-            embed: PhantomData,
-        }
-    }
-}
-
-impl<E, T> IntoResponse for EmbeddedFile<E, T>
-where
-    E: RustEmbed,
-    T: AsRef<str>,
-{
-    fn into_response(self) -> Response {
-        let path: &str = self.path.as_ref();
-        match E::get(path) {
-            Some(content) => {
-                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())
-                    .body(body)
-                    .unwrap()
-            }
-            None => StatusCode::NOT_FOUND.into_response(),
-        }
-    }
-}
-
-#[derive(RustEmbed)]
-#[folder = "static"]
-struct StaticDir;
-type StaticFile<T> = EmbeddedFile<StaticDir, T>;
diff --git a/src/embed.rs b/src/embed.rs
new file mode 100644
index 0000000..285b815
--- /dev/null
+++ b/src/embed.rs
@@ -0,0 +1,58 @@
+use axum::{
+    body::Body,
+    http::{header, StatusCode, Uri},
+    response::{IntoResponse, Response},
+};
+use rust_embed::RustEmbed;
+use std::marker::PhantomData;
+
+pub struct EmbeddedFile<E, T> {
+    pub path: T,
+    embed: PhantomData<E>,
+}
+
+impl<E, T> EmbeddedFile<E, T> {
+    pub fn get(path: T) -> Self {
+        Self {
+            path,
+            embed: PhantomData,
+        }
+    }
+}
+
+impl<E, T> IntoResponse for EmbeddedFile<E, T>
+where
+    E: RustEmbed,
+    T: AsRef<str>,
+{
+    fn into_response(self) -> Response {
+        let path: &str = self.path.as_ref();
+        match E::get(path) {
+            Some(content) => {
+                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())
+                    .body(body)
+                    .unwrap()
+            }
+            None => StatusCode::NOT_FOUND.into_response(),
+        }
+    }
+}
+
+#[derive(RustEmbed)]
+#[folder = "static"]
+struct StaticDir;
+type StaticFile<T> = EmbeddedFile<StaticDir, T>;
+
+// usage: .route_service("/static/*file", static_handler.into_service())
+pub async fn static_handler(uri: Uri) -> impl IntoResponse {
+    let mut path = uri.path().trim_start_matches('/').to_string();
+
+    if path.starts_with("static/") {
+        path = path.replace("static/", "");
+    }
+
+    StaticFile::get(path)
+}
diff --git a/src/howto/views.rs b/src/howto/views.rs
index 0974caf..a69765c 100644
--- a/src/howto/views.rs
+++ b/src/howto/views.rs
@@ -1,6 +1,6 @@
 use axum::{extract::State, response::IntoResponse, Form};
 
-use crate::service::templates;
+use rear::service::templates;
 use serde::Deserialize;
 
 #[derive(Deserialize)]
diff --git a/src/lib.rs b/src/lib.rs
index 1ed76c4..eba3a3e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1 @@
-pub mod admin;
-pub mod auth;
-pub mod service;
 pub mod state;
diff --git a/src/main.rs b/src/main.rs
index a4ab76f..bf6fad5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,5 @@
 mod admin_examples;
+mod embed;
 mod howto;
 mod state;
 
@@ -16,6 +17,7 @@ use axum::{
 };
 use dotenvy::dotenv;
 use log::info;
+use rear::admin;
 use rear::service::{handlers, templates};
 use std::env;
 use std::net::SocketAddr;
@@ -66,7 +68,7 @@ async fn main() {
         //.merge(admin_router)
         .nest("/admin", admin::routes())
         .nest("/howto", howto::routes())
-        .route_service("/static/*file", handlers::static_handler.into_service())
+        .route_service("/static/*file", embed::static_handler.into_service())
         .fallback(handlers::not_found_handler)
         .with_state(state)
         .layer(
diff --git a/src/state.rs b/src/state.rs
index 2b4eb48..4b2d1df 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,12 +1,12 @@
 use axum::extract::FromRef;
 
-use rear::admin::state::SharedRegistry;
+use rear::admin::state::{AdminState, SharedRegistry};
 use rear::service::templates;
 
 #[derive(Clone)]
 pub struct AppState {
     pub templates: templates::Templates,
-    pub admin: AdminState,
+    pub admin: SharedRegistry,
 }
 
 impl FromRef<AppState> for templates::Templates {
@@ -20,3 +20,13 @@ impl FromRef<AppState> for SharedRegistry {
         app_state.admin.clone()
     }
 }
+
+impl AdminState for AppState {
+    fn get_templates(&self) -> &templates::Templates {
+        &self.templates
+    }
+
+    fn get_registry(&self) -> SharedRegistry {
+        self.admin.clone()
+    }
+}

From 74eb3ac4c2dc52923f98735d23650976726ae875 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Gabor=20K=C3=B6rber?= <gab@g4b.org>
Date: Wed, 28 Feb 2024 14:40:59 +0100
Subject: [PATCH 3/3] refactor: removed unused dependencies

---
 Cargo.lock                    | 108 +++-------------------------------
 Cargo.toml                    |   3 +-
 NOTES.md                      |  15 +++++
 rear/Cargo.toml               |  23 --------
 rear/src/admin/domain.rs      |   5 +-
 rear/src/admin/mod.rs         |   2 +-
 rear/src/admin/views.rs       |   4 --
 rear/src/service/handlers.rs  |   9 +--
 rear/src/service/templates.rs |   2 -
 9 files changed, 27 insertions(+), 144 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 081dab1..8ed30e1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -19,9 +19,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
 
 [[package]]
 name = "ahash"
-version = "0.7.7"
+version = "0.7.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd"
+checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9"
 dependencies = [
  "getrandom",
  "once_cell",
@@ -30,9 +30,9 @@ dependencies = [
 
 [[package]]
 name = "ahash"
-version = "0.8.6"
+version = "0.8.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
+checksum = "8b79b82693f705137f8fb9b37871d99e4f9a7df12b917eed79c3d3954830a60b"
 dependencies = [
  "cfg-if",
  "getrandom",
@@ -792,41 +792,6 @@ dependencies = [
  "typenum",
 ]
 
-[[package]]
-name = "darling"
-version = "0.14.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850"
-dependencies = [
- "darling_core",
- "darling_macro",
-]
-
-[[package]]
-name = "darling_core"
-version = "0.14.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0"
-dependencies = [
- "fnv",
- "ident_case",
- "proc-macro2",
- "quote",
- "strsim",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "darling_macro"
-version = "0.14.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e"
-dependencies = [
- "darling_core",
- "quote",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "der"
 version = "0.7.8"
@@ -859,37 +824,6 @@ dependencies = [
  "syn 1.0.109",
 ]
 
-[[package]]
-name = "derive_builder"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "660047478bc508c0fde22c868991eec0c40a63e48d610befef466d48e2bee574"
-dependencies = [
- "derive_builder_macro",
-]
-
-[[package]]
-name = "derive_builder_core"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9b217e6dd1011a54d12f3b920a411b5abd44b1716ecfe94f5f2f2f7b52e08ab7"
-dependencies = [
- "darling",
- "proc-macro2",
- "quote",
- "syn 1.0.109",
-]
-
-[[package]]
-name = "derive_builder_macro"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5f77d7e20ac9153428f7ca14a88aba652adfc7a0ef0a06d654386310ef663b"
-dependencies = [
- "derive_builder_core",
- "syn 1.0.109",
-]
-
 [[package]]
 name = "deunicode"
 version = "1.4.2"
@@ -1311,7 +1245,7 @@ version = "0.12.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
 dependencies = [
- "ahash 0.7.7",
+ "ahash 0.7.8",
 ]
 
 [[package]]
@@ -1320,7 +1254,7 @@ version = "0.14.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
 dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.10",
  "allocator-api2",
 ]
 
@@ -1538,12 +1472,6 @@ dependencies = [
  "cc",
 ]
 
-[[package]]
-name = "ident_case"
-version = "1.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
-
 [[package]]
 name = "idna"
 version = "0.4.0"
@@ -1828,7 +1756,6 @@ dependencies = [
  "async-trait",
  "axum 0.7.4",
  "barrel",
- "derive_builder",
  "dotenvy",
  "dunce",
  "entity",
@@ -1846,7 +1773,6 @@ dependencies = [
  "serde_json",
  "slug",
  "sqlformat",
- "strinto",
  "tokio",
  "tower-http",
  "tracing",
@@ -2379,25 +2305,14 @@ dependencies = [
  "anyhow",
  "async-trait",
  "axum 0.7.4",
- "barrel",
- "derive_builder",
- "dotenvy",
- "dunce",
- "env_logger",
  "log",
- "mime_guess",
  "minijinja",
  "minijinja-autoreload",
- "once_cell",
  "pulldown-cmark",
- "rust-embed",
- "sea-orm",
  "serde",
  "serde_json",
  "slug",
- "sqlformat",
  "tokio",
- "tower-http",
  "tracing",
 ]
 
@@ -3046,7 +2961,7 @@ version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "d84b0a3c3739e220d94b3239fd69fb1f74bc36e16643423bd99de3b43c21bfbd"
 dependencies = [
- "ahash 0.8.6",
+ "ahash 0.8.10",
  "atoi",
  "bigdecimal",
  "byteorder",
@@ -3261,15 +3176,6 @@ dependencies = [
  "unicode-normalization",
 ]
 
-[[package]]
-name = "strinto"
-version = "0.1.0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.48",
-]
-
 [[package]]
 name = "strsim"
 version = "0.10.0"
diff --git a/Cargo.toml b/Cargo.toml
index bccec32..e8b8cfc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -17,7 +17,7 @@ use_barrel = ["barrel", "sqlformat"]
 default = ["use_barrel"]
 
 [dependencies]
-strinto = { path = "./strinto" }
+# strinto = { path = "./strinto" }
 entity = { path = "./entity" }
 rear = { path = "./rear" }
 sea-orm = { version = "0.12.10", features = [
@@ -51,7 +51,6 @@ log = "0.4.20"
 env_logger = "0.11"
 serde_json = "1.0.108"
 slug = "0.1.5"
-derive_builder = "0.13.0"
 async-trait = "0.1.77"
 tracing = "0.1.40"
 tower-http = { version = "0.5.1", features = ["trace"] }
diff --git a/NOTES.md b/NOTES.md
index cf215a6..58d2e06 100644
--- a/NOTES.md
+++ b/NOTES.md
@@ -278,3 +278,18 @@ or:
 `rustup toolchain install stable-msvc`
 
 activate toolchain with `rustup default`
+
+
+### udeps
+
+https://github.com/est31/cargo-udeps
+
+While compilation of this tool also works on Rust stable, it needs Rust nightly to actually run.
+   `cargo install cargo-udeps --locked`
+
+
+cargo +nightly udeps
+
+#### switching to nightly and back
+
+rustup override set nightly
diff --git a/rear/Cargo.toml b/rear/Cargo.toml
index 38f2083..32c0dc2 100644
--- a/rear/Cargo.toml
+++ b/rear/Cargo.toml
@@ -8,22 +8,9 @@ publish = false
 name = "rear"
 path = "src/lib.rs"
 
-[features]
-#  https://github.com/rust-db/barrel/blob/master/guides/diesel-setup.md
-use_barrel = ["barrel", "sqlformat"]
-default = ["use_barrel"]
-
 [dependencies]
-sea-orm = { version = "0.12.10", features = [
-    "runtime-tokio-native-tls",
-    "sqlx-postgres",
-] }
-sqlformat = { version = "0.2.2", optional = true }
 anyhow = "1.0.75"
 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.11", features = [
     "loader",
     "builtins",
@@ -31,21 +18,11 @@ minijinja = { version = "1.0.11", features = [
     "deserialization",
 ] }
 minijinja-autoreload = "1.0.8"
-once_cell = "1.18.0"
 pulldown-cmark = "0.9.3"
-rust-embed = { version = "8.0.0", features = [
-    "axum",
-    "tokio",
-    "include-exclude",
-] }
 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.11"
 serde_json = "1.0.108"
 slug = "0.1.5"
-derive_builder = "0.13.0"
 async-trait = "0.1.77"
 tracing = "0.1.40"
-tower-http = { version = "0.5.1", features = ["trace"] }
diff --git a/rear/src/admin/domain.rs b/rear/src/admin/domain.rs
index 01061c4..2450603 100644
--- a/rear/src/admin/domain.rs
+++ b/rear/src/admin/domain.rs
@@ -50,12 +50,11 @@ mod dto {
 }
 
 pub mod repository {
-    use super::dto::{AdminApp, AdminModel};
+    use super::dto::AdminModel;
     use async_trait::async_trait;
-    use derive_builder::Builder;
     use serde::{Serialize, Serializer};
     use serde_json::Value;
-    use std::{collections::HashMap, vec::IntoIter};
+    use std::vec::IntoIter;
 
     pub type RepositoryContext = AdminModel;
 
diff --git a/rear/src/admin/mod.rs b/rear/src/admin/mod.rs
index fc36eca..4a5de72 100644
--- a/rear/src/admin/mod.rs
+++ b/rear/src/admin/mod.rs
@@ -1,4 +1,4 @@
-use axum::{routing::get, routing::post, Router};
+use axum::{routing::get, Router};
 
 pub mod domain;
 pub mod state;
diff --git a/rear/src/admin/views.rs b/rear/src/admin/views.rs
index 9b80231..bd70f99 100644
--- a/rear/src/admin/views.rs
+++ b/rear/src/admin/views.rs
@@ -1,5 +1,3 @@
-use std::sync::Arc;
-
 use axum::extract::Path;
 use axum::http::HeaderMap;
 use axum::Form;
@@ -8,9 +6,7 @@ use log::info;
 use serde_json::Value;
 
 use crate::admin::domain::{AdminApp, AdminModel};
-use crate::admin::state;
 use crate::admin::state::AdminState;
-use crate::service::templates;
 use serde::{Deserialize, Serialize};
 
 use super::domain::{LookupKey, RepositoryInfo, RepositoryItem, RepositoryList};
diff --git a/rear/src/service/handlers.rs b/rear/src/service/handlers.rs
index 01a6be1..540dab8 100644
--- a/rear/src/service/handlers.rs
+++ b/rear/src/service/handlers.rs
@@ -1,13 +1,6 @@
 /* Default handlers */
 use crate::service::templates::Templates;
-use axum::{
-    body::Body,
-    extract::State,
-    http::{header, StatusCode, Uri},
-    response::{IntoResponse, Response},
-};
-use rust_embed::RustEmbed;
-use std::marker::PhantomData;
+use axum::{extract::State, http::StatusCode, response::IntoResponse};
 
 // usage: .fallback(not_found_handler)
 pub async fn not_found_handler(
diff --git a/rear/src/service/templates.rs b/rear/src/service/templates.rs
index 9118de3..b759502 100644
--- a/rear/src/service/templates.rs
+++ b/rear/src/service/templates.rs
@@ -1,10 +1,8 @@
 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)]