1use crate::error::{ParseError, ParserResult};
2use crate::hir;
3use serde::{Deserialize, Serialize};
4
5#[cfg(test)]
6mod tests;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9pub enum HttpMethod {
10 Get,
11 Post,
12 Put,
13 Patch,
14 Delete,
15 Head,
16 Options,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
20pub enum HttpParamKind {
21 Path,
22 Query,
23 Header,
24 Cookie,
25 Body,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
29pub enum HttpOperationSource {
30 Method,
31 AttributeGet,
32 AttributeSet,
33 AttributeWatch,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37pub struct HttpRoute {
38 pub path: String,
39 pub path_params: Vec<String>,
40 pub query_params: Vec<String>,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct HttpParam {
45 pub name: String,
46 pub wire_name: String,
47 pub ty: hir::TypeSpec,
48 pub kind: HttpParamKind,
49 pub optional: bool,
50 pub flatten: bool,
51}
52
53#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
54pub struct HttpDocumentServer {
55 pub base_url: String,
56 pub description: Option<String>,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize, Default)]
60pub struct HttpDocumentMetadata {
61 pub package: Option<String>,
62 pub version: Option<String>,
63 pub servers: Vec<HttpDocumentServer>,
64 #[serde(default)]
66 pub exceptions: Vec<HttpException>,
67}
68
69#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct HttpException {
75 pub module_path: Vec<String>,
76 pub ident: String,
77 pub status: u16,
79 pub headers: Vec<HttpExceptionMember>,
80 pub cookies: Vec<HttpExceptionMember>,
81 pub body: Vec<HttpExceptionField>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct HttpExceptionMember {
87 pub field: String,
88 pub wire_name: String,
89 pub ty: hir::TypeSpec,
90 pub is_multi: bool,
92 #[serde(default)]
95 pub optional: bool,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct HttpExceptionField {
101 pub field: String,
102 pub wire_name: String,
104 pub optional: bool,
106 pub ty: hir::TypeSpec,
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
111pub struct HttpExceptionRef {
112 pub module_path: Vec<String>,
113 pub ident: String,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117pub struct HttpOperation {
118 pub meta: HttpOperationMeta,
119 pub signature: HttpOperationSignature,
120 pub http: HttpOperationHttpMapping,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
124pub struct HttpOperationMeta {
125 pub name: String,
126 pub operation_id: String,
127 pub source: HttpOperationSource,
128 pub method: HttpMethod,
129 pub routes: Vec<HttpRoute>,
130 pub stream: super::semantics::HttpStreamConfig,
131 pub cors: Option<super::semantics::HttpCorsProfile>,
132 pub security: Option<super::semantics::HttpSecurityProfile>,
133 pub basic_auth_realm: Option<String>,
134 pub deprecated: Option<super::semantics::DeprecatedInfo>,
135 pub upgrade_protocol: Option<String>,
136 pub upgrade_mode: Option<super::semantics::UpgradeMode>,
137 pub websocket: Option<super::semantics::WebSocketConfig>,
138 #[serde(default)]
140 pub raises: Vec<HttpExceptionRef>,
141}
142
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct HttpOperationSignature {
145 pub params: Vec<HttpSignatureParam>,
146 pub return_type: Option<hir::TypeSpec>,
147}
148
149#[derive(Debug, Clone, Serialize, Deserialize)]
150pub struct HttpSignatureParam {
151 pub name: String,
152 pub ty: hir::TypeSpec,
153 pub direction: HttpSignatureParamDirection,
154 pub is_optional: bool,
155 pub is_flatten: bool,
156 pub annotations: Vec<HttpSignatureParamAnnotation>,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
160pub enum HttpSignatureParamDirection {
161 In,
162 Out,
163 InOut,
164}
165
166#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
167pub enum HttpSignatureParamAnnotation {
168 Optional,
169 Flatten,
170 Path { name: String },
171 Query { name: String },
172 Header { name: String },
173 Cookie { name: String },
174 Body,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
178pub struct HttpOperationHttpMapping {
179 pub request: HttpRequestMapping,
180 pub response: HttpResponseMapping,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
184pub struct HttpRequestMapping {
185 pub path: Vec<HttpInputBinding>,
186 pub query: Vec<HttpInputBinding>,
187 pub header: Vec<HttpInputBinding>,
188 pub cookie: Vec<HttpInputBinding>,
189 pub body: HttpRequestBodyMapping,
190}
191
192#[derive(Debug, Clone, Serialize, Deserialize)]
193pub struct HttpInputBinding {
194 pub source_param: String,
195 pub wire_name: String,
196 pub ty: hir::TypeSpec,
197 pub optional: bool,
198}
199
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct HttpRequestBodyMapping {
202 pub content_type: Option<String>,
203 pub content_type_explicit: bool,
204 pub codec: Option<HttpBodyCodec>,
205 pub shape: HttpRequestBodyShape,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
209pub enum HttpRequestBodyShape {
210 Empty,
211 SingleValue {
212 source_param: String,
213 flatten: bool,
214 ty: hir::TypeSpec,
215 },
216 Object {
217 fields: Vec<HttpRequestBodyField>,
218 },
219 Stream {
220 source_param: String,
221 item_ty: hir::TypeSpec,
222 codec: HttpStreamPayloadCodec,
223 },
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct HttpRequestBodyField {
228 pub source_param: String,
229 pub field_name: String,
230 pub ty: hir::TypeSpec,
231 pub optional: bool,
232 pub flatten: bool,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct HttpResponseMapping {
237 pub header: Vec<HttpOutputBinding>,
238 pub cookie: Vec<HttpOutputBinding>,
239 pub body: HttpResponseBodyMapping,
240 pub status: String,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
244pub struct HttpOutputBinding {
245 pub source: HttpOutputSource,
246 pub wire_name: String,
247 pub ty: hir::TypeSpec,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub enum HttpOutputSource {
252 ReturnValue,
253 Param { name: String },
254}
255
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct HttpResponseBodyMapping {
258 pub content_type: Option<String>,
259 pub content_type_explicit: bool,
260 pub codec: Option<HttpBodyCodec>,
261 pub shape: HttpResponseBodyShape,
262}
263
264#[derive(Debug, Clone, Serialize, Deserialize)]
265pub enum HttpResponseBodyShape {
266 Empty,
267 ReturnOnly {
268 ty: hir::TypeSpec,
269 },
270 SingleValue {
271 source: HttpOutputSource,
272 ty: hir::TypeSpec,
273 },
274 Object {
275 fields: Vec<HttpResponseBodyField>,
276 },
277 Stream {
278 item_source: HttpOutputSource,
279 item_ty: hir::TypeSpec,
280 codec: HttpStreamPayloadCodec,
281 },
282}
283
284#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct HttpResponseBodyField {
286 pub source: HttpOutputSource,
287 pub field_name: String,
288 pub ty: hir::TypeSpec,
289}
290
291#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
292pub enum HttpBodyCodec {
293 Json,
294 Text,
295 FormUrlEncoded,
296 Msgpack,
297}
298
299#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
300pub enum HttpStreamPayloadCodec {
301 Ndjson,
302 Sse,
303 Bytes,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
307pub struct HttpInterface {
308 pub name: String,
309 pub module_path: Vec<String>,
310 pub operations: Vec<HttpOperation>,
311}
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
314pub struct RestHirDocument {
315 pub spec: hir::Specification,
316 pub document: HttpDocumentMetadata,
317 pub interfaces: Vec<HttpInterface>,
318}
319
320impl RestHirDocument {
321 pub fn from_props(props: &hir::ParserProperties) -> ParserResult<Self> {
322 let value = props
323 .get("rest_hir")
324 .cloned()
325 .ok_or_else(|| ParseError::Message("missing rest_hir properties".to_string()))?;
326 serde_json::from_value(value).map_err(|err| ParseError::Message(err.to_string()))
327 }
328
329 pub fn find_interface(&self, module_path: &[String], name: &str) -> Option<&HttpInterface> {
330 self.interfaces
331 .iter()
332 .find(|interface| interface.name == name && interface.module_path == module_path)
333 }
334}