1use std::convert::TryFrom;
4use std::io::{Read, BufReader};
5use std::str::FromStr;
6
7use pest::Parser;
8use snafu::ResultExt;
9
10pub use crate::image::*;
11pub use crate::error::*;
12pub use crate::parser::*;
13pub use crate::instructions::*;
14pub use crate::splicer::*;
15pub use crate::stage::*;
16
17#[derive(Debug, PartialEq, Eq, Clone)]
36pub enum Instruction {
37 From(FromInstruction),
38 Arg(ArgInstruction),
39 Label(LabelInstruction),
40 Run(RunInstruction),
41 Entrypoint(EntrypointInstruction),
42 Cmd(CmdInstruction),
43 Copy(CopyInstruction),
44 Env(EnvInstruction),
45 Misc(MiscInstruction)
46}
47
48impl Instruction {
49 pub fn into_from(self) -> Option<FromInstruction> {
52 match self {
53 Instruction::From(f) => Some(f),
54 _ => None,
55 }
56 }
57
58 pub fn as_from(&self) -> Option<&FromInstruction> {
61 match self {
62 Instruction::From(f) => Some(f),
63 _ => None,
64 }
65 }
66
67 pub fn into_arg(self) -> Option<ArgInstruction> {
70 match self {
71 Instruction::Arg(a) => Some(a),
72 _ => None,
73 }
74 }
75
76 pub fn as_arg(&self) -> Option<&ArgInstruction> {
79 match self {
80 Instruction::Arg(a) => Some(a),
81 _ => None,
82 }
83 }
84
85 pub fn into_label(self) -> Option<LabelInstruction> {
88 match self {
89 Instruction::Label(l) => Some(l),
90 _ => None,
91 }
92 }
93
94 pub fn as_label(&self) -> Option<&LabelInstruction> {
97 match self {
98 Instruction::Label(l) => Some(l),
99 _ => None,
100 }
101 }
102
103 pub fn into_run(self) -> Option<RunInstruction> {
106 match self {
107 Instruction::Run(r) => Some(r),
108 _ => None,
109 }
110 }
111
112 pub fn as_run(&self) -> Option<&RunInstruction> {
115 match self {
116 Instruction::Run(r) => Some(r),
117 _ => None,
118 }
119 }
120
121 pub fn into_entrypoint(self) -> Option<EntrypointInstruction> {
124 match self {
125 Instruction::Entrypoint(e) => Some(e),
126 _ => None,
127 }
128 }
129
130 pub fn as_entrypoint(&self) -> Option<&EntrypointInstruction> {
133 match self {
134 Instruction::Entrypoint(e) => Some(e),
135 _ => None,
136 }
137 }
138
139 pub fn into_cmd(self) -> Option<CmdInstruction> {
142 match self {
143 Instruction::Cmd(c) => Some(c),
144 _ => None,
145 }
146 }
147
148 pub fn as_cmd(&self) -> Option<&CmdInstruction> {
151 match self {
152 Instruction::Cmd(c) => Some(c),
153 _ => None,
154 }
155 }
156
157 pub fn into_copy(self) -> Option<CopyInstruction> {
160 match self {
161 Instruction::Copy(c) => Some(c),
162 _ => None,
163 }
164 }
165
166 pub fn as_copy(&self) -> Option<&CopyInstruction> {
169 match self {
170 Instruction::Copy(c) => Some(c),
171 _ => None,
172 }
173 }
174
175 pub fn into_env(self) -> Option<EnvInstruction> {
178 match self {
179 Instruction::Env(e) => Some(e),
180 _ => None,
181 }
182 }
183
184 pub fn as_env(&self) -> Option<&EnvInstruction> {
187 match self {
188 Instruction::Env(e) => Some(e),
189 _ => None,
190 }
191 }
192
193 pub fn into_misc(self) -> Option<MiscInstruction> {
196 match self {
197 Instruction::Misc(m) => Some(m),
198 _ => None,
199 }
200 }
201
202 pub fn as_misc(&self) -> Option<&MiscInstruction> {
205 match self {
206 Instruction::Misc(m) => Some(m),
207 _ => None,
208 }
209 }
210
211 pub fn span(&self) -> Span {
213 match self {
214 Instruction::From(instruction) => instruction.span,
215 Instruction::Arg(instruction) => instruction.span,
216 Instruction::Label(instruction) => instruction.span,
217 Instruction::Run(instruction) => instruction.span,
218 Instruction::Entrypoint(instruction) => instruction.span,
219 Instruction::Cmd(instruction) => instruction.span,
220 Instruction::Copy(instruction) => instruction.span,
221 Instruction::Env(instruction) => instruction.span,
222 Instruction::Misc(instruction) => instruction.span,
223 }
224 }
225}
226
227macro_rules! impl_from_instruction {
230 ($struct:ident, $enum:expr) => {
231 impl From<$struct> for Instruction {
232 fn from(ins: $struct) -> Self {
233 $enum(ins)
234 }
235 }
236 };
237}
238
239impl_from_instruction!(FromInstruction, Instruction::From);
240impl_from_instruction!(ArgInstruction, Instruction::Arg);
241impl_from_instruction!(LabelInstruction, Instruction::Label);
242impl_from_instruction!(RunInstruction, Instruction::Run);
243impl_from_instruction!(EntrypointInstruction, Instruction::Entrypoint);
244impl_from_instruction!(CmdInstruction, Instruction::Cmd);
245impl_from_instruction!(CopyInstruction, Instruction::Copy);
246impl_from_instruction!(EnvInstruction, Instruction::Env);
247impl_from_instruction!(MiscInstruction, Instruction::Misc);
248
249impl TryFrom<Pair<'_>> for Instruction {
250 type Error = Error;
251
252 fn try_from(record: Pair) -> std::result::Result<Self, Self::Error> {
253 let instruction: Instruction = match record.as_rule() {
254 Rule::from => FromInstruction::from_record(record, 0)?.into(),
255 Rule::arg => ArgInstruction::from_record(record)?.into(),
256 Rule::label => LabelInstruction::from_record(record)?.into(),
257
258 Rule::run => RunInstruction::from_record(record)?.into(),
259
260 Rule::entrypoint => EntrypointInstruction::from_record(record)?.into(),
261
262 Rule::cmd => CmdInstruction::from_record(record)?.into(),
263
264 Rule::copy => Instruction::Copy(CopyInstruction::from_record(record)?),
265
266 Rule::env => EnvInstruction::from_record(record)?.into(),
267
268 Rule::misc => MiscInstruction::from_record(record)?.into(),
269
270 _ => return Err(unexpected_token(record))
273 };
274
275 Ok(instruction)
276 }
277}
278
279#[derive(Debug, Clone, PartialEq)]
305pub struct Dockerfile {
306 pub content: String,
308
309 pub global_args: Vec<ArgInstruction>,
311
312 pub instructions: Vec<Instruction>
314}
315
316fn parse_dockerfile(input: &str) -> Result<Dockerfile> {
317 let dockerfile = DockerfileParser::parse(Rule::dockerfile, input)
318 .context(ParseError)?
319 .next()
320 .ok_or(Error::UnknownParseError)?;
321
322 let mut instructions = Vec::new();
323 let mut global_args = Vec::new();
324 let mut from_found = false;
325 let mut from_index = 0;
326
327 for record in dockerfile.into_inner() {
328 if let Rule::EOI = record.as_rule() {
329 continue;
330 }
331
332 if let Rule::comment = record.as_rule() {
334 continue;
335 }
336
337 let mut instruction = Instruction::try_from(record)?;
338 match &mut instruction {
339 Instruction::From(ref mut from) => {
340 from.index = from_index;
342 from_index += 1;
343 from_found = true;
344 },
345 Instruction::Arg(ref arg) => {
346 if !from_found {
349 global_args.push(arg.clone());
350 }
351 },
352 _ => ()
353 };
354
355 instructions.push(instruction);
356 }
357
358 Ok(Dockerfile {
359 content: input.into(),
360 global_args, instructions
361 })
362}
363
364impl Dockerfile {
365 pub fn parse(input: &str) -> Result<Dockerfile> {
367 parse_dockerfile(input)
368 }
369
370 pub fn from_reader<R>(reader: R) -> Result<Dockerfile>
372 where
373 R: Read
374 {
375 let mut buf = String::new();
376 let mut buf_reader = BufReader::new(reader);
377 buf_reader.read_to_string(&mut buf).context(ReadError)?;
378
379 Dockerfile::parse(&buf)
380 }
381
382 pub fn stages(&self) -> Stages {
384 Stages::new(self)
385 }
386
387 pub fn iter_stages(&self) -> std::vec::IntoIter<Stage<'_>> {
388 self.stages().into_iter()
389 }
390
391 pub fn splicer(&self) -> Splicer {
396 Splicer::from(self)
397 }
398
399 pub fn get_global_arg(&self, name: &str) -> Option<&ArgInstruction> {
402 for ins in &self.instructions {
403 match ins {
404 Instruction::Arg(a) => {
405 if a.name.content == name {
406 return Some(a);
407 } else {
408 continue
409 }
410 },
411 _ => return None
412 }
413 }
414
415 None
416 }
417}
418
419impl FromStr for Dockerfile {
420 type Err = Error;
421
422 fn from_str(s: &str) -> Result<Self, Self::Err> {
423 Dockerfile::parse(s)
424 }
425}