1use maud::Markup;
4use maud::html;
5use wdl_ast::AstNode;
6use wdl_ast::AstToken;
7use wdl_ast::v1::InputSection;
8use wdl_ast::v1::MetadataSection;
9use wdl_ast::v1::OutputSection;
10use wdl_ast::v1::ParameterMetadataSection;
11use wdl_ast::v1::RuntimeSection;
12
13use super::*;
14use crate::parameter::Parameter;
15
16#[derive(Debug)]
18pub struct Task {
19 name: String,
21 meta: MetaMap,
23 inputs: Vec<Parameter>,
25 outputs: Vec<Parameter>,
27 runtime_section: Option<RuntimeSection>,
29}
30
31impl Task {
32 pub fn new(
34 name: String,
35 meta_section: Option<MetadataSection>,
36 parameter_meta: Option<ParameterMetadataSection>,
37 input_section: Option<InputSection>,
38 output_section: Option<OutputSection>,
39 runtime_section: Option<RuntimeSection>,
40 ) -> Self {
41 let meta = match meta_section {
42 Some(mds) => parse_meta(&mds),
43 _ => MetaMap::default(),
44 };
45 let parameter_meta = match parameter_meta {
46 Some(pmds) => parse_parameter_meta(&pmds),
47 _ => MetaMap::default(),
48 };
49 let inputs = match input_section {
50 Some(is) => parse_inputs(&is, ¶meter_meta),
51 _ => Vec::new(),
52 };
53 let outputs = match output_section {
54 Some(os) => parse_outputs(&os, &meta, ¶meter_meta),
55 _ => Vec::new(),
56 };
57
58 Self {
59 name,
60 meta,
61 inputs,
62 outputs,
63 runtime_section,
64 }
65 }
66
67 pub fn render_meta(&self) -> Markup {
72 let mut kv = self
73 .meta
74 .iter()
75 .filter(|(k, _)| !matches!(k.as_str(), "outputs" | "description"))
76 .peekable();
77 html! {
78 @if kv.peek().is_some() {
79 h2 { "Meta" }
80 @for (key, value) in kv {
81 p {
82 b { (key) ":" } " " (render_value(value))
83 }
84 }
85 }
86 }
87 }
88
89 pub fn render_runtime_section(&self) -> Markup {
91 match &self.runtime_section {
92 Some(runtime_section) => {
93 html! {
94 h2 { "Default Runtime Attributes" }
95 table class="border" {
96 thead class="border" { tr {
97 th { "Attribute" }
98 th { "Value" }
99 }}
100 tbody class="border" {
101 @for entry in runtime_section.items() {
102 tr class="border" {
103 td class="border" { code { (entry.name().text()) } }
104 td class="border" { code { ({let e = entry.expr(); e.text().to_string() }) } }
105 }
106 }
107 }
108 }
109 }
110 }
111 _ => {
112 html! {}
113 }
114 }
115 }
116
117 pub fn render(&self) -> Markup {
119 html! {
120 div class="table-auto border-collapse" {
121 h1 { (self.name()) }
122 (self.description())
123 (self.render_meta())
124 (self.render_inputs())
125 (self.render_outputs())
126 (self.render_runtime_section())
127 }
128 }
129 }
130}
131
132impl Callable for Task {
133 fn name(&self) -> &str {
134 &self.name
135 }
136
137 fn meta(&self) -> &MetaMap {
138 &self.meta
139 }
140
141 fn inputs(&self) -> &[Parameter] {
142 &self.inputs
143 }
144
145 fn outputs(&self) -> &[Parameter] {
146 &self.outputs
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use wdl_ast::Document;
153
154 use super::*;
155
156 #[test]
157 fn test_task() {
158 let (doc, _) = Document::parse(
159 r#"
160 version 1.0
161
162 task my_task {
163 input {
164 String name
165 }
166 output {
167 String greeting = "Hello, ${name}!"
168 }
169 runtime {
170 docker: "ubuntu:latest"
171 }
172 meta {
173 description: "A simple task"
174 }
175 }
176 "#,
177 );
178
179 let doc_item = doc.ast().into_v1().unwrap().items().next().unwrap();
180 let ast_task = doc_item.into_task_definition().unwrap();
181
182 let task = Task::new(
183 ast_task.name().text().to_owned(),
184 ast_task.metadata(),
185 ast_task.parameter_metadata(),
186 ast_task.input(),
187 ast_task.output(),
188 ast_task.runtime(),
189 );
190
191 assert_eq!(task.name(), "my_task");
192 assert_eq!(
193 task.meta()
194 .get("description")
195 .unwrap()
196 .clone()
197 .unwrap_string()
198 .text()
199 .unwrap()
200 .text(),
201 "A simple task"
202 );
203 assert_eq!(task.inputs().len(), 1);
204 assert_eq!(task.outputs().len(), 1);
205 }
206}