use crate::model::AppMode; use serde::{Deserialize, Serialize}; use crate::virtual_machine::Value; 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, 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, gfx_used_bytes: usize, gfx_inflight_bytes: usize, gfx_slots_occupied: u32, audio_used_bytes: usize, audio_inflight_bytes: usize, audio_slots_occupied: u32, }, #[serde(rename = "cert")] Cert { rule: String, used: u64, limit: u64, frame_index: u64, }, } #[cfg(test)] mod tests { use super::*; use crate::virtual_machine::Value; #[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, gfx_used_bytes: 1024, gfx_inflight_bytes: 0, gfx_slots_occupied: 1, audio_used_bytes: 2048, audio_inflight_bytes: 0, audio_slots_occupied: 2, }; 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")); } }