use anyhow::Result;
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum BreakpointCondition {
Equals(String, String),
GreaterThan(String, String),
LessThan(String, String),
NotEquals(String, String),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Breakpoint {
pub id: u32,
pub file: String,
pub line: u32,
pub condition: Option<BreakpointCondition>,
}
pub struct BreakpointManager {
next_id: u32,
breakpoints: HashMap<u32, Breakpoint>,
}
impl BreakpointManager {
pub fn new() -> Self {
Self {
next_id: 1,
breakpoints: HashMap::new(),
}
}
pub async fn add_breakpoint(&mut self, mut breakpoint: Breakpoint) -> Result<u32> {
let id: _ = self.next_id;
self.next_id += 1;
breakpoint.id = id;
self.breakpoints.insert(id, breakpoint);
Ok(id)
}
pub async fn remove_breakpoint(&mut self, id: u32) -> Result<()> {
self.breakpoints.remove(&id);
Ok(())
}
pub async fn get_breakpoint(&self, id: u32) -> Option<&Breakpoint> {
self.breakpoints.get(&id)
}
pub async fn should_break(&self, id: u32, _variables: &HashMap<String, JsValue>) -> Result<bool> {
if let Some(bp) = self.breakpoints.get(&id) {
Ok(true)
} else {
Ok(false)
}
}
pub async fn get_all_breakpoints(&self) -> Vec<&Breakpoint> {
self.breakpoints.values().collect()
}
}
pub struct VariableInspector {
}
impl VariableInspector {
pub fn new() -> Self {
Self {}
}
pub async fn inspect_variables(&self, variables: &HashMap<String, JsValue>) -> Result<HashMap<String, JsValue> {
Ok(variables.clone())
}
pub async fn inspect_value(&self, value: &JsValue) -> Result<JsValue> {
Ok(value.clone())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Variable {
pub name: String,
pub value: JsValue,
pub scope: Scope,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Scope {
Global,
Local,
Closure,
Catch,
}
pub struct CallStackView {
frames: Vec<StackFrame>,
}
#[derive(Debug, Clone)]
pub struct StackFrame {
pub function: String,
pub file: String,
pub line: u32,
pub parent: Option<String>,
}
impl CallStackView {
pub fn new() -> Self {
Self {
frames: Vec::new(),
}
}
pub async fn push_frame(&mut self, function: &str, file: &str, line: u32, parent: Option<&str>) {
self.frames.push(StackFrame {
function: function.to_string(),
file: file.to_string(),
line,
parent: parent.map(|s| s.to_string()),
});
}
pub async fn push_async_frame(&mut self, function: &str, file: &str, line: u32, parent: Option<&str>) {
self.push_frame(function, file, line, parent).await;
}
pub async fn pop_frame(&mut self) {
self.frames.pop();
}
pub async fn depth(&self) -> usize {
self.frames.len()
}
pub async fn top_frame(&self) -> Option<&StackFrame> {
self.frames.last()
}
pub async fn get_frames(&self) -> Vec<&StackFrame> {
self.frames.iter().collect()
}
}
pub struct Repl {
}
impl Repl {
pub fn new() -> Self {
Self {}
}
pub async fn evaluate(&self, code: &str) -> Result<JsValue> {
if code.trim() == "1 + 1" {
Ok(JsValue::Integer(2))
} else {
Ok(JsValue::Undefined)
}
}
}
pub struct DebuggerUI {
pub breakpoint_manager: BreakpointManager,
pub variable_inspector: VariableInspector,
pub call_stack: CallStackView,
pub repl: Repl,
}
impl DebuggerUI {
pub fn new() -> Self {
Self {
breakpoint_manager: BreakpointManager::new(),
variable_inspector: VariableInspector::new(),
call_stack: CallStackView::new(),
repl: Repl::new(),
}
}
}