use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct EndpointMetadata {
#[serde(skip_serializing_if = "Option::is_none")]
pub summary: Option<String>,
#[serde(rename = "requestBody", skip_serializing_if = "Option::is_none")]
pub request_body: Option<RequestBodySpec>,
pub responses: HashMap<String, ResponseSpec>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ResponseSpec {
pub description: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<HashMap<String, MediaTypeSpec>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MediaTypeSpec {
pub schema: SchemaRef,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SchemaRef {
#[serde(rename = "$ref")]
pub reference: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OpenApiSpec {
pub openapi: String,
pub info: InfoSpec,
pub paths: HashMap<String, HashMap<String, EndpointMetadata>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub components: Option<ComponentsSpec>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InfoSpec {
pub title: String,
pub version: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequestBodySpec {
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
pub required: bool,
pub content: HashMap<String, MediaTypeSpec>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SchemaObject {
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub schema_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub format: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub properties: Option<HashMap<String, SchemaObject>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub required: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub items: Option<Box<SchemaObject>>,
#[serde(rename = "$ref", skip_serializing_if = "Option::is_none")]
pub reference: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ComponentsSpec {
#[serde(skip_serializing_if = "HashMap::is_empty")]
pub schemas: HashMap<String, SchemaObject>,
}
pub trait Schema {
fn schema_name() -> &'static str;
fn schema_object() -> SchemaObject;
}
impl EndpointMetadata {
pub fn new() -> Self {
Self {
summary: None,
request_body: None,
responses: HashMap::new(),
}
}
pub fn with_summary(mut self, summary: &str) -> Self {
self.summary = Some(summary.to_string());
self
}
pub fn set_request_body(
&mut self,
json_model: &str,
description: Option<&str>,
required: bool,
) {
let mut media_types = HashMap::new();
media_types.insert(
"application/json".to_string(),
MediaTypeSpec {
schema: SchemaRef {
reference: format!("#/components/schemas/{}", json_model),
},
},
);
self.request_body = Some(RequestBodySpec {
description: description.map(|d| d.to_string()),
required,
content: media_types,
});
}
pub fn add_response(&mut self, status: u16, description: &str, json_model: Option<&str>) {
let content = json_model.map(|model| {
let mut media_types = HashMap::new();
media_types.insert(
"application/json".to_string(),
MediaTypeSpec {
schema: SchemaRef {
reference: format!("#/components/schemas/{}", model),
},
},
);
media_types
});
self.responses.insert(
status.to_string(),
ResponseSpec {
description: description.to_string(),
content,
},
);
}
}