wdl_doc/callable/
task.rs

1//! Create HTML documentation for WDL tasks.
2
3use 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/// A task in a WDL document.
17#[derive(Debug)]
18pub struct Task {
19    /// The name of the task.
20    name: String,
21    /// The meta of the task.
22    meta: MetaMap,
23    /// The input parameters of the task.
24    inputs: Vec<Parameter>,
25    /// The output parameters of the task.
26    outputs: Vec<Parameter>,
27    /// The runtime section of the task.
28    runtime_section: Option<RuntimeSection>,
29}
30
31impl Task {
32    /// Create a new task.
33    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, &parameter_meta),
51            _ => Vec::new(),
52        };
53        let outputs = match output_section {
54            Some(os) => parse_outputs(&os, &meta, &parameter_meta),
55            _ => Vec::new(),
56        };
57
58        Self {
59            name,
60            meta,
61            inputs,
62            outputs,
63            runtime_section,
64        }
65    }
66
67    /// Render the meta section of the task as HTML.
68    ///
69    /// This will render all metadata key-value pairs except for `outputs` and
70    /// `description`.
71    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    /// Render the runtime section of the task as HTML.
90    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    /// Render the task as HTML.
118    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}