use isoloom_plan::Environment;
use isoloom_plan::configure::{Context, Recipe, Resource, fill};
use isoloom_spec::syntax::ProjectPath;
use crate::GeneratedFile;
pub mod cloud_init;
pub mod dockerfile;
mod password;
pub mod powershell;
pub mod runner;
pub mod shell;
mod units;
pub use units::{linux_unit, systemd_word};
pub const DIR: &str = ".isoloom/configure";
#[derive(Debug, Clone, Copy)]
pub struct Configured<'a> {
env: &'a Environment,
machine: &'a str,
recipe: &'a Recipe,
}
impl<'a> Configured<'a> {
#[must_use]
pub fn of(env: &'a Environment, machine: &'a str) -> Option<Configured<'a>> {
let recipe = env.machines.get(machine)?.recipe.as_ref()?;
Some(Configured { env, machine, recipe })
}
#[must_use]
pub fn machine(&self) -> &'a str {
self.machine
}
#[must_use]
pub fn recipe(&self) -> &'a Recipe {
self.recipe
}
pub fn template(&self, from: &ProjectPath) -> Result<String, String> {
let text = self
.recipe
.files
.get(from)
.ok_or_else(|| format!("the template {from} wasn't read when the files were generated: run `isoloom generate` in the project folder"))?;
fill(text, &Context::new(self.env, self.machine)).map_err(|e| format!("the template {from}: {e}"))
}
#[must_use]
pub fn password(&self, user: &str, password: &str) -> String {
password::hash(&self.env.name, self.machine, user, password)
}
}
#[must_use]
pub(crate) fn compact(library: &str) -> String {
let mut out = String::with_capacity(library.len());
for line in library.lines() {
let t = line.trim_start();
if !t.is_empty() && (!t.starts_with('#') || t.starts_with("# shellcheck")) {
out.push_str(line);
out.push('\n');
}
}
out
}
#[must_use]
pub fn rendered_path(machine: &str, from: &ProjectPath) -> String {
format!("{DIR}/{machine}/rendered/{from}")
}
#[must_use]
pub fn files(env: &Environment) -> Vec<GeneratedFile> {
let mut out = dockerfile::files(env);
let configured = || env.machines.keys().filter_map(|m| Configured::of(env, m));
let windows = |c: &Configured<'_>| c.recipe.family == isoloom_plan::configure::Family::Windows;
if configured().any(|c| !windows(&c) && uses_library(c.recipe)) && !out.iter().any(|f| f.path == shell::LIBRARY_PATH) {
out.push(shell::library_file());
}
if configured().any(|c| windows(&c) && uses_library(c.recipe)) {
out.push(powershell::library_file());
}
out
}
#[must_use]
pub fn uses_library(recipe: &Recipe) -> bool {
recipe
.resources()
.any(|r| !matches!(r, Resource::Run(_) | Resource::Reboot | Resource::Playbook(_)))
}
#[cfg(test)]
mod tests;