Page Menu
Home
DevCentral
Search
Configure Global Search
Log In
Files
F11822901
D3718.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
D3718.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D3718: Support .into_json_response() with Axum
Attached
Detach File
Event Timeline
Log In to Comment