Smol changes
This commit is contained in:
27
Cargo.lock
generated
27
Cargo.lock
generated
@@ -757,6 +757,26 @@ version = "2.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "pin-project"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a"
|
||||
dependencies = [
|
||||
"pin-project-internal",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-internal"
|
||||
version = "1.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.111",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.16"
|
||||
@@ -1047,13 +1067,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustapi"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"http 0.2.12",
|
||||
"http-body-util",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tower 0.4.13",
|
||||
"tower-http 0.5.2",
|
||||
@@ -1454,6 +1475,10 @@ version = "0.4.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"pin-project",
|
||||
"pin-project-lite",
|
||||
"tower-layer",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
|
||||
@@ -10,11 +10,12 @@ serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["fmt", "env-filter"] }
|
||||
tower = "0.4"
|
||||
tower = { version = "0.4", features = ["util"] }
|
||||
tower-http = { version = "0.5", features = ["trace", "normalize-path"] }
|
||||
http = "0.2"
|
||||
utoipa = "4"
|
||||
utoipa-swagger-ui = { version = "7", features = ["axum"] }
|
||||
thiserror = "2.0.17"
|
||||
|
||||
[dev-dependencies]
|
||||
axum = { version = "0.7", features = ["macros"] }
|
||||
|
||||
@@ -28,7 +28,7 @@ mod tests {
|
||||
use super::*;
|
||||
use axum::{body::Body, http::{Request, StatusCode}};
|
||||
use http_body_util::BodyExt;
|
||||
use tower::ServiceExt;
|
||||
use tower::util::ServiceExt;
|
||||
|
||||
use crate::handlers::types::{HealthResponse, MessageResponse};
|
||||
use crate::state::new_state;
|
||||
|
||||
28
src/config.rs
Normal file
28
src/config.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use std::env;
|
||||
use std::net::SocketAddr;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Config {
|
||||
pub host: String,
|
||||
pub port: u16,
|
||||
pub log_level: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn from_env() -> Self {
|
||||
Self {
|
||||
host: env::var("HOST").unwrap_or_else(|_| "0.0.0.0".to_string()),
|
||||
port: env::var("PORT")
|
||||
.ok()
|
||||
.and_then(|p| p.parse().ok())
|
||||
.unwrap_or(8080),
|
||||
log_level: env::var("RUST_LOG").unwrap_or_else(|_| "info".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn socket_addr(&self) -> SocketAddr {
|
||||
format!("{}:{}", self.host, self.port)
|
||||
.parse()
|
||||
.expect("Invalid host/port configuration")
|
||||
}
|
||||
}
|
||||
38
src/error.rs
Normal file
38
src/error.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use axum::{
|
||||
http::StatusCode,
|
||||
response::{IntoResponse, Response},
|
||||
Json,
|
||||
};
|
||||
use serde_json::json;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum AppError {
|
||||
#[error("Internal Server Error")]
|
||||
InternalServerError,
|
||||
#[error("Bad Request: {0}")]
|
||||
BadRequest(String),
|
||||
#[error("Not Found")]
|
||||
NotFound,
|
||||
// Add more errors here as needed, e.g.:
|
||||
// #[error("Database error: {0}")]
|
||||
// DatabaseError(#[from] sqlx::Error),
|
||||
}
|
||||
|
||||
impl IntoResponse for AppError {
|
||||
fn into_response(self) -> Response {
|
||||
let (status, message) = match self {
|
||||
AppError::InternalServerError => (StatusCode::INTERNAL_SERVER_ERROR, "Internal Server Error".to_string()),
|
||||
AppError::BadRequest(msg) => (StatusCode::BAD_REQUEST, msg),
|
||||
AppError::NotFound => (StatusCode::NOT_FOUND, "Not Found".to_string()),
|
||||
};
|
||||
|
||||
let body = Json(json!({
|
||||
"error": message,
|
||||
}));
|
||||
|
||||
(status, body).into_response()
|
||||
}
|
||||
}
|
||||
|
||||
pub type AppResult<T> = Result<T, AppError>;
|
||||
@@ -1,5 +1,6 @@
|
||||
use axum::{Json, extract::{Extension, State}};
|
||||
|
||||
use crate::error::AppResult;
|
||||
use crate::handlers::types::{MessageRequest, MessageResponse};
|
||||
use crate::middleware::request_id::{RequestId, generate_request_id};
|
||||
use crate::state::SharedState;
|
||||
@@ -16,13 +17,13 @@ pub async fn echo(
|
||||
State(_state): State<SharedState>,
|
||||
req_id: Option<Extension<RequestId>>,
|
||||
Json(payload): Json<MessageRequest>,
|
||||
) -> Json<MessageResponse> {
|
||||
) -> AppResult<Json<MessageResponse>> {
|
||||
let id = req_id
|
||||
.map(|rid| rid.0.0)
|
||||
.unwrap_or_else(generate_request_id);
|
||||
|
||||
Json(MessageResponse {
|
||||
Ok(Json(MessageResponse {
|
||||
echoed: payload.message,
|
||||
id,
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use axum::{Json, extract::State};
|
||||
|
||||
use crate::error::AppResult;
|
||||
use crate::handlers::types::HealthResponse;
|
||||
use crate::state::SharedState;
|
||||
|
||||
@@ -10,9 +11,9 @@ use crate::state::SharedState;
|
||||
(status = 200, description = "Health check", body = HealthResponse)
|
||||
)
|
||||
)]
|
||||
pub async fn health(State(state): State<SharedState>) -> Json<HealthResponse> {
|
||||
Json(HealthResponse {
|
||||
pub async fn health(State(state): State<SharedState>) -> AppResult<Json<HealthResponse>> {
|
||||
Ok(Json(HealthResponse {
|
||||
status: "ok".to_string(),
|
||||
service: state.app_name.clone(),
|
||||
})
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utoipa::ToSchema;
|
||||
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct HealthResponse {
|
||||
pub status: String,
|
||||
pub service: String,
|
||||
@@ -12,7 +12,7 @@ pub struct MessageRequest {
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, ToSchema)]
|
||||
#[derive(Debug, Serialize, Deserialize, ToSchema)]
|
||||
pub struct MessageResponse {
|
||||
pub echoed: String,
|
||||
pub id: String,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
pub mod app;
|
||||
pub mod config;
|
||||
pub mod docs;
|
||||
pub mod error;
|
||||
pub mod handlers;
|
||||
pub mod middleware;
|
||||
pub mod state;
|
||||
|
||||
16
src/main.rs
16
src/main.rs
@@ -1,18 +1,19 @@
|
||||
use std::net::SocketAddr;
|
||||
|
||||
use axum::body::Body;
|
||||
use axum::http::Request;
|
||||
use axum::ServiceExt;
|
||||
use rustapi::config::Config;
|
||||
use tower::Layer;
|
||||
use tower_http::normalize_path::NormalizePathLayer;
|
||||
use tracing::info;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
init_tracing();
|
||||
let config = Config::from_env();
|
||||
init_tracing(&config);
|
||||
|
||||
let state = rustapi::state::new_state("rustapi");
|
||||
|
||||
let addr: SocketAddr = ([0, 0, 0, 0], 8080).into();
|
||||
let addr = config.socket_addr();
|
||||
let listener = tokio::net::TcpListener::bind(addr).await?;
|
||||
|
||||
info!("listening on http://{}", addr);
|
||||
@@ -28,12 +29,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn init_tracing() {
|
||||
fn init_tracing(config: &Config) {
|
||||
use tracing_subscriber::{fmt, EnvFilter};
|
||||
|
||||
let filter = EnvFilter::try_from_default_env()
|
||||
.or_else(|_| EnvFilter::try_new("info"))
|
||||
.unwrap();
|
||||
let filter = EnvFilter::try_new(&config.log_level)
|
||||
.unwrap_or_else(|_| EnvFilter::new("info"));
|
||||
|
||||
fmt()
|
||||
.with_env_filter(filter)
|
||||
|
||||
Reference in New Issue
Block a user