1use std::path::PathBuf;
12use std::rc::Rc;
13
14use maud::Markup;
15use maud::PreEscaped;
16use maud::Render;
17use maud::html;
18use wdl_ast::AstToken;
19use wdl_ast::SupportedVersion;
20use wdl_ast::SyntaxTokenExt;
21use wdl_ast::VersionStatement;
22
23use crate::HTMLPage;
24use crate::Markdown;
25use crate::VersionBadge;
26use crate::docs_tree::Header;
27use crate::docs_tree::PageSections;
28use crate::docs_tree::PageType;
29use crate::runnable::Runnable;
30
31pub fn parse_preamble_comments(version: &VersionStatement) -> String {
33 let comments = version
34 .keyword()
35 .inner()
36 .preceding_trivia()
37 .map(|t| match t.kind() {
38 wdl_ast::SyntaxKind::Comment => match t.to_string().strip_prefix("## ") {
39 Some(comment) => comment.to_string(),
40 None => "".to_string(),
41 },
42 wdl_ast::SyntaxKind::Whitespace => "".to_string(),
43 _ => {
44 panic!("Unexpected token kind: {:?}", t.kind())
45 }
46 })
47 .collect::<Vec<_>>();
48 comments.join("\n")
49}
50
51#[derive(Debug)]
53pub(crate) struct Document {
54 name: String,
56 version: VersionBadge,
58 version_statement: VersionStatement,
62 local_pages: Vec<(PathBuf, Rc<HTMLPage>)>,
64}
65
66impl Document {
67 pub(crate) fn new(
69 name: String,
70 version: SupportedVersion,
71 version_statement: VersionStatement,
72 local_pages: Vec<(PathBuf, Rc<HTMLPage>)>,
73 ) -> Self {
74 Self {
75 name,
76 version: VersionBadge::new(version),
77 version_statement,
78 local_pages,
79 }
80 }
81
82 pub fn name(&self) -> &str {
84 &self.name
85 }
86
87 pub fn render_version(&self) -> Markup {
89 self.version.render()
90 }
91
92 pub fn render_preamble(&self) -> Option<Markup> {
94 let preamble = parse_preamble_comments(&self.version_statement);
95 if preamble.is_empty() {
96 return None;
97 }
98 Some(html! {
99 div class="markdown-body" {
100 (Markdown(&preamble).render())
101 }
102 })
103 }
104
105 pub fn render(&self) -> (Markup, PageSections) {
107 let rows = self.local_pages.iter().map(|page| {
108 html! {
109 div class="main__grid-row" x-data="{ description_expanded: false }" {
110 @match page.1.page_type() {
111 PageType::Struct(_) => {
112 div class="main__grid-cell" {
113 a class="text-brand-pink-400 hover:text-pink-200" href=(page.0.to_string_lossy()) {
114 (page.1.name())
115 }
116 }
117 div class="main__grid-cell" { code { "struct" } }
118 div class="main__grid-cell" { "N/A" }
119 }
120 PageType::Task(t) => {
121 div class="main__grid-cell" {
122 a class="text-brand-violet-400 hover:text-violet-200" href=(page.0.to_string_lossy()) {
123 (page.1.name())
124 }
125 }
126 div class="main__grid-cell" { code { "task" } }
127 div class="main__grid-cell" {
128 (t.render_description(true))
129 }
130 }
131 PageType::Workflow(w) => {
132 div class="main__grid-cell" {
133 a class="text-brand-emerald-400 hover:text-brand-emerald-200" href=(page.0.to_string_lossy()) {
134 (page.1.name())
135 }
136 }
137 div class="main__grid-cell" { code { "workflow" } }
138 div class="main__grid-cell" {
139 (w.render_description(true))
140 }
141 }
142 PageType::Index(_) => {
144 div class="main__grid-cell" { "ERROR" }
146 div class="main__grid-cell" { "ERROR" }
147 div class="main__grid-cell" { "ERROR" }
148 }
149 }
150 div x-show="description_expanded" class="main__grid-full-width-cell" {
151 @match page.1.page_type() {
152 PageType::Struct(_) => "ERROR"
153 PageType::Task(t) => {
154 (t.render_description(false))
155 }
156 PageType::Workflow(w) => {
157 (w.render_description(false))
158 }
159 PageType::Index(_) => "ERROR"
160 }
161 }
162 }
163 }
164 }.into_string()).collect::<Vec<_>>().join(&html! { div class="main__grid-row-separator" {} }.into_string());
165
166 let markup = html! {
167 div class="main__container" {
168 h1 id="title" class="main__title" { (self.name()) }
169 div class="main__badge-container" {
170 (self.render_version())
171 }
172 @if let Some(preamble) = self.render_preamble() {
173 div id="preamble" class="main__section" {
174 (preamble)
175 }
176 }
177 div class="main__section" {
178 h2 id="toc" class="main__section-header" { "Table of Contents" }
179 div class="main__grid-container" {
180 div class="main__grid-toc-container" {
181 div class="main__grid-header-cell" { "Page" }
182 div class="main__grid-header-cell" { "Type" }
183 div class="main__grid-header-cell" { "Description" }
184 div class="main__grid-header-separator" {}
185 (PreEscaped(rows))
186 }
187 }
188 }
189 }
190 };
191
192 let mut headers = PageSections::default();
193 headers.push(Header::Header(
194 "Preamble".to_string(),
195 "preamble".to_string(),
196 ));
197 headers.push(Header::Header(
198 "Table of Contents".to_string(),
199 "toc".to_string(),
200 ));
201
202 (markup, headers)
203 }
204}