use std::io::Write;
use anyhow::Result;
use indexmap::IndexMap;
use crate::models::{ApiDocumentation, Endpoint, Example};
use crate::utils::decode_json_pointer_token;
enum ExampleBlock {
Json {
label: String,
value: serde_json::Value,
},
External {
label: String,
url: String,
},
}
pub(super) fn write_examples<W: Write>(
writer: &mut W,
endpoint: &Endpoint,
doc: &ApiDocumentation,
) -> Result<()> {
let mut blocks: Vec<ExampleBlock> = Vec::new();
if let Some(body) = endpoint
.parameters
.iter()
.find(|p| p.parameter_in == "body")
{
collect_examples("Request", &body.example, &body.examples, doc, &mut blocks);
}
for (code, response) in &endpoint.responses {
if let Some(content) = &response.content {
for (content_type, media) in content {
let prefix = format!("Response `{}` (`{}`)", code, content_type);
collect_examples(&prefix, &media.example, &media.examples, doc, &mut blocks);
}
}
}
writeln!(writer, "\n#### Examples")?;
if blocks.is_empty() {
writeln!(writer, "*No examples available*")?;
return Ok(());
}
for block in blocks {
match block {
ExampleBlock::Json { label, value } => {
writeln!(writer, "\n**{}**\n", label)?;
writeln!(writer, "```json")?;
let pretty =
serde_json::to_string_pretty(&value).unwrap_or_else(|_| value.to_string());
writeln!(writer, "{}", pretty)?;
writeln!(writer, "```")?;
}
ExampleBlock::External { label, url } => {
writeln!(writer, "\n**{}**\n", label)?;
writeln!(writer, "External value: <{}>", url)?;
}
}
}
Ok(())
}
fn collect_examples(
label_prefix: &str,
example: &Option<serde_json::Value>,
examples: &Option<IndexMap<String, Example>>,
doc: &ApiDocumentation,
blocks: &mut Vec<ExampleBlock>,
) {
if let Some(value) = example {
blocks.push(ExampleBlock::Json {
label: label_prefix.to_string(),
value: value.clone(),
});
}
if let Some(named) = examples {
for (name, raw) in named {
let Some(resolved) = resolve_example(raw, doc) else {
continue;
};
let label = format!("{} — {}", label_prefix, name);
if let Some(value) = &resolved.value {
blocks.push(ExampleBlock::Json {
label,
value: value.clone(),
});
} else if let Some(url) = &resolved.external_value {
blocks.push(ExampleBlock::External {
label,
url: url.clone(),
});
}
}
}
}
fn resolve_example<'a>(example: &'a Example, doc: &'a ApiDocumentation) -> Option<&'a Example> {
if let Some(reference) = &example.reference {
if let Some(name) = reference.strip_prefix("#/components/examples/") {
return doc.examples.get(&decode_json_pointer_token(name));
}
return None;
}
Some(example)
}