Skip to main content

xidl_parser/rest_hir/model/
mod.rs

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    /// Document-level `exception` declarations projected for HTTP use.
65    #[serde(default)]
66    pub exceptions: Vec<HttpException>,
67}
68
69/// An `exception` declaration with its HTTP error-channel semantics.
70///
71/// Members annotated `@header`/`@cookie` become the error response's
72/// headers/cookies; unannotated members form the JSON error body.
73#[derive(Debug, Clone, Serialize, Deserialize)]
74pub struct HttpException {
75    pub module_path: Vec<String>,
76    pub ident: String,
77    /// HTTP status code bound via `@http(<status>)` / `@http(status = <status>)`.
78    pub status: u16,
79    pub headers: Vec<HttpExceptionMember>,
80    pub cookies: Vec<HttpExceptionMember>,
81    pub body: Vec<HttpExceptionField>,
82}
83
84/// A `@header`/`@cookie` annotated member of an exception.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct HttpExceptionMember {
87    pub field: String,
88    pub wire_name: String,
89    pub ty: hir::TypeSpec,
90    /// `sequence<...>` members repeat the header (append) instead of replacing it.
91    pub is_multi: bool,
92    /// `@optional` members may be absent: the field is `Option<_>` and the
93    /// header is only written when a value exists.
94    #[serde(default)]
95    pub optional: bool,
96}
97
98/// An unannotated member of an exception: part of the JSON error body.
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct HttpExceptionField {
101    pub field: String,
102    /// JSON property name after applying the existing naming annotations.
103    pub wire_name: String,
104    /// Whether the JSON property may be absent.
105    pub optional: bool,
106    pub ty: hir::TypeSpec,
107}
108
109/// A `raises(...)` entry on an operation, resolved to a declared exception.
110#[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    /// Exceptions declared via `raises(...)` on this operation.
139    #[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}