dockerfile_parser_rs/
lib.rs

1mod ast;
2mod error;
3mod parser;
4mod symbols;
5mod utils;
6
7use std::fs::File;
8use std::io::Write;
9use std::path::PathBuf;
10
11use crate::parser::instructions::add;
12use crate::parser::instructions::arg;
13use crate::parser::instructions::cmd;
14use crate::parser::instructions::copy;
15use crate::parser::instructions::entrypoint;
16use crate::parser::instructions::env;
17use crate::parser::instructions::expose;
18use crate::parser::instructions::from;
19use crate::parser::instructions::label;
20use crate::parser::instructions::run;
21use crate::parser::instructions::user;
22use crate::parser::instructions::volume;
23use crate::parser::instructions::workdir;
24use crate::symbols::chars::HASHTAG;
25use crate::utils::read_lines;
26use crate::utils::split_instruction_and_arguments;
27
28pub use crate::ast::Instruction;
29pub use crate::error::ParseError;
30
31pub type ParseResult<T> = Result<T, ParseError>;
32
33#[derive(Debug)]
34pub struct Dockerfile {
35    pub path: PathBuf,
36    pub instructions: Vec<Instruction>,
37}
38
39impl Dockerfile {
40    pub fn new(path: PathBuf) -> Self {
41        Dockerfile {
42            path,
43            instructions: Vec::new(),
44        }
45    }
46
47    pub fn from(path: PathBuf) -> ParseResult<Self> {
48        let mut dockerfile = Dockerfile::new(path);
49        dockerfile.instructions = dockerfile.parse()?;
50        Ok(dockerfile)
51    }
52
53    pub fn parse(&self) -> ParseResult<Vec<Instruction>> {
54        let lines = read_lines(&self.path);
55        let mut instructions = Vec::new();
56
57        for line in lines {
58            // preserve empty lines
59            if line.is_empty() {
60                instructions.push(Instruction::Empty);
61            // preserve comments
62            } else if line.starts_with(HASHTAG) {
63                instructions.push(Instruction::Comment(line.to_owned()));
64            } else {
65                let (instruction, arguments) = split_instruction_and_arguments(&line)?;
66                let instruction = match instruction.as_str() {
67                    "ADD" => add::parse(arguments),
68                    "ARG" => arg::parse(arguments),
69                    "CMD" => cmd::parse(arguments),
70                    "COPY" => copy::parse(arguments),
71                    "ENTRYPOINT" => entrypoint::parse(arguments),
72                    "ENV" => env::parse(arguments),
73                    "EXPOSE" => expose::parse(arguments),
74                    "LABEL" => label::parse(arguments),
75                    "FROM" => from::parse(arguments),
76                    "RUN" => run::parse(arguments),
77                    "USER" => user::parse(arguments),
78                    "VOLUME" => volume::parse(arguments),
79                    "WORKDIR" => workdir::parse(arguments),
80                    _ => return Err(ParseError::UnknownInstruction(instruction)),
81                }
82                .map_err(|e| ParseError::SyntaxError(e.to_string()))?;
83                instructions.push(instruction);
84            }
85        }
86        Ok(instructions)
87    }
88
89    pub fn dump(&self) -> std::io::Result<()> {
90        let mut file = File::create(&self.path)?;
91        for instruction in &self.instructions {
92            writeln!(file, "{}", instruction)?;
93        }
94        Ok(())
95    }
96}