use std::collections::{HashMap, HashSet};
use serde::{Deserialize, Serialize};
use crate::zapcode_browser::compiler::CompiledProgram;
use crate::zapcode_browser::error::{Result, ZapcodeError};
use crate::zapcode_browser::sandbox::ResourceLimits;
use crate::zapcode_browser::value::Value;
use crate::zapcode_browser::vm::{CallFrame, Continuation, TryInfo, Vm, VmState};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct VmSnapshot {
program: CompiledProgram,
stack: Vec<Value>,
frames: Vec<CallFrame>,
globals: Vec<(String, Value)>,
try_stack: Vec<TryInfo>,
continuations: Vec<Continuation>,
stdout: String,
limits: ResourceLimits,
external_functions: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ZapcodeSnapshot {
data: Vec<u8>,
}
impl ZapcodeSnapshot {
pub(crate) fn capture(vm: &Vm) -> Result<Self> {
let builtin_names: HashSet<&str> = Vm::BUILTIN_GLOBAL_NAMES.iter().copied().collect();
let user_globals: Vec<(String, Value)> = vm
.globals
.iter()
.filter(|(k, _)| !builtin_names.contains(k.as_str()))
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
let snapshot = VmSnapshot {
program: vm.program.clone(),
stack: vm.stack.clone(),
frames: vm.frames.clone(),
globals: user_globals,
try_stack: vm.try_stack.clone(),
continuations: vm.continuations.clone(),
stdout: vm.stdout.clone(),
limits: vm.limits.clone(),
external_functions: vm.external_functions.iter().cloned().collect(),
};
let data = postcard::to_allocvec(&snapshot)
.map_err(|e| ZapcodeError::SnapshotError(format!("capture failed: {}", e)))?;
Ok(Self { data })
}
pub fn dump(&self) -> Result<Vec<u8>> {
postcard::to_allocvec(self)
.map_err(|e| ZapcodeError::SnapshotError(format!("dump failed: {}", e)))
}
pub fn load(bytes: &[u8]) -> Result<Self> {
postcard::from_bytes(bytes)
.map_err(|e| ZapcodeError::SnapshotError(format!("load failed: {}", e)))
}
pub fn resume(self, return_value: Value) -> Result<VmState> {
let vm_snap: VmSnapshot = postcard::from_bytes(&self.data)
.map_err(|e| ZapcodeError::SnapshotError(format!("resume decode failed: {}", e)))?;
let user_globals: HashMap<String, Value> = vm_snap.globals.into_iter().collect();
let ext_set: HashSet<String> = vm_snap.external_functions.into_iter().collect();
let mut vm = Vm::from_snapshot(
vm_snap.program,
vm_snap.stack,
vm_snap.frames,
user_globals,
vm_snap.try_stack,
vm_snap.continuations,
vm_snap.stdout,
vm_snap.limits,
ext_set,
);
vm.stack.push(return_value);
vm.resume_execution()
}
}