use std::io::Write;
use anyhow::Result;
use crate::models::{ApiDocumentation, DetailLevel, DocConfig, Endpoint};
use crate::utils::{clean_for_id, extract_content_type};
use super::examples::write_examples;
use super::schema::{response_schema, write_schema_table};
pub(super) fn write_endpoint<W: Write>(
writer: &mut W,
endpoint: &Endpoint,
doc: &ApiDocumentation,
config: &DocConfig,
include_heading: bool,
) -> Result<()> {
let title = get_short_title(endpoint);
if include_heading {
let anchor = clean_for_id(&title);
writeln!(writer, "### {} {{#{}}}", title, anchor)?;
} else {
writeln!(writer, "**{}**", title)?;
}
writeln!(
writer,
"**Operation:** {} {}",
endpoint.method, endpoint.path
)?;
if let Some(description) = &endpoint.description {
writeln!(writer, "**Description:** {}", description)?;
} else if let Some(summary) = &endpoint.summary {
writeln!(writer, "**Description:** {}", summary)?;
}
if endpoint.deprecated {
writeln!(writer, "\n> **Deprecated**: This endpoint is deprecated.")?;
}
if let Some(operation_id) = &endpoint.operation_id {
writeln!(writer, "**Operation ID:** `{}`", operation_id)?;
}
if config.detail_level != DetailLevel::Basic {
if !endpoint.parameters.is_empty() {
writeln!(writer, "\n#### Parameters")?;
writeln!(writer, "| Name | In | Required | Description |")?;
writeln!(writer, "|------|----|---------:|-------------|")?;
for param in &endpoint.parameters {
if config.required_only && !param.required.unwrap_or(false) {
continue;
}
let required_str = if param.required.unwrap_or(false) {
"Yes"
} else {
"No"
};
let desc = param.description.as_deref().unwrap_or("-");
writeln!(
writer,
"| `{}` | {} | {} | {} |",
param.name, param.parameter_in, required_str, desc
)?;
}
}
writeln!(writer, "\n#### Responses")?;
writeln!(writer, "| Code | Type | Description |")?;
writeln!(writer, "|------|------|-------------|")?;
for (code, response) in &endpoint.responses {
let desc = response.description.as_deref().unwrap_or("-");
let content_type = extract_content_type(response).unwrap_or_default();
writeln!(writer, "| {} | {} | {} |", code, content_type, desc)?;
}
if config.include_schemas && config.detail_level == DetailLevel::Full {
writeln!(writer, "\n#### Request Schema")?;
let body_param = endpoint
.parameters
.iter()
.find(|p| p.parameter_in == "body" && p.schema.is_some());
if let Some(param) = body_param {
if let Some(schema) = ¶m.schema {
write_schema_table(writer, schema, doc, "request")?;
} else {
writeln!(writer, "*No request schema available*")?;
}
} else {
writeln!(writer, "*No request schema available*")?;
}
writeln!(writer, "\n#### Response Schema")?;
if let Some((_, response)) = endpoint
.responses
.iter()
.find(|(code, _)| code.starts_with('2'))
{
if let Some(schema) = response_schema(response) {
write_schema_table(writer, schema, doc, "response")?;
} else {
writeln!(writer, "*No response schema available*")?;
}
} else {
writeln!(writer, "*No success response schema available*")?;
}
}
if config.include_examples && config.detail_level == DetailLevel::Full {
write_examples(writer, endpoint, doc)?;
}
}
writeln!(writer)?; Ok(())
}
pub(super) fn get_short_title(endpoint: &Endpoint) -> String {
if let Some(operation_id) = &endpoint.operation_id {
return operation_id.clone();
} else if let Some(summary) = &endpoint.summary {
if let Some(first_word) = summary.split_whitespace().next() {
if first_word.chars().any(|c| c.is_uppercase()) {
return first_word.to_string();
}
}
return summary.clone();
}
format!("{} {}", endpoint.method, endpoint.path)
}