use std::collections::HashMap;
use crate::meta::ClassId;
use crate::obj::GodotClass;
#[derive(Debug)]
pub struct DocsPlugin {
class_name: ClassId,
item: DocsItem,
}
impl DocsPlugin {
pub fn new<T: GodotClass>(item: DocsItem) -> Self {
Self {
class_name: T::class_id(),
item,
}
}
}
type ITraitImplDocs = &'static str;
#[derive(Debug)]
pub enum DocsItem {
Struct(StructDocs),
InherentImpl(InherentImplDocs),
ITraitImpl(ITraitImplDocs),
}
#[derive(Default, Copy, Clone, Debug)]
pub struct StructDocs {
pub base: &'static str,
pub description: &'static str,
pub experimental: &'static str,
pub deprecated: &'static str,
pub properties: &'static str,
}
#[derive(Default, Clone, Debug)]
pub struct InherentImplDocs {
pub methods_xml: &'static str,
pub signals_xml: &'static str,
pub constants_xml: &'static str,
}
#[derive(Default)]
struct AggregatedDocs {
definition: StructDocs,
methods_xmls: Vec<&'static str>,
signals_xmls: Vec<&'static str>,
constants_xmls: Vec<&'static str>,
}
#[doc(hidden)]
pub fn gather_xml_docs() -> impl Iterator<Item = String> {
let mut map = HashMap::<ClassId, AggregatedDocs>::new();
crate::private::iterate_docs_plugins(|shard| {
let class_name = shard.class_name;
match &shard.item {
DocsItem::Struct(struct_docs) => {
map.entry(class_name).or_default().definition = *struct_docs;
}
DocsItem::InherentImpl(trait_docs) => {
map.entry(class_name)
.or_default()
.methods_xmls
.push(trait_docs.methods_xml);
map.entry(class_name)
.and_modify(|pieces| pieces.constants_xmls.push(trait_docs.constants_xml));
map.entry(class_name)
.and_modify(|pieces| pieces.signals_xmls.push(trait_docs.signals_xml));
}
DocsItem::ITraitImpl(methods_xml) => {
map.entry(class_name)
.or_default()
.methods_xmls
.push(methods_xml);
}
}
});
map.into_iter().map(|(class, pieces)| {
let StructDocs {
base,
description,
experimental,
deprecated,
properties,
} = pieces.definition;
let methods_block = wrap_in_xml_block("methods", pieces.methods_xmls);
let signals_block = wrap_in_xml_block("signals", pieces.signals_xmls);
let constants_block = wrap_in_xml_block("constants", pieces.constants_xmls);
let (brief, description) = match description.split_once("[br]") {
Some((brief, description)) => (brief, description.trim_start_matches("[br]")),
None => (description, ""),
};
format!(r#"<?xml version="1.0" encoding="UTF-8"?>
<class name="{class}" inherits="{base}"{deprecated}{experimental} xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>{brief}</brief_description>
<description>{description}</description>
{methods_block}
{constants_block}
{signals_block}
<members>{properties}</members>
</class>"#)
})
}
fn wrap_in_xml_block(tag: &str, mut blocks: Vec<&'static str>) -> String {
blocks.sort();
let content = String::from_iter(blocks);
if content.is_empty() {
String::new()
} else {
format!("<{tag}>{content}</{tag}>")
}
}
pub unsafe fn register() {
for xml in gather_xml_docs() {
unsafe {
crate::sys::interface_fn!(editor_help_load_xml_from_utf8_chars_and_len)(
xml.as_ptr() as *const std::ffi::c_char,
xml.len() as i64,
);
}
}
}