From 6b716301f0d9cd18877792f986ff5eee7adeea10 Mon Sep 17 00:00:00 2001
From: Andrea Zagli <azagli@libero.it>
Date: Sat, 22 Jun 2024 09:15:52 +0200
Subject: [PATCH] Replaced actix with axum.

---
 Cargo.toml       |  4 +-
 examples/grid.rs | 97 ++++++++++++++++--------------------------------
 2 files changed, 36 insertions(+), 65 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index d6c75d9..56bc9ba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,8 @@ tera = "1.8.0"
 
 [dev-dependencies]
 config = "0.13"
-actix-web = "4"
+tokio = { version = "1", features = [ "full" ] }
+axum = "0.7.5"
+axum-macros = "0.4.1"
 futures = "0.3"
 sqlx = { version = "0.6", features = [ "runtime-async-std-native-tls", "sqlite" ] }
diff --git a/examples/grid.rs b/examples/grid.rs
index a8df8e2..321ac02 100644
--- a/examples/grid.rs
+++ b/examples/grid.rs
@@ -1,31 +1,29 @@
 extern crate zakgrid;
 
-use std::any::Any;
+use std::{any::Any, sync::Arc};
 
 use config::{Config, ConfigError};
 use serde_derive::Deserialize;
 
-use actix_web::{
-    body::BoxBody,
-    dev::ServiceResponse,
-    error,
-    http::{header::ContentType, StatusCode},
-    middleware::{self, ErrorHandlerResponse, ErrorHandlers},
-    web, App, Error, HttpResponse, HttpServer, Responder, Result,
+use axum::{
+	routing::get, Router,
+	extract::State,
+	response::{Html, IntoResponse, Response},
 };
+use axum_macros::debug_handler;
 
 use futures::TryStreamExt;
-use sqlx::{database::HasStatement, sqlite::SqliteStatement, Column, Connection, Execute, Executor, Row, SqliteConnection, Statement};
+use sqlx::{Column, Connection, Executor, Row, SqliteConnection, Statement};
 use std::str::FromStr;
 
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Deserialize, Clone)]
 #[allow(unused)]
 struct Http {
 	bind_ip: String,
 	bind_port: u16,
 }
 
-#[derive(Debug, Deserialize)]
+#[derive(Debug, Deserialize, Clone)]
 #[allow(unused)]
 struct Settings {
 	http: Http,
@@ -45,14 +43,14 @@ struct AppState {
 	settings: Settings,
 }
 
-fn get_not_exists(value: &serde_json::Value, user_data: &Option<Box<dyn Any>>) -> String {
+fn get_not_exists(_value: &serde_json::Value, _user_data: &Option<Box<dyn Any>>) -> String {
 	String::from("not exists (returned by fn)")
 }
 
+#[debug_handler]
 async fn index(
-    data: web::Data<AppState>,
-) -> Result<impl Responder, Error> {
-
+	State(_state): State<Arc<AppState>>,
+) -> Response {
 	let mut grid = zakgrid::Grid::new();
 
 	grid.set_header_tmpl(String::from("<tr style='border: 1px solid;'>\n{{ row }}</tr>\n"));
@@ -73,7 +71,7 @@ async fn index(
 	col.set_title("the second column title");
 	grid.add_column(col);
 
-	let mut col = zakgrid::Column::new("surname");
+	let col = zakgrid::Column::new("surname");
 	grid.add_column(col);
 
 	let mut col = zakgrid::Column::new("age");
@@ -94,9 +92,9 @@ async fn index(
 		s.push_str(format!("col {} title {}<br/>\n", i, v.title()).as_str());
 	}
 
-	let mut conn = SqliteConnection::connect("examples/grid.db").await.unwrap();
+	let mut conn = futures::executor::block_on( async { SqliteConnection::connect("examples/grid.db").await.unwrap() });
 
-	let p = conn.prepare("SELECT * FROM clients WHERE FALSE").await.unwrap();
+	let p = futures::executor::block_on( async { conn.prepare("SELECT * FROM clients WHERE FALSE").await.unwrap() });
 
 	//let q: sqlx::query::Query<'_, sqlx::Sqlite, _> = sqlx::query("SELECT * FROM clients WHERE FALSE");
 	//let p = q.statement().unwrap();
@@ -109,7 +107,7 @@ async fn index(
 	s.push_str(header.as_str());
 
 	let mut rows = sqlx::query("SELECT * FROM clients").fetch(&mut conn);
-	while let Some(row) = rows.try_next().await.unwrap() {
+	while let Some(row) = futures::executor::block_on( async { rows.try_next().await.unwrap() }) {
 		let cols = row.columns();
 
 		let mut r = String::new();
@@ -130,14 +128,17 @@ async fn index(
 	s.push_str("</body>\n");
 	s.push_str("</html>\n");
 
-	Ok(HttpResponse::Ok().body(s))
+	Html(s).into_response()
 }
 
-#[actix_web::main]
-async fn main() -> std::io::Result<()> {
+#[tokio::main]
+async fn main() {
+
+	let settings = Settings::new().unwrap();
+	let s2 = settings.clone();
 
 	let appstate: AppState = AppState {
-		settings: Settings::new().unwrap(),
+		settings: settings,
 	};
 
 	// Print out our settings
@@ -146,48 +147,16 @@ async fn main() -> std::io::Result<()> {
 		appstate.settings
 	);
 
-    println!("Listening on: {}:{}, open browser and visit have a try!", appstate.settings.http.bind_ip, appstate.settings.http.bind_port);
-    HttpServer::new(move || {
-        App::new()
-            .app_data(web::Data::new(AppState {
-				settings: Settings::new().unwrap(),
-			}))
-            .wrap(middleware::Logger::default()) // enable logger
-            .service(web::resource("/").route(web::get().to(index)))
-            .service(web::scope("").wrap(error_handlers()))
-    })
-    .bind((appstate.settings.http.bind_ip, appstate.settings.http.bind_port))?
-    .run()
-    .await
-}
+	let shared_state = Arc::new(appstate);
 
-// Custom error handlers, to return HTML responses when an error occurs.
-fn error_handlers() -> ErrorHandlers<BoxBody> {
-    ErrorHandlers::new().handler(StatusCode::NOT_FOUND, not_found)
-}
 
-// Error handler for a 404 Page not found error.
-fn not_found<B>(res: ServiceResponse<B>) -> Result<ErrorHandlerResponse<BoxBody>> {
-    let response = get_error_response(&res, "Page not found");
-    Ok(ErrorHandlerResponse::Response(ServiceResponse::new(
-        res.into_parts().0,
-        response.map_into_left_body(),
-    )))
-}
+    let app = Router::new()
+        .route("/", get(index))
+        .with_state(shared_state);
+
+    println!("Listening on: {}:{}, open browser and visit have a try!", s2.http.bind_ip, s2.http.bind_port);
 
-// Generic error handler.
-fn get_error_response<B>(res: &ServiceResponse<B>, error: &str) -> HttpResponse {
-    let request = res.request();
-
-    // Provide a fallback to a simple plain text response in case an error occurs during the
-    // rendering of the error page.
-    let fallback = |e: &str| {
-        HttpResponse::build(res.status())
-            .content_type(ContentType::plaintext())
-            .body(e.to_string())
-    };
-
-    HttpResponse::build(res.status())
-            .content_type(ContentType::html())
-            .body("")
+    // run our app with hyper, listening globally on port 3000
+    let listener = tokio::net::TcpListener::bind(format!("{}:{}", s2.http.bind_ip, s2.http.bind_port)).await.unwrap();
+    axum::serve(listener, app).await.unwrap();
 }
-- 
2.49.0