use super::helpers::*;
use std::path::Path;
pub(crate) fn cmd_lock_compress(state_dir: &Path, json: bool) -> Result<(), String> {
let machines = discover_machines(state_dir);
let mut compressed = 0u64;
let mut total_saved = 0u64;
for m in &machines {
let lock_path = state_dir.join(m).join("state.lock.yaml");
if !lock_path.exists() {
continue;
}
let content = std::fs::read_to_string(&lock_path).unwrap_or_default();
let original_size = content.len() as u64;
if original_size == 0 {
continue;
}
let mut minified = String::new();
for line in content.lines() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with('#') {
continue;
}
minified.push_str(line);
minified.push('\n');
}
let new_size = minified.len() as u64;
if new_size < original_size {
let compressed_path = state_dir.join(format!("{m}.lock.yaml.min"));
std::fs::write(&compressed_path, &minified)
.map_err(|e| format!("Failed to write compressed lock: {e}"))?;
total_saved += original_size - new_size;
compressed += 1;
}
}
if json {
println!(r#"{{"compressed":{compressed},"bytes_saved":{total_saved}}}"#);
} else if compressed == 0 {
println!("No lock files needed compression");
} else {
println!("Compressed {compressed} lock files, saved {total_saved} bytes");
}
Ok(())
}
pub(crate) fn cmd_lock_archive(state_dir: &Path, json: bool) -> Result<(), String> {
let machines = discover_machines(state_dir);
let archive_dir = state_dir.join("archive");
let mut archived = 0u64;
for m in &machines {
let lock_path = state_dir.join(m).join("state.lock.yaml");
if !lock_path.exists() {
continue;
}
let events_path = state_dir.join(format!("{m}.events.jsonl"));
if events_path.exists() {
std::fs::create_dir_all(&archive_dir)
.map_err(|e| format!("Failed to create archive dir: {e}"))?;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let archive_name = format!("{m}.events.{timestamp}.jsonl");
let dest = archive_dir.join(&archive_name);
std::fs::copy(&events_path, &dest)
.map_err(|e| format!("Failed to archive {}: {}", events_path.display(), e))?;
archived += 1;
}
}
if json {
println!(r#"{{"archived":{archived}}}"#);
} else if archived == 0 {
println!("No event logs to archive");
} else {
println!(
"Archived {} event logs to {}",
archived,
archive_dir.display()
);
}
Ok(())
}
pub(crate) fn cmd_lock_snapshot(state_dir: &Path, json: bool) -> Result<(), String> {
let machines = discover_machines(state_dir);
let snapshot_dir = state_dir.join("snapshots");
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs();
let snapshot_name = format!("snapshot-{timestamp}");
let dest_dir = snapshot_dir.join(&snapshot_name);
let mut copied = 0u64;
for m in &machines {
let lock_path = state_dir.join(m).join("state.lock.yaml");
if !lock_path.exists() {
continue;
}
let dest_machine_dir = dest_dir.join(m);
std::fs::create_dir_all(&dest_machine_dir)
.map_err(|e| format!("Failed to create snapshot dir: {e}"))?;
let dest = dest_machine_dir.join("state.lock.yaml");
std::fs::copy(&lock_path, &dest)
.map_err(|e| format!("Failed to snapshot {}: {}", lock_path.display(), e))?;
copied += 1;
}
if json {
println!(r#"{{"snapshot":"{snapshot_name}","files":{copied}}}"#);
} else if copied == 0 {
println!("No lock files to snapshot");
} else {
println!("Created snapshot '{snapshot_name}' with {copied} lock files");
}
Ok(())
}
pub(crate) fn cmd_lock_defrag(state_dir: &Path, json: bool) -> Result<(), String> {
let machines = discover_machines(state_dir);
let mut defragged = 0u64;
for m in &machines {
let lock_path = state_dir.join(m).join("state.lock.yaml");
if !lock_path.exists() {
continue;
}
let content = std::fs::read_to_string(&lock_path).unwrap_or_default();
if let Ok(mut lock) = serde_yaml_ng::from_str::<crate::core::types::StateLock>(&content) {
let mut sorted: indexmap::IndexMap<String, crate::core::types::ResourceLock> =
indexmap::IndexMap::new();
let mut keys: Vec<String> = lock.resources.keys().cloned().collect();
keys.sort();
for key in keys {
if let Some(val) = lock.resources.swap_remove(&key) {
sorted.insert(key, val);
}
}
lock.resources = sorted;
let new_content = serde_yaml_ng::to_string(&lock)
.map_err(|e| format!("Failed to serialize lock: {e}"))?;
std::fs::write(&lock_path, &new_content)
.map_err(|e| format!("Failed to write lock: {e}"))?;
defragged += 1;
}
}
if json {
println!(r#"{{"defragged":{defragged}}}"#);
} else if defragged == 0 {
println!("No lock files to defragment");
} else {
println!("Defragmented {defragged} lock files (resources reordered alphabetically)");
}
Ok(())
}