#[cfg(test)]
mod tests;
use std::sync::Arc;
use crate::app::App;
use crate::application::Application;
use crate::core::New;
use crate::middleware::{Middleware, WithMiddleware};
use crate::request::Request;
use crate::response::Response;
use crate::router::{PathParams, Router};
use crate::server::ConnectionInfo;
use crate::server_config::ServerConfig;
#[cfg(feature = "openapi")]
use crate::mime_type::MimeType;
#[cfg(feature = "openapi")]
use crate::range::Range;
#[cfg(feature = "openapi")]
use crate::response::STATUS_CODE_REASON_PHRASE;
#[derive(Clone)]
pub struct AppWithState<S> {
state: Arc<S>,
router: Router,
config: Option<Arc<ServerConfig>>,
}
impl<S: Send + Sync + 'static> AppWithState<S> {
pub fn new(state: S) -> Self {
AppWithState {
state: Arc::new(state),
router: Router::new(),
config: None,
}
}
pub fn with_config(mut self, config: ServerConfig) -> Self {
self.config = Some(Arc::new(config));
self
}
pub fn state(&self) -> &S {
&self.state
}
fn fallback_app(&self) -> App {
match &self.config {
Some(c) => App::with_config((**c).clone()),
None => App::new(),
}
}
pub fn get<F>(mut self, pattern: &str, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
let state = Arc::clone(&self.state);
self.router = self.router.get(pattern, move |req, params, conn| {
handler(req, params, conn, &state)
});
self
}
pub fn post<F>(mut self, pattern: &str, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
let state = Arc::clone(&self.state);
self.router = self.router.post(pattern, move |req, params, conn| {
handler(req, params, conn, &state)
});
self
}
pub fn put<F>(mut self, pattern: &str, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
let state = Arc::clone(&self.state);
self.router = self.router.put(pattern, move |req, params, conn| {
handler(req, params, conn, &state)
});
self
}
pub fn patch<F>(mut self, pattern: &str, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
let state = Arc::clone(&self.state);
self.router = self.router.patch(pattern, move |req, params, conn| {
handler(req, params, conn, &state)
});
self
}
pub fn delete<F>(mut self, pattern: &str, handler: F) -> Self
where
F: Fn(&Request, &PathParams, &ConnectionInfo, &S) -> Response + Send + Sync + 'static,
{
let state = Arc::clone(&self.state);
self.router = self.router.delete(pattern, move |req, params, conn| {
handler(req, params, conn, &state)
});
self
}
pub fn route_entries(&self) -> Vec<crate::router::RouteInfo> {
self.router.route_entries()
}
pub fn mcp(self, name: impl Into<String>, version: impl Into<String>) -> crate::mcp::McpServer {
crate::mcp::McpServer::new(name, version).wrap(self)
}
pub fn wrap<M: Middleware + 'static>(self, layer: M) -> WithMiddleware<AppWithState<S>> {
WithMiddleware::new(self).wrap(layer)
}
#[cfg(feature = "openapi")]
pub fn openapi(self, config: crate::openapi::OpenApiConfig) -> Self {
let spec_json = Arc::new(crate::openapi::build_spec(&config, &self.route_entries()));
let html = Arc::new(crate::openapi::swagger_ui_html("/openapi.json"));
let spec_for_route = Arc::clone(&spec_json);
self.get("/openapi.json", move |_req, _params, _conn, _state| {
let mut r = Response::new();
r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code;
r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string();
r.content_range_list = vec![Range::get_content_range(
spec_for_route.as_bytes().to_vec(),
MimeType::APPLICATION_JSON.to_string(),
)];
r
})
.get("/docs", move |_req, _params, _conn, _state| {
let mut r = Response::new();
r.status_code = *STATUS_CODE_REASON_PHRASE.n200_ok.status_code;
r.reason_phrase = STATUS_CODE_REASON_PHRASE.n200_ok.reason_phrase.to_string();
r.content_range_list = vec![Range::get_content_range(
html.as_bytes().to_vec(),
MimeType::TEXT_HTML.to_string(),
)];
r
})
}
}
impl<S: Send + Sync + 'static> Application for AppWithState<S> {
fn execute(&self, request: &Request, connection: &ConnectionInfo) -> Result<Response, String> {
if let Some(response) = self.router.handle(request, connection) {
return Ok(response);
}
self.fallback_app().execute(request, connection)
}
}