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;