Skip to main content

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 = if let Some(name) = self.ctx.get("name").and_then(|v| v.as_str())
90            && let Some(version) = self.ctx.get("version").and_then(|v| v.as_str())
91        {
92            format!("{name}:{version}")
93        } else {
94            "prosa:1.0.0".to_string()
95        };
96
97        writeln!(f, "To build your container, use the command:")?;
98        if self.is_docker {
99            write!(f, "  `docker build")?;
100            if self.path.is_some() {
101                write!(f, " -f {}", self.get_path().display())?;
102            }
103            writeln!(f, " -t {img_name} .`")?;
104
105            if self.ctx.contains_key("builder_image") {
106                writeln!(
107                    f,
108                    "If you have an external git dependency, specify your ssh agent with:"
109                )?;
110
111                write!(f, "  `docker build")?;
112                if self.path.is_some() {
113                    write!(f, " -f {}", self.get_path().display())?;
114                }
115                writeln!(f, " --ssh default=$SSH_AUTH_SOCK -t {img_name} .`")
116            } else {
117                Ok(())
118            }
119        } else if self.path.is_some() {
120            writeln!(
121                f,
122                "  `podman build -f {} -t {img_name} .`",
123                self.get_path().display()
124            )
125        } else {
126            writeln!(f, "  `podman build -t {img_name} .`")
127        }
128    }
129}