dockerfile_parser/instructions/
run.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 RunInstruction {
19 pub span: Span,
20 pub expr: ShellOrExecExpr,
21}
22
23impl RunInstruction {
24 pub(crate) fn from_record(record: Pair) -> Result<RunInstruction> {
25 let span = Span::from_pair(&record);
26 let field = record.into_inner().next().unwrap();
27
28 match field.as_rule() {
29 Rule::run_exec => Ok(RunInstruction {
30 span,
31 expr: ShellOrExecExpr::Exec(parse_string_array(field)?),
32 }),
33 Rule::run_shell => Ok(RunInstruction {
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 RunInstruction {
67 type Error = Error;
68
69 fn try_from(instruction: &'a Instruction) -> std::result::Result<Self, Self::Error> {
70 if let Instruction::Run(r) = instruction {
71 Ok(r)
72 } else {
73 Err(Error::ConversionError {
74 from: format!("{:?}", instruction),
75 to: "RunInstruction".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 run_basic() -> Result<()> {
92 assert_eq!(
93 parse_single(r#"run echo "hello world""#, Rule::run)?
94 .as_run().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#"run ["echo", "hello world"]"#, Rule::run)?,
102 RunInstruction {
103 span: Span::new(0, 27),
104 expr: ShellOrExecExpr::Exec(StringArray {
105 span: Span::new(4, 27),
106 elements: vec![SpannedString {
107 span: Span::new(5, 11),
108 content: "echo".to_string(),
109 }, SpannedString {
110 span: Span::new(13, 26),
111 content: "hello world".to_string(),
112 }]
113 }),
114 }.into()
115 );
116
117 Ok(())
118 }
119
120 #[test]
121 fn run_multiline_shell() -> Result<()> {
122 assert_eq!(
123 parse_single(indoc!(r#"
124 run echo \
125 "hello world"
126 "#), Rule::run)?
127 .as_run().unwrap()
128 .as_shell().unwrap(),
129 &BreakableString::new((4, 26))
130 .add_string((4, 9), "echo ")
131 .add_string((11, 26), " \"hello world\"")
132 );
133
134 assert_eq!(
135 parse_single(indoc!(r#"
136 run echo \
137 "hello world"
138 "#), Rule::run)?
139 .as_run().unwrap()
140 .as_shell().unwrap()
141 .to_string(),
142 "echo \"hello world\""
143 );
144
145 assert_eq!(
148 parse_single("run echo \\ \t \t\t\n \"hello world\"", Rule::run)?
149 .as_run().unwrap()
150 .as_shell().unwrap()
151 .to_string(),
152 "echo \"hello world\""
153 );
154
155 Ok(())
156 }
157
158 #[test]
159 fn run_multiline_shell_comment() -> Result<()> {
160 assert_eq!(
161 parse_single(
162 indoc!(r#"
163 run foo && \
164 # implicitly escaped
165 bar && \
166 # explicitly escaped \
167 baz
168 "#),
169 Rule::run
170 )?
171 .into_run().unwrap()
172 .into_shell().unwrap(),
173 BreakableString::new((4, 85))
174 .add_string((4, 11), "foo && ")
175 .add_comment((17, 37), "# implicitly escaped")
176 .add_string((38, 49), " bar && ")
177 .add_comment((55, 77), "# explicitly escaped \\")
178 .add_string((78, 85), " baz")
179 );
180
181 Ok(())
182 }
183
184 #[test]
185 fn run_multiline_shell_large() -> Result<()> {
186 let ins = parse_single(
189 indoc!(r#"
190 run set -x && \
191 # lorem ipsum
192 echo "hello world" && \
193 # dolor sit amet,
194 # consectetur \
195 # adipiscing elit, \
196 # sed do eiusmod
197 # tempor incididunt ut labore
198 echo foo && \
199 echo 'bar' \
200 && echo baz \
201 # et dolore magna aliqua."#),
202 Rule::run
203 )?.into_run().unwrap().into_shell().unwrap();
204
205 assert_eq!(
206 ins,
207 BreakableString::new((4, 266))
208 .add_string((4, 14), "set -x && ")
209 .add_comment((20, 33), "# lorem ipsum")
210 .add_string((34, 60), " echo \"hello world\" && ")
211 .add_comment((66, 83), "# dolor sit amet,")
212 .add_comment((88, 103), "# consectetur \\")
213 .add_comment((108, 128), "# adipiscing elit, \\")
214 .add_comment((133, 149), "# sed do eiusmod")
215 .add_comment((154, 183), "# tempor incididunt ut labore")
216 .add_string((184, 200), " echo foo && ")
217 .add_string((202, 217), " echo 'bar' ")
218 .add_string((219, 235), " && echo baz ")
219 .add_comment((241, 266), "# et dolore magna aliqua.")
220 );
221
222 assert_eq!(
223 ins.to_string(),
224 r#"set -x && echo "hello world" && echo foo && echo 'bar' && echo baz "#
225 );
226
227 Ok(())
228 }
229
230 #[test]
231 fn run_multline_exec() -> Result<()> {
232 assert_eq!(
233 parse_single(r#"run\
234 [\
235 "echo", \
236 "hello world"\
237 ]"#, Rule::run)?,
238 RunInstruction {
239 span: Span::new(0, 66),
240 expr: ShellOrExecExpr::Exec(StringArray {
241 span: Span::new(13, 66),
242 elements: vec![SpannedString {
243 span: Span::new(24, 30),
244 content: "echo".to_string(),
245 }, SpannedString {
246 span: Span::new(42, 55),
247 content: "hello world".to_string(),
248 }],
249 }),
250 }.into()
251 );
252
253 Ok(())
254 }
255
256 #[test]
257 fn run_multiline_exec_comment() -> Result<()> {
258 assert_eq!(
259 parse_single(r#"run\
260 [\
261 "echo", \
262 "hello world"\
263 ]"#, Rule::run)?,
264 RunInstruction {
265 span: Span::new(0, 66),
266 expr: ShellOrExecExpr::Exec(StringArray {
267 span: Span::new(13, 66),
268 elements: vec![SpannedString {
269 span: Span::new(24, 30),
270 content: "echo".to_string(),
271 }, SpannedString {
272 span: Span::new(42, 55),
273 content: "hello world".to_string(),
274 }],
275 })
276 }.into()
277 );
278
279 Ok(())
280 }
281}