use crate::config::resolve_config;
use crate::container::{
build_named_service_args, ensure_container_available, list_service_containers, list_service_status,
stop_container, tail_container_logs,
};
use crate::git::resolve_repo_root;
use crate::util::{die, print_json};
pub fn services_up(repo_path: &str, config: Option<String>, restart: bool) {
let repo_root = resolve_repo_root(repo_path);
let base_config = resolve_config(&repo_root, config.as_deref());
if base_config.services.is_empty() {
die("no services defined in config");
}
ensure_container_available();
let prefix = repo_key(&repo_root);
if restart {
for name in list_service_containers(&prefix) {
stop_container(&name);
}
}
let net = base_config.network.clone();
for svc in &base_config.services {
if svc.name.is_empty() || svc.image.is_empty() {
die("service entries require name and image");
}
let (name, args) = build_named_service_args(&prefix, svc, &repo_root, &net);
let status = crate::util::run_cmd(&args, None, true);
if !status.success() {
die("failed to start service container");
}
println!("started {}", name);
}
}
pub fn services_down(repo_path: &str, config: Option<String>) {
let repo_root = resolve_repo_root(repo_path);
let base_config = resolve_config(&repo_root, config.as_deref());
ensure_container_available();
let prefix = repo_key(&repo_root);
if !base_config.services.is_empty() {
for svc in &base_config.services {
let name = format!("vvbox-svc-{}-{}", prefix, svc.name);
stop_container(&name);
}
} else {
for name in list_service_containers(&prefix) {
stop_container(&name);
}
}
println!("services stopped");
}
pub fn services_status(repo_path: &str, config: Option<String>, json: bool) {
let repo_root = resolve_repo_root(repo_path);
let base_config = resolve_config(&repo_root, config.as_deref());
ensure_container_available();
let prefix = repo_key(&repo_root);
let statuses = list_service_status(&prefix);
if json {
let mut arr = vec![];
if !base_config.services.is_empty() {
for svc in &base_config.services {
let name = format!("vvbox-svc-{}-{}", prefix, svc.name);
let status = statuses
.iter()
.find(|(id, _)| id == &name)
.map(|(_, s)| s.clone())
.unwrap_or_else(|| "missing".to_string());
arr.push(serde_json::json!({ "name": name, "status": status }));
}
} else {
for (id, status) in statuses {
arr.push(serde_json::json!({ "name": id, "status": status }));
}
}
print_json(&serde_json::json!({ "services": arr }));
return;
}
if !base_config.services.is_empty() {
for svc in &base_config.services {
let name = format!("vvbox-svc-{}-{}", prefix, svc.name);
let status = statuses
.iter()
.find(|(id, _)| id == &name)
.map(|(_, s)| s.clone())
.unwrap_or_else(|| "missing".to_string());
println!("{} {}", name, status);
}
} else if statuses.is_empty() {
println!("no services running");
} else {
for (id, status) in statuses {
println!("{} {}", id, status);
}
}
}
pub fn services_restart(repo_path: &str, config: Option<String>) {
let repo_root = resolve_repo_root(repo_path);
let base_config = resolve_config(&repo_root, config.as_deref());
if base_config.services.is_empty() {
die("no services defined in config");
}
ensure_container_available();
let prefix = repo_key(&repo_root);
for name in list_service_containers(&prefix) {
stop_container(&name);
}
let net = base_config.network.clone();
for svc in &base_config.services {
if svc.name.is_empty() || svc.image.is_empty() {
die("service entries require name and image");
}
let (name, args) = build_named_service_args(&prefix, svc, &repo_root, &net);
let status = crate::util::run_cmd(&args, None, true);
if !status.success() {
die("failed to start service container");
}
println!("started {}", name);
}
}
pub fn services_logs(repo_path: &str, config: Option<String>, name: String, tail: Option<u32>, since: Option<String>, no_follow: bool) {
let repo_root = resolve_repo_root(repo_path);
let base_config = resolve_config(&repo_root, config.as_deref());
if base_config.services.is_empty() {
die("no services defined in config");
}
ensure_container_available();
let prefix = repo_key(&repo_root);
let svc_name = format!("vvbox-svc-{}-{}", prefix, name);
let ok = tail_container_logs(&svc_name, tail, since, !no_follow);
if !ok {
die("failed to read service logs");
}
}
pub fn repo_key(repo_root: &std::path::Path) -> String {
let binding = repo_root.to_string_lossy();
let bytes = binding.as_bytes();
let mut hash: u64 = 1469598103934665603;
for b in bytes {
hash ^= *b as u64;
hash = hash.wrapping_mul(1099511628211);
}
format!("{:x}", hash)
}