alux_http_openapi/
handler.rs1use crate::input::{
4 OpenApiBodyInput, OpenApiCookieInput, OpenApiFormInput, OpenApiHeaderInput, OpenApiInputsAlg,
5 OpenApiMultipartInput, OpenApiPathInput, OpenApiQueryInput, OpenApiRawBodyInput, OpenApiUnstatedInput,
6};
7use crate::output::OpenApiOutputAlg;
8use crate::route::{OpenApiEndpoint, OpenApiRoute, OpenApiRouteImpl, OpenApiSelector};
9use alux_ext::{ApplyAlg, HandlerContextAlg, OperationAlg};
10use alux_http::{
11 HandlerAlg, HandlerEndpointAlg, HttpInputAlg, HttpMethod, HttpSelectorAlg, OutputKindAlg, RouteAlg, RoutePath,
12 SelectorAlg,
13};
14use alux_shape::Spelling;
15use alux_shape_jsonschema::JsonSchemaShape;
16use serde_json::{Value, json};
17use std::sync::Arc;
18
19pub struct OpenApiHandlerImpl<Context> {
25 schema: JsonSchemaShape,
26 context: core::marker::PhantomData<fn() -> Context>,
27}
28
29impl<Context> Default for OpenApiHandlerImpl<Context> {
30 fn default() -> Self {
31 Self::new()
32 }
33}
34
35impl<Context> OpenApiHandlerImpl<Context> {
36 pub fn new() -> Self {
38 Self {
39 schema: JsonSchemaShape::new(Spelling::Snake, "#/components/schemas/"),
40 context: core::marker::PhantomData,
41 }
42 }
43
44 pub fn document(&self, title: &str, version: &str, route: &OpenApiRoute) -> Value {
46 let paths = route.paths_object();
47 let schemas = self.schema.definitions().into_iter().collect::<serde_json::Map<_, _>>();
48 let mut document = json!({
49 "openapi": "3.1.0",
50 "info": { "title": title, "version": version },
51 "paths": paths,
52 });
53 if !schemas.is_empty()
54 && let Some(document) = document.as_object_mut()
55 {
56 document.insert("components".into(), json!({ "schemas": Value::Object(schemas) }));
57 }
58
59 document
60 }
61}
62
63impl<Context> HandlerContextAlg<Context> for OpenApiHandlerImpl<Context>
64where
65 Context: Send + Sync + 'static,
66{
67 type Handle = Arc<Context>;
68}
69
70impl<Context> HandlerAlg for OpenApiHandlerImpl<Context> {
71 type Endpoint = OpenApiEndpoint;
72}
73
74impl<Context> HttpInputAlg for OpenApiHandlerImpl<Context> {
75 type Path<I> = OpenApiPathInput<I>;
76 type Query<I> = OpenApiQueryInput<I>;
77 type Body<I> = OpenApiBodyInput<I>;
78 type Form<I> = OpenApiFormInput<I>;
79 type Multipart<I> = OpenApiMultipartInput<I>;
80 type RawBody<I> = OpenApiRawBodyInput<I>;
81 type Header<I> = OpenApiHeaderInput<I>;
82 type Cookie<I> = OpenApiCookieInput<I>;
83 type Auth<I> = OpenApiHeaderInput<I>;
85 type Context<I> = OpenApiUnstatedInput<I>;
86}
87
88impl<Context, Inputs, Args, Transform, Answering, Output>
89 HandlerEndpointAlg<Arc<Context>, Inputs, Args, Transform, Output> for OpenApiHandlerImpl<Context>
90where
91 Context: Send + Sync + 'static,
92 Inputs: OpenApiInputsAlg,
93 Transform: OutputKindAlg<Self, Output, Transform = Answering>,
94 Answering: OpenApiOutputAlg<Output>,
95{
96 fn finish_handler<Handler>(&self, _handler: Handler) -> <Self as HandlerAlg>::Endpoint
97 where
98 Handler: OperationAlg + ApplyAlg<Arc<Context>, Args, Output = Output> + Send + Sync + 'static,
99 {
100 let arguments = Handler::ARG_NAMES.iter().copied().zip(Inputs::describe(&self.schema)).collect();
103
104 OpenApiEndpoint {
105 operation: Handler::NAME,
106 doc: Handler::DOC,
107 arguments,
108 answers: Answering::answers(&self.schema),
109 }
110 }
111}
112
113impl<Context> SelectorAlg for OpenApiHandlerImpl<Context> {
114 type Selector = OpenApiSelector;
115
116 fn identity(&self) -> Self::Selector {
117 SelectorAlg::identity(&OpenApiRouteImpl)
118 }
119
120 fn compose(&self, first: Self::Selector, second: Self::Selector) -> Self::Selector {
121 SelectorAlg::compose(&OpenApiRouteImpl, first, second)
122 }
123}
124
125impl<Context> RouteAlg for OpenApiHandlerImpl<Context> {
126 type Route = OpenApiRoute;
127 type Selector = OpenApiSelector;
128 type Endpoint = OpenApiEndpoint;
129
130 fn initial(&self) -> Self::Route {
131 RouteAlg::initial(&OpenApiRouteImpl)
132 }
133
134 fn coproduct(&self, left: Self::Route, right: Self::Route) -> Self::Route {
135 RouteAlg::coproduct(&OpenApiRouteImpl, left, right)
136 }
137
138 fn precompose(&self, selector: Self::Selector, route: Self::Route) -> Self::Route {
139 RouteAlg::precompose(&OpenApiRouteImpl, selector, route)
140 }
141
142 fn lift(&self, endpoint: Self::Endpoint) -> Self::Route {
143 RouteAlg::lift(&OpenApiRouteImpl, endpoint)
144 }
145}
146
147impl<Context> HttpSelectorAlg for OpenApiHandlerImpl<Context> {
148 type Selector = OpenApiSelector;
149
150 fn http_method(&self, method: HttpMethod) -> Self::Selector {
151 HttpSelectorAlg::http_method(&OpenApiRouteImpl, method)
152 }
153
154 fn http_path(&self, path: &RoutePath) -> Self::Selector {
155 HttpSelectorAlg::http_path(&OpenApiRouteImpl, path)
156 }
157
158 fn http_prefix(&self, prefix: &RoutePath) -> Self::Selector {
159 HttpSelectorAlg::http_prefix(&OpenApiRouteImpl, prefix)
160 }
161}