Skip to main content

better_fetch/openapi/
document.rs

1//! Serializable OpenAPI 3.0 document types.
2
3use indexmap::IndexMap;
4use serde::Serialize;
5
6/// OpenAPI 3.0 document.
7#[derive(Debug, Clone, Serialize)]
8#[serde(rename_all = "camelCase")]
9pub struct OpenApiDocument {
10    pub openapi: String,
11    pub info: OpenApiInfo,
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub servers: Option<Vec<OpenApiServer>>,
14    pub paths: IndexMap<String, IndexMap<String, OpenApiOperation>>,
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub components: Option<OpenApiComponents>,
17}
18
19/// API metadata (`info`).
20#[derive(Debug, Clone, Serialize)]
21#[serde(rename_all = "camelCase")]
22pub struct OpenApiInfo {
23    pub title: String,
24    pub version: String,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub description: Option<String>,
27}
28
29/// Server URL (`servers[]`).
30#[derive(Debug, Clone, Serialize)]
31#[serde(rename_all = "camelCase")]
32pub struct OpenApiServer {
33    pub url: String,
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub description: Option<String>,
36}
37
38/// Reusable schemas (`components.schemas`).
39#[derive(Debug, Clone, Serialize)]
40#[serde(rename_all = "camelCase")]
41pub struct OpenApiComponents {
42    pub schemas: IndexMap<String, serde_json::Value>,
43}
44
45/// JSON Schema reference or inline schema.
46#[derive(Debug, Clone, Serialize)]
47#[serde(untagged)]
48pub enum OpenApiSchemaRef {
49    Ref {
50        #[serde(rename = "$ref")]
51        ref_path: String,
52    },
53    Inline(serde_json::Value),
54}
55
56/// Path or query parameter.
57#[derive(Debug, Clone, Serialize)]
58#[serde(rename_all = "camelCase")]
59pub struct OpenApiParameter {
60    pub name: String,
61    #[serde(rename = "in")]
62    pub location: String,
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub description: Option<String>,
65    pub required: bool,
66    pub schema: OpenApiSchemaRef,
67}
68
69/// Request body with JSON content.
70#[derive(Debug, Clone, Serialize)]
71#[serde(rename_all = "camelCase")]
72pub struct OpenApiRequestBody {
73    #[serde(skip_serializing_if = "Option::is_none")]
74    pub description: Option<String>,
75    pub required: bool,
76    pub content: IndexMap<String, OpenApiMediaType>,
77}
78
79/// Media type entry (`content.application/json`).
80#[derive(Debug, Clone, Serialize)]
81pub struct OpenApiMediaType {
82    pub schema: OpenApiSchemaRef,
83}
84
85/// Operation responses map.
86#[derive(Debug, Clone, Serialize)]
87pub struct OpenApiResponses {
88    #[serde(flatten)]
89    pub statuses: IndexMap<String, OpenApiResponse>,
90}
91
92/// Single HTTP response.
93#[derive(Debug, Clone, Serialize)]
94#[serde(rename_all = "camelCase")]
95pub struct OpenApiResponse {
96    pub description: String,
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub content: Option<IndexMap<String, OpenApiMediaType>>,
99}
100
101/// Path operation (`get`, `post`, …).
102#[derive(Debug, Clone, Serialize)]
103#[serde(rename_all = "camelCase")]
104pub struct OpenApiOperation {
105    #[serde(skip_serializing_if = "Option::is_none")]
106    pub summary: Option<String>,
107    #[serde(skip_serializing_if = "Option::is_none")]
108    pub description: Option<String>,
109    #[serde(skip_serializing_if = "Option::is_none")]
110    pub operation_id: Option<String>,
111    #[serde(skip_serializing_if = "Vec::is_empty")]
112    pub parameters: Vec<OpenApiParameter>,
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub request_body: Option<OpenApiRequestBody>,
115    pub responses: OpenApiResponses,
116}
117
118impl OpenApiDocument {
119    /// Serialize the document to pretty-printed JSON.
120    pub fn to_json_pretty(&self) -> serde_json::Result<String> {
121        serde_json::to_string_pretty(self)
122    }
123
124    /// Serialize the document to JSON.
125    pub fn to_json(&self) -> serde_json::Result<String> {
126        serde_json::to_string(self)
127    }
128}