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 members: &'static str,
}
#[derive(Default, Clone, Debug)]
pub struct InherentImplDocs {
pub methods: &'static str,
pub signals: &'static str,
pub constants: &'static str,
}
#[derive(Default)]
struct DocPieces {
definition: StructDocs,
methods: Vec<&'static str>,
signals: Vec<&'static str>,
constants: Vec<&'static str>,
}
#[doc(hidden)]
pub fn gather_xml_docs() -> impl Iterator<Item = String> {
let mut map = HashMap::<ClassId, DocPieces>::new();
crate::private::iterate_docs_plugins(|x| {
let class_name = x.class_name;
match &x.item {
DocsItem::Struct(s) => {
map.entry(class_name).or_default().definition = *s;
}
DocsItem::InherentImpl(trait_docs) => {
let InherentImplDocs {
methods,
constants,
signals,
} = trait_docs;
map.entry(class_name).or_default().methods.push(methods);
map.entry(class_name)
.and_modify(|pieces| pieces.constants.push(constants));
map.entry(class_name)
.and_modify(|pieces| pieces.signals.push(signals));
}
DocsItem::ITraitImpl(methods) => {
map.entry(class_name).or_default().methods.push(methods);
}
}
});
map.into_iter().map(|(class, pieces)| {
let StructDocs {
base,
description,
experimental,
deprecated,
members,
} = pieces.definition;
let method_docs = String::from_iter(pieces.methods);
let signal_docs = String::from_iter(pieces.signals);
let constant_docs = String::from_iter(pieces.constants);
let methods_block = if method_docs.is_empty() {
String::new()
} else {
format!("<methods>{method_docs}</methods>")
};
let signals_block = if signal_docs.is_empty() {
String::new()
} else {
format!("<signals>{signal_docs}</signals>")
};
let constants_block = if constant_docs.is_empty() {
String::new()
} else {
format!("<constants>{constant_docs}</constants>")
};
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>{members}</members>
</class>"#)
},
)
}
pub unsafe fn register() {
for xml in gather_xml_docs() {
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,
);
}
}