use crate::packs::schema::{ServicePack, ServicePackSignature};
use anyhow::{Context, Result};
use std::collections::HashMap;
use std::fs;
use std::path::{Path, PathBuf};
use tracing::{debug, info, warn};
pub struct ServicePackLoader;
impl ServicePackLoader {
pub fn load_all(project_path: &Path) -> Result<Vec<ServicePack>> {
let mut packs = Vec::new();
if let Ok(user_packs) = Self::load_user_packs() {
packs.extend(user_packs);
}
if let Ok(project_packs) = Self::load_project_packs(project_path) {
packs.extend(project_packs);
}
info!("Loaded {} service pack(s)", packs.len());
Ok(packs)
}
fn load_user_packs() -> Result<Vec<ServicePack>> {
let packs_dir = Self::user_packs_dir()?;
if !packs_dir.exists() {
return Ok(Vec::new());
}
Self::load_packs_from_dir(&packs_dir)
}
fn load_project_packs(project_path: &Path) -> Result<Vec<ServicePack>> {
let packs_dir = project_path.join(".darpan.d").join("packs");
if !packs_dir.exists() {
return Ok(Vec::new());
}
Self::load_packs_from_dir(&packs_dir)
}
fn load_packs_from_dir(dir: &Path) -> Result<Vec<ServicePack>> {
let mut packs = Vec::new();
let entries = fs::read_dir(dir)
.with_context(|| format!("Failed to read packs directory: {:?}", dir))?;
for entry in entries {
let entry = entry?;
let path = entry.path();
if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("yml") {
match Self::load_pack_from_file(&path) {
Ok(pack) => {
debug!("Loaded service pack: {} v{}", pack.name, pack.version);
packs.push(pack);
}
Err(e) => {
warn!("Failed to load pack from {:?}: {}", path, e);
}
}
}
}
Ok(packs)
}
fn load_pack_from_file(path: &Path) -> Result<ServicePack> {
let content = fs::read_to_string(path)
.with_context(|| format!("Failed to read pack file: {:?}", path))?;
let pack: ServicePack = serde_yaml::from_str(&content)
.with_context(|| format!("Failed to parse pack file: {:?}", path))?;
Ok(pack)
}
pub fn user_packs_dir() -> Result<PathBuf> {
let config_dir = dirs::config_dir()
.context("Could not determine config directory")?
.join("darpan")
.join("packs");
fs::create_dir_all(&config_dir)?;
Ok(config_dir)
}
pub fn project_packs_dir(project_path: &Path) -> PathBuf {
project_path.join(".darpan.d").join("packs")
}
pub fn build_signature_map(packs: &[ServicePack]) -> HashMap<String, Vec<&ServicePackSignature>> {
let mut map: HashMap<String, Vec<&ServicePackSignature>> = HashMap::new();
for pack in packs {
for signature in &pack.signatures {
if let Some(port) = signature.port {
map.entry(format!("port:{}", port))
.or_insert_with(Vec::new)
.push(signature);
}
if let Some(ref pattern) = signature.process_pattern {
map.entry(format!("process:{}", pattern))
.or_insert_with(Vec::new)
.push(signature);
}
if let Some(ref unit) = signature.systemd_unit {
map.entry(format!("systemd:{}", unit))
.or_insert_with(Vec::new)
.push(signature);
}
if let Some(ref image) = signature.docker_image {
map.entry(format!("docker_image:{}", image))
.or_insert_with(Vec::new)
.push(signature);
}
}
}
map
}
}