wdl_ast/v1/task/requirements/item/
container.rs

1//! The `container` item within the `requirements` block.
2
3use wdl_grammar::SyntaxKind;
4use wdl_grammar::SyntaxNode;
5
6use crate::AstNode;
7use crate::AstToken;
8use crate::TreeNode;
9use crate::v1::RequirementsItem;
10use crate::v1::TASK_REQUIREMENT_CONTAINER;
11use crate::v1::common::container::value;
12use crate::v1::common::container::value::Value;
13
14/// The `container` item within a `requirements` block.
15#[derive(Debug)]
16pub struct Container<N: TreeNode = SyntaxNode>(RequirementsItem<N>);
17
18impl<N: TreeNode> Container<N> {
19    /// Gets the [`Value`] from a [`Container`] (if it can be parsed).
20    pub fn value(&self) -> value::Result<Value<N>> {
21        Value::try_from(self.0.expr())
22    }
23}
24
25impl<N: TreeNode> AstNode<N> for Container<N> {
26    fn can_cast(kind: SyntaxKind) -> bool {
27        RequirementsItem::<N>::can_cast(kind)
28    }
29
30    fn cast(inner: N) -> Option<Self> {
31        RequirementsItem::cast(inner).and_then(|item| Container::try_from(item).ok())
32    }
33
34    fn inner(&self) -> &N {
35        self.0.inner()
36    }
37}
38
39impl<N: TreeNode> TryFrom<RequirementsItem<N>> for Container<N> {
40    type Error = ();
41
42    fn try_from(value: RequirementsItem<N>) -> Result<Self, Self::Error> {
43        if value.name().text() == TASK_REQUIREMENT_CONTAINER {
44            return Ok(Self(value));
45        }
46
47        Err(())
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use crate::Document;
54
55    #[test]
56    fn simple_example() {
57        let (document, diagnostics) = Document::parse(
58            r#"version 1.2
59
60task hello {
61    requirements {
62        container: "ubuntu"
63    }
64}"#,
65        );
66
67        assert!(diagnostics.is_empty());
68
69        let section = document
70            .ast()
71            .as_v1()
72            .expect("v1 ast")
73            .tasks()
74            .next()
75            .expect("the 'hello' task to exist")
76            .requirements()
77            .expect("the 'requirements' block to exist");
78
79        let container = section.items().filter_map(|p| p.into_container());
80
81        assert!(container.count() == 1);
82    }
83
84    #[test]
85    fn missing_container_item() {
86        let (document, diagnostics) = Document::parse(
87            r#"version 1.2
88
89task hello {
90    requirements {
91        foo: "ubuntu"
92    }
93}"#,
94        );
95
96        assert!(diagnostics.is_empty());
97
98        let section = document
99            .ast()
100            .as_v1()
101            .expect("v1 ast")
102            .tasks()
103            .next()
104            .expect("the 'hello' task to exist")
105            .requirements()
106            .expect("the 'requirements' block to exist");
107
108        let container = section.items().filter_map(|p| p.into_container());
109
110        assert_eq!(container.count(), 0);
111    }
112
113    #[test]
114    fn docker_alias() {
115        // NOTE: the `docker` key is only an alias of the `container` key within
116        // `runtime` blocks—not `requirements` blocks.
117        let (document, diagnostics) = Document::parse(
118            r#"version 1.2
119
120task hello {
121    requirements {
122        docker: "ubuntu"
123    }
124}"#,
125        );
126
127        assert!(diagnostics.is_empty());
128
129        let section = document
130            .ast()
131            .as_v1()
132            .expect("v1 ast")
133            .tasks()
134            .next()
135            .expect("the 'hello' task to exist")
136            .requirements()
137            .expect("the 'requirements' block to exist");
138
139        let container = section.items().filter_map(|p| p.into_container());
140
141        assert_eq!(container.count(), 0);
142    }
143}