Skip to main content

dap/
lib.rs

1//! # miden-debug-dap, a Rust implementation of the Debug Adapter Protocol
2//!
3//! ## Introduction
4//!
5//! This crate is a Rust implementation of the [Debug Adapter Protocol](https://microsoft.github.io/debug-adapter-protocol/)
6//! (or DAP for short).
7//!
8//! The best way to think of DAP is to compare it to [LSP](https://microsoft.github.io/language-server-protocol/)
9//! (Language Server Protocol) but for debuggers. The core idea is the same: a protocol that serves
10//! as *lingua franca* for editors and debuggers to talk to each other. This means that an editor
11//! that implements DAP can use a debugger that also implements DAP.
12//!
13//! In practice, the adapter might be separate from the actual debugger. For example, one could
14//! implement an adapter that writes commands to the stdin of a gdb subprocess, then parses
15//! the output it receives (this is why it's called an "adapter" - it adapts the debugger to
16//! editors that know DAP).
17//!
18//! ## Minimal example
19//!
20//! To get started, create a binary project and add `miden-debug-dap` to your Cargo.toml:
21//!
22//! ```toml
23//! [package]
24//! name = "dummy-server"
25//! version = "*"
26//! edition = "2024"
27//!
28//! [dependencies]
29//! miden-debug-dap = "*"
30//! ```
31//!
32//! Our dummy server is going to read its input from a text file and write the output to stdout.
33//!
34//! ```rust
35//! use std::fs::File;
36//! use std::io::{BufReader, BufWriter};
37//!
38//! use thiserror::Error;
39//!
40//! use dap::prelude::*;
41//!
42//! #[derive(Error, Debug)]
43//! enum MyAdapterError {
44//!   #[error("Unhandled command")]
45//!   UnhandledCommandError,
46//!
47//!   #[error("Missing command")]
48//!   MissingCommandError,
49//! }
50//!
51//! type DynResult<T> = std::result::Result<T, Box<dyn std::error::Error>>;
52//!
53//! fn main() -> DynResult<()> {
54//!   let output = BufWriter::new(std::io::stdout());
55//!   let f = File::open("testinput.txt")?;
56//!   let input = BufReader::new(f);
57//!   let mut server = Server::new(input, output);
58//!
59//!   let req = match server.poll_request()? {
60//!     Some(req) => req,
61//!     None => return Err(Box::new(MyAdapterError::MissingCommandError)),
62//!   };
63//!   if let Command::Initialize(_) = req.command {
64//!     let rsp = req.success(
65//!       ResponseBody::Initialize(Some(types::Capabilities {
66//!         ..Default::default()
67//!       })),
68//!     );
69//!
70//!     // When you call respond, send_event etc. the message will be wrapped
71//!     // in a base message with a appropriate seq number, so you don't have to keep track of that yourself
72//!     server.respond(rsp)?;
73//!
74//!     server.send_event(Event::Initialized)?;
75//!   } else {
76//!     return Err(Box::new(MyAdapterError::UnhandledCommandError));
77//!   }
78//!
79//!   // You can send events from other threads while the server is blocked
80//!   // polling for requests by grabbing a `ServerOutput` mutex:
81//!   let server_output = server.output.clone();
82//!   std::thread::spawn(move || {
83//!       std::thread::sleep(std::time::Duration::from_millis(500));
84//!
85//!       let mut server_output = server_output.lock().unwrap();
86//!       server_output
87//!           .send_event(Event::Capabilities(events::CapabilitiesEventBody {
88//!               ..Default::default()
89//!           }))
90//!           .unwrap();
91//!   });
92//!
93//!   // The thread will concurrently send an event while we are polling
94//!   // for the next request
95//!   let _ = server.poll_request()?;
96//!
97//!   Ok(())
98//! }
99//! ```
100pub mod base_message;
101pub mod errors;
102pub mod events;
103pub mod prelude;
104pub mod requests;
105pub mod responses;
106pub mod reverse_requests;
107pub mod server;
108pub mod types;
109pub mod utils;
110pub use utils::get_spec_version;