ash_rpc_core/
utils.rs

1//! Utility functions for JSON-RPC documentation generation.
2
3use crate::traits::MethodInfo;
4use std::collections::HashMap;
5
6/// Generate OpenAPI/Swagger documentation for JSON-RPC methods
7pub fn render_docs(method_info: &HashMap<String, MethodInfo>) -> serde_json::Value {
8    let mut methods = serde_json::Map::new();
9
10    for (method_name, info) in method_info {
11        let mut method_doc = serde_json::Map::new();
12
13        // Basic method info
14        method_doc.insert(
15            "summary".to_string(),
16            serde_json::Value::String(
17                info.description
18                    .clone()
19                    .unwrap_or_else(|| format!("Execute {} method", method_name)),
20            ),
21        );
22
23        // Request body schema
24        let mut request_body = serde_json::Map::new();
25        let mut content = serde_json::Map::new();
26        let mut json_content = serde_json::Map::new();
27        let mut schema = serde_json::Map::new();
28
29        schema.insert(
30            "type".to_string(),
31            serde_json::Value::String("object".to_string()),
32        );
33
34        let mut properties = serde_json::Map::new();
35        properties.insert(
36            "jsonrpc".to_string(),
37            serde_json::json!({
38                "type": "string",
39                "enum": ["2.0"],
40                "description": "JSON-RPC version"
41            }),
42        );
43        properties.insert(
44            "method".to_string(),
45            serde_json::json!({
46                "type": "string",
47                "enum": [method_name],
48                "description": "Method name"
49            }),
50        );
51        properties.insert(
52            "id".to_string(),
53            serde_json::json!({
54                "oneOf": [
55                    {"type": "string"},
56                    {"type": "number"},
57                    {"type": "null"}
58                ],
59                "description": "Request identifier"
60            }),
61        );
62
63        if let Some(params_schema) = &info.params_schema {
64            properties.insert("params".to_string(), params_schema.clone());
65        }
66
67        schema.insert(
68            "properties".to_string(),
69            serde_json::Value::Object(properties),
70        );
71        schema.insert(
72            "required".to_string(),
73            serde_json::json!(["jsonrpc", "method"]),
74        );
75
76        json_content.insert("schema".to_string(), serde_json::Value::Object(schema));
77        content.insert(
78            "application/json".to_string(),
79            serde_json::Value::Object(json_content),
80        );
81        request_body.insert("content".to_string(), serde_json::Value::Object(content));
82        request_body.insert("required".to_string(), serde_json::Value::Bool(true));
83
84        method_doc.insert(
85            "requestBody".to_string(),
86            serde_json::Value::Object(request_body),
87        );
88
89        // Response schema
90        let mut responses = serde_json::Map::new();
91        let mut success_response = serde_json::Map::new();
92        success_response.insert(
93            "description".to_string(),
94            serde_json::Value::String("Successful response".to_string()),
95        );
96
97        let mut success_content = serde_json::Map::new();
98        let mut success_json = serde_json::Map::new();
99        let mut success_schema = serde_json::Map::new();
100
101        success_schema.insert(
102            "type".to_string(),
103            serde_json::Value::String("object".to_string()),
104        );
105
106        let mut success_properties = serde_json::Map::new();
107        success_properties.insert(
108            "jsonrpc".to_string(),
109            serde_json::json!({
110                "type": "string",
111                "enum": ["2.0"]
112            }),
113        );
114        success_properties.insert(
115            "id".to_string(),
116            serde_json::json!({
117                "oneOf": [
118                    {"type": "string"},
119                    {"type": "number"},
120                    {"type": "null"}
121                ]
122            }),
123        );
124
125        if let Some(result_schema) = &info.result_schema {
126            success_properties.insert("result".to_string(), result_schema.clone());
127        } else {
128            success_properties.insert(
129                "result".to_string(),
130                serde_json::json!({
131                    "description": "Method result"
132                }),
133            );
134        }
135
136        success_schema.insert(
137            "properties".to_string(),
138            serde_json::Value::Object(success_properties),
139        );
140        success_schema.insert(
141            "required".to_string(),
142            serde_json::json!(["jsonrpc", "result"]),
143        );
144
145        success_json.insert(
146            "schema".to_string(),
147            serde_json::Value::Object(success_schema),
148        );
149        success_content.insert(
150            "application/json".to_string(),
151            serde_json::Value::Object(success_json),
152        );
153        success_response.insert(
154            "content".to_string(),
155            serde_json::Value::Object(success_content),
156        );
157
158        responses.insert(
159            "200".to_string(),
160            serde_json::Value::Object(success_response),
161        );
162
163        // Error response
164        let error_response = serde_json::json!({
165            "description": "Error response",
166            "content": {
167                "application/json": {
168                    "schema": {
169                        "type": "object",
170                        "properties": {
171                            "jsonrpc": {
172                                "type": "string",
173                                "enum": ["2.0"]
174                            },
175                            "error": {
176                                "type": "object",
177                                "properties": {
178                                    "code": {
179                                        "type": "integer",
180                                        "description": "Error code"
181                                    },
182                                    "message": {
183                                        "type": "string",
184                                        "description": "Error message"
185                                    },
186                                    "data": {
187                                        "description": "Additional error data"
188                                    }
189                                },
190                                "required": ["code", "message"]
191                            },
192                            "id": {
193                                "oneOf": [
194                                    {"type": "string"},
195                                    {"type": "number"},
196                                    {"type": "null"}
197                                ]
198                            }
199                        },
200                        "required": ["jsonrpc", "error"]
201                    }
202                }
203            }
204        });
205
206        responses.insert("400".to_string(), error_response);
207        method_doc.insert(
208            "responses".to_string(),
209            serde_json::Value::Object(responses),
210        );
211
212        methods.insert(method_name.clone(), serde_json::Value::Object(method_doc));
213    }
214
215    // Build the full OpenAPI document
216    serde_json::json!({
217        "openapi": "3.0.3",
218        "info": {
219            "title": "JSON-RPC API",
220            "description": "Auto-generated documentation for JSON-RPC methods",
221            "version": "1.0.0"
222        },
223        "servers": [
224            {
225                "url": "/",
226                "description": "JSON-RPC endpoint"
227            }
228        ],
229        "paths": {
230            "/": {
231                "post": {
232                    "summary": "JSON-RPC endpoint",
233                    "description": "Execute JSON-RPC methods",
234                    "requestBody": {
235                        "required": true,
236                        "content": {
237                            "application/json": {
238                                "schema": {
239                                    "oneOf": methods.values().map(|method| {
240                                        if let serde_json::Value::Object(method_obj) = method {
241                                            if let Some(serde_json::Value::Object(request_body)) = method_obj.get("requestBody") {
242                                                if let Some(serde_json::Value::Object(content)) = request_body.get("content") {
243                                                    if let Some(serde_json::Value::Object(json_content)) = content.get("application/json") {
244                                                        return json_content.get("schema").cloned().unwrap_or(serde_json::Value::Null);
245                                                    }
246                                                }
247                                            }
248                                        }
249                                        serde_json::Value::Null
250                                    }).collect::<Vec<_>>()
251                                }
252                            }
253                        }
254                    },
255                    "responses": {
256                        "200": {
257                            "description": "Successful response",
258                            "content": {
259                                "application/json": {
260                                    "schema": {
261                                        "oneOf": [
262                                            {
263                                                "type": "object",
264                                                "properties": {
265                                                    "jsonrpc": {"type": "string", "enum": ["2.0"]},
266                                                    "result": {"description": "Method result"},
267                                                    "id": {"oneOf": [{"type": "string"}, {"type": "number"}, {"type": "null"}]}
268                                                },
269                                                "required": ["jsonrpc", "result"]
270                                            },
271                                            {
272                                                "type": "object",
273                                                "properties": {
274                                                    "jsonrpc": {"type": "string", "enum": ["2.0"]},
275                                                    "error": {
276                                                        "type": "object",
277                                                        "properties": {
278                                                            "code": {"type": "integer"},
279                                                            "message": {"type": "string"},
280                                                            "data": {"description": "Additional error data"}
281                                                        },
282                                                        "required": ["code", "message"]
283                                                    },
284                                                    "id": {"oneOf": [{"type": "string"}, {"type": "number"}, {"type": "null"}]}
285                                                },
286                                                "required": ["jsonrpc", "error"]
287                                            }
288                                        ]
289                                    }
290                                }
291                            }
292                        }
293                    }
294                }
295            }
296        },
297        "components": {
298            "schemas": {
299                "JsonRpcRequest": {
300                    "type": "object",
301                    "properties": {
302                        "jsonrpc": {"type": "string", "enum": ["2.0"]},
303                        "method": {"type": "string"},
304                        "params": {"description": "Method parameters"},
305                        "id": {"oneOf": [{"type": "string"}, {"type": "number"}, {"type": "null"}]}
306                    },
307                    "required": ["jsonrpc", "method"]
308                },
309                "JsonRpcSuccessResponse": {
310                    "type": "object",
311                    "properties": {
312                        "jsonrpc": {"type": "string", "enum": ["2.0"]},
313                        "result": {"description": "Method result"},
314                        "id": {"oneOf": [{"type": "string"}, {"type": "number"}, {"type": "null"}]}
315                    },
316                    "required": ["jsonrpc", "result"]
317                },
318                "JsonRpcErrorResponse": {
319                    "type": "object",
320                    "properties": {
321                        "jsonrpc": {"type": "string", "enum": ["2.0"]},
322                        "error": {
323                            "type": "object",
324                            "properties": {
325                                "code": {"type": "integer"},
326                                "message": {"type": "string"},
327                                "data": {"description": "Additional error data"}
328                            },
329                            "required": ["code", "message"]
330                        },
331                        "id": {"oneOf": [{"type": "string"}, {"type": "number"}, {"type": "null"}]}
332                    },
333                    "required": ["jsonrpc", "error"]
334                }
335            }
336        },
337        "tags": [
338            {
339                "name": "JSON-RPC",
340                "description": "JSON-RPC 2.0 methods"
341            }
342        ]
343    })
344}