use std::path::Path;
use maud::Markup;
use maud::html;
use wdl_ast::AstNode;
use wdl_ast::AstToken;
use wdl_ast::SupportedVersion;
use wdl_ast::v1::Decl;
use wdl_ast::v1::MetadataValue;
use wdl_ast::v1::StructDefinition;
use crate::VersionBadge;
use crate::docs_tree::PageSections;
use crate::meta::DEFAULT_DESCRIPTION;
use crate::meta::DESCRIPTION_KEY;
use crate::meta::DefinitionMeta;
use crate::meta::MetaMap;
use crate::meta::MetaMapExt;
use crate::meta::MetaMapValueSource;
use crate::meta::doc_comments;
use crate::meta::parse_metadata_items;
#[derive(Debug)]
struct Member {
decl: Decl,
meta: MetaMap,
}
impl Member {
fn new(decl: Decl, meta: MetaMap) -> Self {
Self { decl, meta }
}
pub fn full_description(&self) -> String {
self.meta
.full_description()
.unwrap_or_else(|| String::from(DEFAULT_DESCRIPTION))
}
}
#[derive(Debug)]
pub struct Struct {
meta: MetaMap,
members: Vec<Member>,
definition: StructDefinition,
version: VersionBadge,
}
impl DefinitionMeta for Struct {
fn meta(&self) -> &MetaMap {
&self.meta
}
}
impl Struct {
pub fn new(
definition: StructDefinition,
version: SupportedVersion,
enable_doc_comments: bool,
) -> Self {
let mut meta = definition
.metadata()
.map(|meta| parse_metadata_items(meta.items()))
.fold(MetaMap::new(), |mut acc, mut meta| {
acc.append(&mut meta);
acc
});
if enable_doc_comments {
meta.append(&mut doc_comments(definition.keyword().inner()));
}
let parameter_meta = definition
.parameter_metadata()
.map(|meta| parse_metadata_items(meta.items()))
.fold(MetaMap::new(), |mut acc, mut meta| {
acc.append(&mut meta);
acc
});
let members = parse_member_meta(&definition, ¶meter_meta, enable_doc_comments);
Self {
meta,
members,
definition,
version: VersionBadge::new(version),
}
}
pub fn render(&self, assets: &Path) -> (Markup, PageSections) {
let name = self.definition.name();
let name = name.text();
let members = html! {
div class="main__section" {
h2 id="struct-members" class="main__section-header" { "Members" }
@for member in self.members.iter() {
@let member_name = member.decl.name();
@let member_id = format!("member.{}", member_name.text());
@let member_anchor = format!("#{member_id}");
section id=(member_id) {
div class="main__meta-item-member" {
a href=(member_anchor) {}
h3 class="main__section-subheader" { (member_name.text()) }
}
div class="main__meta-item-member-description" {
@for paragraph in member.full_description().split('\n') {
p class="main__meta-item-member-description-para" { (paragraph) }
}
}
}
}
}
};
let meta_markup = self
.meta
.render_remaining(&[DESCRIPTION_KEY], assets)
.map_or_else(|| html! {}, |markup| html! { (markup) });
let markup = html! {
div class="main__container" {
p class="text-brand-pink-400" { "Struct" }
h1 id="title" class="main__title" { code { (name) } }
div class="markdown-body mb-4" {
(self.meta.render_description(false))
}
div class="main__badge-container" {
(self.version.render())
}
div class="main__section" {
sprocket-code language="wdl" {
(self.definition)
}
}
div class="main__section" {
(meta_markup)
}
(members)
}
};
(markup, PageSections::default())
}
}
fn parse_member_meta(
definition: &StructDefinition,
parameter_meta: &MetaMap,
enable_doc_comments: bool,
) -> Vec<Member> {
definition
.members()
.map(|decl| {
let name = decl.name().text().to_owned();
let mut meta_map = MetaMap::default();
if let Some(MetaMapValueSource::MetaValue(meta)) = parameter_meta.get(&name) {
match meta {
MetadataValue::Object(o) => {
for item in o.items() {
meta_map.insert(
item.name().text().to_string(),
MetaMapValueSource::MetaValue(item.value().clone()),
);
}
}
MetadataValue::String(_s) => {
meta_map.insert(
DESCRIPTION_KEY.to_string(),
MetaMapValueSource::MetaValue(meta.clone()),
);
}
_ => {}
}
}
if enable_doc_comments {
if let Some(token) = decl.inner().first_token() {
meta_map.append(&mut doc_comments(&token));
}
}
Member::new(Decl::Unbound(decl.clone()), meta_map)
})
.collect()
}