cargo_prosa/package/
container.rs

1use std::{
2    fmt, fs, io,
3    path::{Path, PathBuf},
4};
5
6use clap::ArgMatches;
7use tera::Tera;
8
9use crate::cargo::CargoMetadata;
10
11/// Struct to handle Container file creation
12pub struct ContainerFile {
13    is_docker: bool,
14    ctx: tera::Context,
15    path: Option<String>,
16}
17
18impl ContainerFile {
19    /// Create a container file builder from `cargo-prosa` command arguments
20    pub fn new(args: &ArgMatches) -> io::Result<ContainerFile> {
21        let package_metadata = CargoMetadata::load_package_metadata()?;
22        let is_docker = args.get_flag("docker");
23        let mut ctx = tera::Context::new();
24        package_metadata.j2_context(&mut ctx);
25        ctx.insert("docker", &is_docker);
26        ctx.insert(
27            "image",
28            args.get_one::<String>("image")
29                .expect("required container base image"),
30        );
31        ctx.insert(
32            "package_manager",
33            args.get_one::<String>("package_manager")
34                .expect("required package manager"),
35        );
36        let builder_img = args.get_one::<String>("builder");
37        if let Some(img) = builder_img {
38            ctx.insert("builder_image", img);
39        }
40
41        Ok(ContainerFile {
42            is_docker,
43            ctx,
44            path: args.get_one::<String>("PATH").cloned(),
45        })
46    }
47
48    /// Method to get the path of the Dockerfile/Containerfile
49    pub fn get_path(&self) -> PathBuf {
50        if let Some(p) = &self.path {
51            let path = Path::new(p);
52            if path.is_dir() {
53                if self.is_docker {
54                    path.join("Dockerfile")
55                } else {
56                    path.join("Containerfile")
57                }
58            } else {
59                path.to_path_buf()
60            }
61        } else if self.is_docker {
62            Path::new("Dockerfile").to_path_buf()
63        } else {
64            Path::new("Containerfile").to_path_buf()
65        }
66    }
67
68    /// Method to create a container file
69    pub fn create_container_file(&self) -> tera::Result<()> {
70        let template_name = if self.is_docker {
71            "Dockerfile"
72        } else {
73            "Containerfile"
74        };
75
76        let mut tera_build = Tera::default();
77        tera_build.add_raw_template(
78            template_name,
79            include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/assets/container.j2")),
80        )?;
81
82        let build_file = fs::File::create(self.get_path()).map_err(tera::Error::io_error)?;
83        tera_build.render_to(template_name, &self.ctx, build_file)
84    }
85}
86
87impl fmt::Display for ContainerFile {
88    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89        let img_name = format!(
90            "{}:{}",
91            self.ctx.get("name").unwrap().as_str().unwrap(),
92            self.ctx.get("version").unwrap().as_str().unwrap()
93        );
94        writeln!(f, "To build your container, use the command:")?;
95        if self.is_docker {
96            write!(f, "  `docker build")?;
97            if self.path.is_some() {
98                write!(f, " -f {}", self.get_path().display())?;
99            }
100            writeln!(f, " -t {img_name} .`")?;
101
102            if self.ctx.contains_key("builder_image") {
103                writeln!(
104                    f,
105                    "If you have an external git dependency, specify your ssh agent with:"
106                )?;
107
108                write!(f, "  `docker build")?;
109                if self.path.is_some() {
110                    write!(f, " -f {}", self.get_path().display())?;
111                }
112                writeln!(f, " --ssh default=$SSH_AUTH_SOCK -t {img_name} .`")
113            } else {
114                Ok(())
115            }
116        } else if self.path.is_some() {
117            writeln!(
118                f,
119                "  `podman build -f {} -t {} .`",
120                self.get_path().display(),
121                img_name
122            )
123        } else {
124            writeln!(f, "  `podman build -t {img_name} .`")
125        }
126    }
127}