use indexmap::IndexSet;
use crate::{TypeDescription, TypeEnumKind};
fn get_list_of_types(desc: &TypeDescription) -> IndexSet<&TypeDescription> {
let mut types = IndexSet::new();
let mut remaining = vec![desc];
while let Some(cur) = remaining.pop() {
types.insert(cur);
match cur.kind() {
crate::TypeKind::Bool
| crate::TypeKind::Integer { .. }
| crate::TypeKind::Float { .. }
| crate::TypeKind::Enum(_, _)
| crate::TypeKind::String => (),
crate::TypeKind::Wrapped(wrapped) => remaining.push(wrapped),
crate::TypeKind::Array(arr) => remaining.push(arr),
crate::TypeKind::HashMap { key, value } => {
remaining.push(value);
remaining.push(key);
}
crate::TypeKind::Struct(strt) => {
remaining.extend(strt.iter().rev().map(|field| &field.kind));
}
}
}
types
}
pub fn render_to_markdown(desc: &TypeDescription) -> Result<String, std::fmt::Error> {
use std::fmt::Write;
let list_of_types = get_list_of_types(desc);
let mut markdown = String::new();
for ty in list_of_types {
writeln!(markdown, "# {}", ty.name())?;
writeln!(markdown)?;
if let Some(doc) = ty.doc() {
writeln!(markdown, "{}", doc)?;
}
match ty.kind() {
crate::TypeKind::Bool
| crate::TypeKind::Integer { .. }
| crate::TypeKind::Float { .. }
| crate::TypeKind::String => (),
crate::TypeKind::Wrapped(wrapped_ty) => {
writeln!(markdown)?;
writeln!(markdown, "_Represented by {}_", wrapped_ty.name())?;
}
crate::TypeKind::Array(arr_ty) => {
writeln!(markdown)?;
writeln!(markdown, "_Array Elements of {}_", arr_ty.name())?;
}
crate::TypeKind::HashMap {
key: key_ty,
value: value_ty,
} => {
writeln!(markdown)?;
writeln!(
markdown,
"_Key: {}, Values: {}_",
key_ty.name(),
value_ty.name()
)?;
}
crate::TypeKind::Struct(strct) => {
writeln!(markdown)?;
writeln!(markdown, "**Fields:**")?;
writeln!(markdown)?;
for field in strct {
writeln!(
markdown,
"- `{}` ({}): {}",
field.name(),
field.kind().name(),
field.doc().as_ref().unwrap_or(&"_No doc_")
)?;
}
}
crate::TypeKind::Enum(tag_kind, variants) => {
write!(markdown, "**Variants:** ")?;
if let TypeEnumKind::Tagged(tag) = tag_kind {
writeln!(markdown, "(Tagged with field `{tag}`)")?;
} else {
writeln!(markdown, "Untagged")?;
};
writeln!(markdown)?;
for variant in variants {
match variant.repr() {
crate::EnumVariantRepresentation::String(_) => {
writeln!(
markdown,
"- `{}`: {}",
variant.name(),
variant.doc().unwrap_or("_No doc_")
)?;
}
crate::EnumVariantRepresentation::Wrapped(wrapped_ty) => {
writeln!(
markdown,
"- `{}` ({}): {}",
variant.name(),
wrapped_ty.name(),
variant.doc().unwrap_or("_No doc_")
)?;
}
}
}
}
}
writeln!(markdown)?;
}
Ok(markdown)
}
#[cfg(test)]
mod tests {
use crate::AsTypeDescription;
use super::render_to_markdown;
#[test]
fn render_simple() {
let ty_desc = std::collections::HashMap::<String, Vec<String>>::as_type_description();
let markdown = render_to_markdown(&ty_desc).unwrap();
println!("{markdown}");
}
}