#![cfg_attr(coverage_nightly, coverage(off))]
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DapRequest {
pub seq: i64,
#[serde(rename = "type")]
pub type_field: String,
pub command: String,
#[serde(default)]
pub arguments: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DapResponse {
pub seq: i64,
#[serde(rename = "type")]
pub type_field: String,
pub request_seq: i64,
pub success: bool,
pub command: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<serde_json::Value>,
}
impl DapResponse {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn success(
request_seq: i64,
seq: i64,
command: String,
body: Option<serde_json::Value>,
) -> Self {
Self {
seq,
type_field: "response".to_string(),
request_seq,
success: true,
command,
message: None,
body,
}
}
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn error(request_seq: i64, seq: i64, command: String, message: String) -> Self {
Self {
seq,
type_field: "response".to_string(),
request_seq,
success: false,
command,
message: Some(message),
body: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DapEvent {
pub seq: i64,
#[serde(rename = "type")]
pub type_field: String,
pub event: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct DapCapabilities {
pub supports_configuration_done_request: bool,
pub supports_function_breakpoints: bool,
pub supports_conditional_breakpoints: bool,
pub supports_hit_conditional_breakpoints: bool,
pub supports_evaluate_for_hovers: bool,
pub supports_step_back: bool,
pub supports_set_variable: bool,
pub supports_restart_frame: bool,
pub supports_goto_targets_request: bool,
pub supports_step_in_targets_request: bool,
pub supports_completions_request: bool,
pub supports_modules_request: bool,
pub supports_restart_request: bool,
pub supports_exception_options: bool,
pub supports_value_formatting_options: bool,
pub supports_exception_info_request: bool,
pub supports_terminate_debuggee: bool,
pub supports_delayed_stack_trace_loading: bool,
pub supports_loaded_sources_request: bool,
pub supports_log_points: bool,
pub supports_terminate_threads_request: bool,
pub supports_set_expression: bool,
pub supports_terminate_request: bool,
pub supports_data_breakpoints: bool,
pub supports_read_memory_request: bool,
pub supports_write_memory_request: bool,
pub supports_disassemble_request: bool,
pub supports_cancel_request: bool,
pub supports_breakpoint_locations_request: bool,
pub supports_clipboard_context: bool,
pub supports_stepping_granularity: bool,
pub supports_instruction_breakpoints: bool,
pub supports_exception_filter_options: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
pub struct Breakpoint {
pub source: String,
pub line: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub column: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub condition: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Variable {
pub name: String,
pub value: String,
#[serde(rename = "type")]
pub type_info: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub variables_reference: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StackFrame {
pub id: i64,
pub name: String,
pub source: Option<Source>,
pub line: i64,
pub column: i64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Source {
pub name: Option<String>,
pub path: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Scope {
pub name: String,
pub variables_reference: i64,
pub expensive: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Thread {
pub id: i64,
pub name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct InitializeRequestArguments {
pub client_id: Option<String>,
pub adapter_id: String,
pub lines_start_at1: Option<bool>,
pub columns_start_at1: Option<bool>,
pub path_format: Option<String>,
pub supports_variable_type: Option<bool>,
pub supports_variable_paging: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct LaunchRequestArguments {
pub program: String,
pub stop_on_entry: Option<bool>,
pub cwd: Option<String>,
#[serde(flatten)]
pub additional: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SetBreakpointsArguments {
pub source: Source,
pub breakpoints: Option<Vec<SourceBreakpoint>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceBreakpoint {
pub line: i64,
pub column: Option<i64>,
pub condition: Option<String>,
pub hit_condition: Option<String>,
pub log_message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SourceLocation {
pub file: String,
pub line: usize,
pub column: Option<usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SnapshotDelta {
pub changed_vars: HashMap<String, serde_json::Value>,
pub removed_vars: HashSet<String>,
pub stack_delta: i32,
}
impl SnapshotDelta {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "score_range")]
pub fn compute(previous: &ExecutionSnapshot, current: &ExecutionSnapshot) -> Self {
let mut changed_vars = HashMap::new();
let mut removed_vars = HashSet::new();
for (key, value) in ¤t.variables {
if let Some(prev_value) = previous.variables.get(key) {
if prev_value != value {
changed_vars.insert(key.clone(), value.clone());
}
} else {
changed_vars.insert(key.clone(), value.clone());
}
}
for key in previous.variables.keys() {
if !current.variables.contains_key(key) {
removed_vars.insert(key.clone());
}
}
let stack_delta = (current.call_stack.len() as i32) - (previous.call_stack.len() as i32);
Self {
changed_vars,
removed_vars,
stack_delta,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionSnapshot {
pub timestamp: u64,
pub sequence: usize,
pub variables: HashMap<String, serde_json::Value>,
pub call_stack: Vec<StackFrame>,
pub location: SourceLocation,
#[serde(skip_serializing_if = "Option::is_none")]
pub delta: Option<SnapshotDelta>,
}
impl ExecutionSnapshot {
#[provable_contracts_macros::contract("pmat-core.yaml", equation = "check_compliance")]
pub fn apply_delta(&self, delta: &SnapshotDelta) -> Self {
let mut new_variables = self.variables.clone();
for (key, value) in &delta.changed_vars {
new_variables.insert(key.clone(), value.clone());
}
for key in &delta.removed_vars {
new_variables.remove(key);
}
Self {
timestamp: self.timestamp + 1000, sequence: self.sequence + 1,
variables: new_variables,
call_stack: self.call_stack.clone(), location: self.location.clone(),
delta: Some(delta.clone()),
}
}
}
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dap_response_success_serialization() {
let response = DapResponse::success(
1,
2,
"initialize".to_string(),
Some(serde_json::json!({"test": "value"})),
);
assert_eq!(response.type_field, "response");
assert!(response.success);
assert_eq!(response.command, "initialize");
assert!(response.message.is_none());
assert!(response.body.is_some());
}
#[test]
fn test_dap_response_error_serialization() {
let response = DapResponse::error(
1,
2,
"unknown".to_string(),
"Command not supported".to_string(),
);
assert_eq!(response.type_field, "response");
assert!(!response.success);
assert_eq!(response.command, "unknown");
assert_eq!(response.message, Some("Command not supported".to_string()));
assert!(response.body.is_none());
}
#[test]
fn test_breakpoint_equality() {
let bp1 = Breakpoint {
source: "main.rs".to_string(),
line: 10,
column: None,
condition: None,
};
let bp2 = Breakpoint {
source: "main.rs".to_string(),
line: 10,
column: None,
condition: None,
};
assert_eq!(bp1, bp2);
}
#[test]
fn test_dap_capabilities_default() {
let caps = DapCapabilities::default();
assert!(!caps.supports_configuration_done_request);
assert!(!caps.supports_conditional_breakpoints);
}
}