cargo_prosa/package/
deb.rs

1use std::{
2    fs, io,
3    path::{Path, PathBuf},
4};
5
6use tera::Tera;
7
8use crate::{cargo::CargoMetadata, package::ASSETS_SYSTEMD_J2};
9
10/// Struct to handle Container file creation
11pub struct DebPkg {
12    path: PathBuf,
13    ctx: tera::Context,
14}
15
16impl DebPkg {
17    const DEB_DATA_TARGET: &'static str = "prosa-deb";
18
19    /// Create a debian package builder from build.rs script
20    pub fn new(path: PathBuf) -> io::Result<DebPkg> {
21        let package_metadata = CargoMetadata::load_package_metadata()?;
22        let mut ctx = tera::Context::new();
23        package_metadata.j2_context(&mut ctx);
24
25        // Add package build context
26        ctx.insert(
27            "config",
28            &format!("/etc/ProSA/{}.yml", package_metadata.name),
29        );
30        ctx.insert("bin", &format!("/usr/bin/{}", package_metadata.name));
31
32        Ok(DebPkg { path, ctx })
33    }
34
35    fn get_binary_assets(name: &str) -> toml_edit::Array {
36        let mut binary_assets = toml_edit::Array::new();
37        binary_assets.push(format!("target/release/{name}"));
38        binary_assets.push("usr/bin/");
39        binary_assets.push("755");
40        binary_assets
41    }
42
43    fn get_config_assets(name: &str) -> toml_edit::Array {
44        let mut config_assets = toml_edit::Array::new();
45        config_assets.push(format!("target/{}/{}.yml", Self::DEB_DATA_TARGET, name));
46        config_assets.push("etc/ProSA/");
47        config_assets.push("644");
48        config_assets
49    }
50
51    fn get_readme_assets(name: &str) -> toml_edit::Array {
52        let mut readme_assets = toml_edit::Array::new();
53        readme_assets.push("README.md");
54        readme_assets.push(format!("usr/share/doc/{name}/README"));
55        readme_assets.push("644");
56        readme_assets
57    }
58
59    /// Function to add debian package metadata to `Cargo.toml`
60    pub fn add_deb_pkg_metadata(deb_table: &mut toml_edit::Table, name: &str) {
61        if !deb_table.contains_key("depends") {
62            deb_table.insert(
63                "depends",
64                toml_edit::Item::Value(toml_edit::Value::String(toml_edit::Formatted::new(
65                    "$auto, libssl3".to_string(),
66                ))),
67            );
68        }
69
70        if !deb_table.contains_key("maintainer-scripts") {
71            deb_table.insert(
72                "maintainer-scripts",
73                toml_edit::Item::Value(format!("target/{}/", Self::DEB_DATA_TARGET).into()),
74            );
75        }
76
77        if !deb_table.contains_key("assets") {
78            // Add every assets properties to deb table
79            let mut assets = toml_edit::Array::new();
80
81            assets.push(Self::get_binary_assets(name));
82            assets.push(Self::get_config_assets(name));
83
84            if Path::new("README.md").is_file() {
85                assets.push(Self::get_readme_assets(name));
86            }
87
88            deb_table.insert("assets", toml_edit::Item::Value(assets.into()));
89        }
90
91        if let Some(toml_edit::Item::Value(toml_edit::Value::InlineTable(systemd_units))) =
92            deb_table.get_mut("systemd-units")
93        {
94            if !systemd_units.contains_key("enable") {
95                systemd_units.insert(
96                    "enable",
97                    toml_edit::Value::Boolean(toml_edit::Formatted::new(true)),
98                );
99            }
100        } else {
101            let mut inline_table = toml_edit::InlineTable::new();
102
103            inline_table.insert(
104                "enable",
105                toml_edit::Value::Boolean(toml_edit::Formatted::new(true)),
106            );
107
108            deb_table.insert(
109                "systemd-units",
110                toml_edit::Item::Value(toml_edit::Value::InlineTable(inline_table)),
111            );
112        }
113    }
114
115    /// Method to write package data (useful for the deb package) into a folder
116    pub fn write_package_data(&self) -> io::Result<()> {
117        let name = self
118            .ctx
119            .get("name")
120            .and_then(|n| n.as_str())
121            .ok_or(io::Error::new(
122                io::ErrorKind::InvalidData,
123                "Missing package name",
124            ))?;
125        let pkg_data_path = self.path.join(Self::DEB_DATA_TARGET);
126        fs::create_dir_all(&pkg_data_path)?;
127
128        // Copy configuration file
129        fs::copy(
130            self.path.join("config.yml"),
131            pkg_data_path.join(format!("{name}.yml")),
132        )?;
133
134        // Write systemd file
135        let mut tera_build = Tera::default();
136        tera_build
137            .add_raw_template("prosa.service", ASSETS_SYSTEMD_J2)
138            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
139
140        let main_file = fs::File::create(pkg_data_path.join("service"))?;
141        tera_build
142            .render_to("prosa.service", &self.ctx, main_file)
143            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
144    }
145}