use std::collections::HashMap;
use anyhow::{anyhow, Result};
use easy_error::Error as BundError;
use rust_dynamic::value::Value;
use rust_multistackvm::multistackvm::VM;
use super::helpers::{active_config, active_store, push};
use crate::backup::BackupState;
pub fn register(vm: &mut VM) -> Result<()> {
let words: &[(&str, fn(&mut VM) -> std::result::Result<&mut VM, BundError>)] =
&[("ink.backup.last", w_last), ("ink.backup.list", w_list), ("ink.backup.make", w_make)];
for (name, f) in words {
vm.register_inline(name.to_string(), *f).map_err(|e| anyhow!("register {name}: {e}"))?;
}
for (name, _) in words {
if let Some(short) = name.strip_prefix("ink.") {
let _ = vm.register_alias(short.to_string(), name.to_string());
}
}
Ok(())
}
fn to_bund_err(e: anyhow::Error) -> BundError {
easy_error::err_msg(e.to_string())
}
fn w_last(vm: &mut VM) -> std::result::Result<&mut VM, BundError> {
do_last(vm).map_err(to_bund_err)
}
fn do_last(vm: &mut VM) -> Result<&mut VM> {
let tag = "ink.backup.last";
let store = active_store(tag)?;
let out = match BackupState::load(store.project_root()) {
Some(state) => {
let mut m: HashMap<String, Value> = HashMap::new();
m.insert("last_at".into(), Value::from_string(&state.last_at.to_rfc3339()));
Value::from_dict(m)
}
None => Value::nodata(),
};
push(vm, out);
Ok(vm)
}
fn w_list(vm: &mut VM) -> std::result::Result<&mut VM, BundError> {
do_list(vm).map_err(to_bund_err)
}
fn do_list(vm: &mut VM) -> Result<&mut VM> {
let tag = "ink.backup.list";
let store = active_store(tag)?;
let dir = crate::store::default_user_backup_dir(store.project_root());
let mut rows: Vec<(std::time::SystemTime, Value)> = Vec::new();
if let Ok(entries) = std::fs::read_dir(&dir) {
for entry in entries.flatten() {
let name = entry.file_name().to_string_lossy().to_string();
if !(name.starts_with("blackinkhaven_") && name.ends_with(".zip")) {
continue;
}
let Ok(meta) = entry.metadata() else { continue };
let mtime = meta.modified().unwrap_or(std::time::UNIX_EPOCH);
let mut m: HashMap<String, Value> = HashMap::new();
m.insert("name".into(), Value::from_string(&name));
m.insert("bytes".into(), Value::from_int(meta.len() as i64));
m.insert(
"modified".into(),
Value::from_string(&chrono::DateTime::<chrono::Utc>::from(mtime).to_rfc3339()),
);
rows.push((mtime, Value::from_dict(m)));
}
}
rows.sort_by(|a, b| b.0.cmp(&a.0));
push(vm, Value::from_list(rows.into_iter().map(|(_, v)| v).collect()));
Ok(vm)
}
fn w_make(vm: &mut VM) -> std::result::Result<&mut VM, BundError> {
do_make(vm).map_err(to_bund_err)
}
fn do_make(vm: &mut VM) -> Result<&mut VM> {
let tag = "ink.backup.make";
let store = active_store(tag)?;
let cfg = active_config(tag)?;
let root = std::fs::canonicalize(store.project_root())
.map_err(|e| anyhow!("{tag}: canonicalize project root: {e}"))?;
let out = crate::store::default_user_backup_dir(&root);
let skip = crate::cli::backup::skip_dirs_for(&root, &out);
let archive = crate::backup::create_backup(&root, &out, &skip, None)
.map_err(|e| anyhow!("{tag}: {e}"))?;
crate::backup::prune_backups(&out, cfg.backup.keep_last);
let mut m: HashMap<String, Value> = HashMap::new();
m.insert("archive".into(), Value::from_string(&archive.display().to_string()));
m.insert("kept".into(), Value::from_int(cfg.backup.keep_last as i64));
push(vm, Value::from_dict(m));
Ok(vm)
}