use crate::app_mode::AppMode; use prometeu_bytecode::Value; use serde::{Deserialize, Serialize}; pub const DEVTOOLS_PROTOCOL_VERSION: u32 = 1; #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "type")] pub enum DebugCommand { #[serde(rename = "ok")] Ok, #[serde(rename = "start")] Start, #[serde(rename = "pause")] Pause, #[serde(rename = "resume")] Resume, #[serde(rename = "step")] Step, #[serde(rename = "stepFrame")] StepFrame, #[serde(rename = "getState")] GetState, #[serde(rename = "setBreakpoint")] SetBreakpoint { pc: usize }, #[serde(rename = "listBreakpoints")] ListBreakpoints, #[serde(rename = "clearBreakpoint")] ClearBreakpoint { pc: usize }, } #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "type")] pub enum DebugResponse { #[serde(rename = "handshake")] Handshake { protocol_version: u32, runtime_version: String, cartridge: HandshakeCartridge }, #[serde(rename = "getState")] GetState { pc: usize, stack_top: Vec, frame_index: u64, app_id: u32 }, #[serde(rename = "breakpoints")] Breakpoints { pcs: Vec }, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct HandshakeCartridge { pub app_id: u32, pub title: String, pub app_version: String, pub app_mode: AppMode, } #[derive(Debug, Serialize, Deserialize)] #[serde(tag = "event")] pub enum DebugEvent { #[serde(rename = "breakpointHit")] BreakpointHit { pc: usize, frame_index: u64 }, #[serde(rename = "log")] Log { level: String, source: String, msg: String }, #[serde(rename = "telemetry")] Telemetry { frame_index: u64, vm_steps: u32, syscalls: u32, cycles: u64, cycles_budget: u64, host_cpu_time_us: u64, violations: u32, glyph_slots_used: u32, glyph_slots_total: u32, sound_slots_used: u32, sound_slots_total: u32, scene_slots_used: u32, scene_slots_total: u32, }, #[serde(rename = "fault")] Fault { kind: String, summary: String, pc: Option, trap_code: Option, opcode: Option, }, #[serde(rename = "cert")] Cert { rule: String, used: u64, limit: u64, frame_index: u64 }, } #[cfg(test)] mod tests { use super::*; #[test] fn test_telemetry_event_serialization() { let event = DebugEvent::Telemetry { frame_index: 10, vm_steps: 100, syscalls: 5, cycles: 5000, cycles_budget: 10000, host_cpu_time_us: 1200, violations: 0, glyph_slots_used: 1, glyph_slots_total: 16, sound_slots_used: 2, sound_slots_total: 16, scene_slots_used: 3, scene_slots_total: 16, }; let json = serde_json::to_string(&event).unwrap(); assert!(json.contains("\"event\":\"telemetry\"")); assert!(json.contains("\"cycles\":5000")); assert!(json.contains("\"cycles_budget\":10000")); } #[test] fn test_get_state_serialization() { let resp = DebugResponse::GetState { pc: 42, stack_top: vec![Value::Int64(10), Value::String("test".into()), Value::Boolean(true)], frame_index: 5, app_id: 1, }; let json = serde_json::to_string(&resp).unwrap(); assert!(json.contains("\"type\":\"getState\"")); assert!(json.contains("\"pc\":42")); assert!(json.contains("\"stack_top\":[10,\"test\",true]")); assert!(json.contains("\"frame_index\":5")); assert!(json.contains("\"app_id\":1")); } #[test] fn test_fault_event_serialization() { let event = DebugEvent::Fault { kind: "vm_trap".into(), summary: "PVM Trap 0x00000004".into(), pc: Some(12), trap_code: Some(4), opcode: Some(0x70), }; let json = serde_json::to_string(&event).unwrap(); assert!(json.contains("\"event\":\"fault\"")); assert!(json.contains("\"kind\":\"vm_trap\"")); assert!(json.contains("\"trap_code\":4")); assert!(json.contains("\"opcode\":112")); } }