dockerfile_parser/instructions/
cmd.rs1use std::convert::TryFrom;
4
5use crate::Span;
6use crate::dockerfile_parser::Instruction;
7use crate::error::*;
8use crate::util::*;
9use crate::parser::*;
10
11#[derive(Debug, PartialEq, Eq, Clone)]
18pub struct CmdInstruction {
19 pub span: Span,
20 pub expr: ShellOrExecExpr,
21}
22
23impl CmdInstruction {
24 pub(crate) fn from_record(record: Pair) -> Result<CmdInstruction> {
25 let span = Span::from_pair(&record);
26 let field = record.into_inner().next().unwrap();
27
28 match field.as_rule() {
29 Rule::cmd_exec => Ok(CmdInstruction {
30 span,
31 expr: ShellOrExecExpr::Exec(parse_string_array(field)?),
32 }),
33 Rule::cmd_shell => Ok(CmdInstruction {
34 span,
35 expr: ShellOrExecExpr::Shell(parse_any_breakable(field)?),
36 }),
37 _ => Err(unexpected_token(field)),
38 }
39 }
40
41 pub fn into_shell(self) -> Option<BreakableString> {
44 self.expr.into_shell()
45 }
46
47 pub fn as_shell(&self) -> Option<&BreakableString> {
50 self.expr.as_shell()
51 }
52
53 pub fn into_exec(self) -> Option<StringArray> {
56 self.expr.into_exec()
57 }
58
59 pub fn as_exec(&self) -> Option<&StringArray> {
62 self.expr.as_exec()
63 }
64}
65
66impl<'a> TryFrom<&'a Instruction> for &'a CmdInstruction {
67 type Error = Error;
68
69 fn try_from(instruction: &'a Instruction) -> std::result::Result<Self, Self::Error> {
70 if let Instruction::Cmd(c) = instruction {
71 Ok(c)
72 } else {
73 Err(Error::ConversionError {
74 from: format!("{:?}", instruction),
75 to: "CmdInstruction".into()
76 })
77 }
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use indoc::indoc;
84 use pretty_assertions::assert_eq;
85
86 use super::*;
87 use crate::Span;
88 use crate::test_util::*;
89
90 #[test]
91 fn cmd_basic() -> Result<()> {
92 assert_eq!(
93 parse_single(r#"CMD echo "hello world""#, Rule::cmd)?
94 .as_cmd().unwrap()
95 .as_shell().unwrap(),
96 &BreakableString::new((4, 22))
97 .add_string((4, 22), "echo \"hello world\"")
98 );
99
100 assert_eq!(
101 parse_single(r#"CMD echo "hello world""#, Rule::cmd)?
102 .as_cmd().unwrap()
103 .as_shell().unwrap()
104 .to_string(),
105 "echo \"hello world\""
106 );
107
108 assert_eq!(
109 parse_single(r#"cmd ["echo", "hello world"]"#, Rule::cmd)?,
110 CmdInstruction {
111 span: Span::new(0, 27),
112 expr: ShellOrExecExpr::Exec(StringArray {
113 span: Span::new(4, 27),
114 elements: vec![SpannedString {
115 span: Span::new(5, 11),
116 content: "echo".to_string(),
117 }, SpannedString {
118 span: Span::new(13, 26),
119 content: "hello world".to_string(),
120 }]
121 }),
122 }.into()
123 );
124
125 Ok(())
126 }
127
128 #[test]
129 fn cmd_multiline_exec() -> Result<()> {
130 assert_eq!(
131 parse_single(r#"cmd\
132 [\
133 "echo", \
134 "hello world"\
135 ]"#, Rule::cmd)?,
136 CmdInstruction {
137 span: Span::new(0, 66),
138 expr: ShellOrExecExpr::Exec(StringArray {
139 span: Span::new(13, 66),
140 elements: vec![SpannedString {
141 span: Span::new(24, 30),
142 content: "echo".to_string(),
143 }, SpannedString {
144 span: Span::new(42, 55),
145 content: "hello world".to_string(),
146 }]
147 }),
148 }.into()
149 );
150
151 Ok(())
152 }
153
154 #[test]
155 fn cmd_multiline_shell() -> Result<()> {
156 assert_eq!(
157 parse_single(indoc!(r#"
158 cmd echo \
159 "hello world"
160 "#), Rule::cmd)?
161 .as_cmd().unwrap()
162 .as_shell().unwrap(),
163 &BreakableString::new((4, 26))
164 .add_string((4, 9), "echo ")
165 .add_string((11, 26), " \"hello world\"")
166 );
167
168 assert_eq!(
169 parse_single(indoc!(r#"
170 cmd echo \
171 "hello world"
172 "#), Rule::cmd)?
173 .as_cmd().unwrap()
174 .as_shell().unwrap()
175 .to_string(),
176 "echo \"hello world\""
177 );
178
179 Ok(())
180 }
181
182 #[test]
183 fn cmd_multiline_shell_large() -> Result<()> {
184 let ins = parse_single(
187 indoc!(r#"
188 cmd set -x && \
189 # lorem ipsum
190 echo "hello world" && \
191 # dolor sit amet,
192 # consectetur \
193 # adipiscing elit, \
194 # sed do eiusmod
195 # tempor incididunt ut labore
196 echo foo && \
197 echo 'bar' \
198 && echo baz \
199 # et dolore magna aliqua."#),
200 Rule::cmd
201 )?.into_cmd().unwrap().into_shell().unwrap();
202
203 assert_eq!(
204 ins,
205 BreakableString::new((4, 266))
206 .add_string((4, 14), "set -x && ")
207 .add_comment((20, 33), "# lorem ipsum")
208 .add_string((34, 60), " echo \"hello world\" && ")
209 .add_comment((66, 83), "# dolor sit amet,")
210 .add_comment((88, 103), "# consectetur \\")
211 .add_comment((108, 128), "# adipiscing elit, \\")
212 .add_comment((133, 149), "# sed do eiusmod")
213 .add_comment((154, 183), "# tempor incididunt ut labore")
214 .add_string((184, 200), " echo foo && ")
215 .add_string((202, 217), " echo 'bar' ")
216 .add_string((219, 235), " && echo baz ")
217 .add_comment((241, 266), "# et dolore magna aliqua.")
218 );
219
220 assert_eq!(
221 ins.to_string(),
222 r#"set -x && echo "hello world" && echo foo && echo 'bar' && echo baz "#
223 );
224
225 Ok(())
226 }
227}