use std::path::PathBuf;
use maud::Markup;
use wdl_ast::AstToken;
use wdl_ast::SupportedVersion;
use wdl_ast::v1::MetadataValue;
use wdl_ast::v1::WorkflowDefinition;
use super::*;
use crate::docs_tree::Header;
use crate::docs_tree::PageSections;
use crate::meta::DESCRIPTION_KEY;
use crate::parameter::Parameter;
const NAME_KEY: &str = "name";
const CATEGORY_KEY: &str = "category";
#[derive(Debug)]
pub(crate) struct Workflow {
name: String,
version: VersionBadge,
meta: MetaMap,
inputs: Vec<Parameter>,
outputs: Vec<Parameter>,
wdl_path: Option<PathBuf>,
}
impl Workflow {
pub fn new(
name: String,
version: SupportedVersion,
definition: WorkflowDefinition,
wdl_path: Option<PathBuf>,
) -> Self {
let meta = match definition.metadata() {
Some(mds) => parse_meta(&mds),
_ => MetaMap::default(),
};
let parameter_meta = match definition.parameter_metadata() {
Some(pmds) => parse_parameter_meta(&pmds),
_ => MetaMap::default(),
};
let inputs = match definition.input() {
Some(is) => parse_inputs(&is, ¶meter_meta),
_ => Vec::new(),
};
let outputs = match definition.output() {
Some(os) => parse_outputs(&os, &meta, ¶meter_meta),
_ => Vec::new(),
};
Self {
name,
version: VersionBadge::new(version),
meta,
inputs,
outputs,
wdl_path,
}
}
pub fn name_override(&self) -> Option<String> {
self.meta.get(NAME_KEY).and_then(|v| match v {
MetadataValue::String(s) => Some(
s.text()
.expect("meta string should not be interpolated")
.text()
.to_string(),
),
_ => None,
})
}
pub fn category(&self) -> Option<String> {
self.meta.get(CATEGORY_KEY).and_then(|v| match v {
MetadataValue::String(s) => Some(
s.text()
.expect("meta string should not be interpolated")
.text()
.to_string(),
),
_ => None,
})
}
pub fn render_name(&self) -> Markup {
if let Some(name) = self.name_override() {
html! { (name) }
} else {
html! { (self.name) }
}
}
pub fn render_meta(&self, assets: &Path) -> Option<Markup> {
self.meta().render_remaining(
&[
DESCRIPTION_KEY,
NAME_KEY,
CATEGORY_KEY,
"allowNestedInputs",
"allow_nested_inputs",
"outputs",
],
assets,
)
}
pub fn render_allow_nested_inputs(&self) -> Markup {
if let Some(MetadataValue::Boolean(b)) = self
.meta
.get("allowNestedInputs")
.or(self.meta.get("allow_nested_inputs"))
{
if b.value() {
return html! {
div class="main__badge main__badge--success" {
span class="main__badge-text" {
"Nested Inputs Allowed"
}
}
};
}
}
html! {
div class="main__badge main__badge--disabled" {
span class="main__badge-text" {
"Nested Inputs Not Allowed"
}
}
}
}
pub fn render_category(&self) -> Option<Markup> {
self.category().map(|category| {
html! {
div class="main__badge" {
span class="main__badge-text" {
"Category"
}
div class="main__badge-inner" {
span class="main__badge-inner-text" {
(category)
}
}
}
}
})
}
pub fn render(&self, assets: &Path) -> (Markup, PageSections) {
let mut headers = PageSections::default();
let meta_markup = if let Some(meta) = self.render_meta(assets) {
html! { (meta) }
} else {
html! {}
};
let (input_markup, inner_headers) = self.render_inputs(assets);
headers.extend(inner_headers);
let markup = html! {
div class="main__container" {
span class="text-emerald-400" { "Workflow" }
h1 id="title" class="main__title" { (self.render_name()) }
div class="markdown-body mb-4" {
(self.render_description(false))
}
div class="main__badge-container" {
(self.render_version())
@if let Some(badge) = self.render_category() {
(badge)
}
(self.render_allow_nested_inputs())
}
(self.render_run_with(assets))
div class="main__section" {
(meta_markup)
}
(input_markup)
(self.render_outputs(assets))
}
};
headers.push(Header::Header("Outputs".to_string(), "outputs".to_string()));
(markup, headers)
}
}
impl Runnable for Workflow {
fn name(&self) -> &str {
&self.name
}
fn version(&self) -> &VersionBadge {
&self.version
}
fn meta(&self) -> &MetaMap {
&self.meta
}
fn inputs(&self) -> &[Parameter] {
&self.inputs
}
fn outputs(&self) -> &[Parameter] {
&self.outputs
}
fn is_workflow(&self) -> bool {
true
}
fn wdl_path(&self) -> Option<&Path> {
self.wdl_path.as_deref()
}
}
#[cfg(test)]
mod tests {
use wdl_ast::Document;
use wdl_ast::version::V1;
use super::*;
#[test]
fn test_workflow() {
let (doc, _) = Document::parse(
r#"
version 1.0
workflow test {
input {
String name
}
output {
String greeting = "Hello, ${name}!"
}
}
"#,
);
let doc_item = doc.ast().into_v1().unwrap().items().next().unwrap();
let ast_workflow = doc_item.into_workflow_definition().unwrap();
let workflow = Workflow::new(
ast_workflow.name().text().to_string(),
SupportedVersion::V1(V1::Zero),
ast_workflow,
None,
);
assert_eq!(workflow.name(), "test");
assert_eq!(workflow.inputs.len(), 1);
assert_eq!(workflow.outputs.len(), 1);
}
}