Page MenuHomeDevCentral

D3718.diff
No OneTemporary

D3718.diff

diff --git a/axum/Cargo.toml b/axum/Cargo.toml
--- a/axum/Cargo.toml
+++ b/axum/Cargo.toml
@@ -24,6 +24,8 @@
serde = { version = "^1.0.226", features = [ "derive" ], optional = true }
http-body-util = "0.1.3"
tokio = "1.47.1"
+log = "0.4.28"
+tower-service = "0.3.3"
[features]
default = ["minimal"]
diff --git a/axum/src/api/mod.rs b/axum/src/api/mod.rs
--- a/axum/src/api/mod.rs
+++ b/axum/src/api/mod.rs
@@ -14,3 +14,4 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
pub mod guards;
+pub mod replies;
diff --git a/axum/src/api/replies.rs b/axum/src/api/replies.rs
new file mode 100644
--- /dev/null
+++ b/axum/src/api/replies.rs
@@ -0,0 +1,62 @@
+/* -------------------------------------------------------------
+ Limiting Factor :: axum :: API :: replies
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ Project: Nasqueron
+ License: BSD-2-Clause
+ ------------------------------------------------------------- */
+
+//! # API standard and JSON responses.
+//!
+//! This module provides useful traits and methods to craft API replies from an existing type.
+
+use axum::http::StatusCode;
+use axum::Json;
+
+#[cfg(feature = "serialization")]
+use serde::Serialize;
+
+/* -------------------------------------------------------------
+ JSON responses
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+pub type ApiJsonResponse<T> = Result<Json<T>, (StatusCode, Json<String>)>;
+
+/// This trait allows to consume an object into an HTTP response.
+pub trait ApiResponse<T> {
+ /// Consumes the value and creates a JSON or a Status result response.
+ fn into_json_response(self) -> ApiJsonResponse<T>;
+}
+
+impl<T> ApiResponse<T> for Json<T> {
+ fn into_json_response(self) -> ApiJsonResponse<T> {
+ Ok(self)
+ }
+}
+
+#[cfg(feature = "serialization")]
+impl<T> ApiResponse<T> for T where T: Serialize {
+ fn into_json_response(self) -> ApiJsonResponse<T> {
+ Ok(Json(self))
+ }
+}
+
+// -------------------------------------------------------------
+// Failures
+// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+pub trait FailureResponse {
+ fn status_code(&self) -> StatusCode;
+
+ fn response(&self) -> String;
+}
+
+impl<T, E> ApiResponse<T> for Result<T, E>
+ where T: ApiResponse<T>, E: FailureResponse
+{
+ fn into_json_response(self) -> ApiJsonResponse<T> {
+ match self {
+ Ok(value) => value.into_json_response(),
+ Err(error) => Err((error.status_code(), Json(error.response())))
+ }
+ }
+}
diff --git a/axum/src/app.rs b/axum/src/app.rs
new file mode 100644
--- /dev/null
+++ b/axum/src/app.rs
@@ -0,0 +1,87 @@
+/* -------------------------------------------------------------
+ Limiting Factor :: axum :: App
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ Project: Nasqueron
+ License: BSD-2-Clause
+ ------------------------------------------------------------- */
+
+use axum::Router;
+use log::{error, info};
+use tokio::net::TcpListener;
+
+/* -------------------------------------------------------------
+ Re-exports from core
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+pub use limiting_factor_core::app::ServerConfig;
+
+/* -------------------------------------------------------------
+ Main application server
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+pub struct App {
+ pub config: ServerConfig,
+
+ router: Router,
+}
+
+impl Default for App {
+ fn default() -> Self {
+ Self {
+ config: ServerConfig::from_env(),
+ router: Router::new(),
+ }
+ }
+}
+
+impl App {
+ pub fn new (config: ServerConfig, router: Router) -> Self {
+ Self {
+ config,
+ router,
+ }
+ }
+
+ pub fn from_config(config: ServerConfig) -> Self {
+ Self {
+ config,
+ router: Router::new(),
+ }
+ }
+
+ pub fn with_config(mut self, config: ServerConfig) -> Self {
+ self.config = config;
+
+ self
+ }
+
+ fn resolve_router(&self) -> Router {
+ if self.config.mount_point == "/" {
+ return self.router.clone();
+ }
+
+ Router::new()
+ .nest(&*self.config.mount_point, self.router.clone())
+ }
+
+ pub async fn run(self) -> bool {
+ let app = self.resolve_router();
+ let socket_address = self.config.get_socket_address();
+
+ info!("🚀 Starting server");
+ match TcpListener::bind(&socket_address).await {
+ Ok(listener) => {
+ info!("Listening to {}", socket_address);
+ axum::serve(listener, app).await.unwrap();
+
+ true
+ }
+
+ Err(error) => {
+ error!("{}", error);
+
+ false
+ }
+ }
+ }
+}
diff --git a/axum/src/lib.rs b/axum/src/lib.rs
--- a/axum/src/lib.rs
+++ b/axum/src/lib.rs
@@ -8,3 +8,4 @@
------------------------------------------------------------- */
pub mod api;
+pub mod app;
diff --git a/core/src/app.rs b/core/src/app.rs
new file mode 100644
--- /dev/null
+++ b/core/src/app.rs
@@ -0,0 +1,76 @@
+/* -------------------------------------------------------------
+ Limiting Factor :: Core :: App
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+ Project: Nasqueron
+ License: BSD-2-Clause
+ ------------------------------------------------------------- */
+
+//! # Create a web server application
+
+use std::default::Default;
+use std::env;
+
+/* -------------------------------------------------------------
+ Base server configuration
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+/// Base configuration for a server
+pub struct ServerConfig {
+ /// The address to attach the listener to
+ pub address: String,
+
+ /// The port to serve
+ pub port: u16,
+
+ /// The mount point of every request URL
+ /// "/" is a good default to let proxy sort this
+ pub mount_point: String,
+}
+
+impl Default for ServerConfig {
+ fn default() -> Self {
+ Self {
+ address: "0.0.0.0".to_string(),
+ port: 8080,
+ mount_point: "/".to_string(),
+ }
+ }
+}
+
+impl ServerConfig {
+ pub fn from_env() -> Self {
+ Self::from_env_or(ServerConfig::default())
+ }
+
+ pub fn from_env_or(default_config: ServerConfig) -> Self {
+ let address = env::var("APP_ADDRESS")
+ .unwrap_or_else(|_| default_config.address);
+
+ let port = read_port_from_environment_or(default_config.port);
+
+ let mount_point = env::var("APP_MOUNT_POINT")
+ .unwrap_or_else(|_| default_config.mount_point);
+
+ Self {
+ address,
+ port,
+ mount_point,
+ }
+ }
+
+ pub fn get_socket_address(&self) -> String {
+ format!("{}:{}", self.address, self.port)
+ }
+}
+
+/* -------------------------------------------------------------
+ Helper methods
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+fn read_port_from_environment_or(default_port: u16) -> u16 {
+ match env::var("APP_PORT") {
+ Ok(port) => port.parse().unwrap_or(default_port),
+
+ Err(_) => default_port,
+ }
+}
diff --git a/core/src/lib.rs b/core/src/lib.rs
--- a/core/src/lib.rs
+++ b/core/src/lib.rs
@@ -9,3 +9,4 @@
------------------------------------------------------------- */
pub mod api;
+pub mod app;

File Metadata

Mime Type
text/plain
Expires
Fri, Sep 26, 00:37 (22 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3017668
Default Alt Text
D3718.diff (7 KB)

Event Timeline