dap-rs, a Rust implementation of the Debug Adapter Protocol
Introduction
This crate is a Rust implementation of the Debug Adapter Protocol (or DAP for short).
The best way to think of DAP is to compare it to LSP (Language Server Protocol) but for debuggers. The core idea is the same: a protocol that serves as lingua franca for editors and debuggers to talk to each other. This means that an editor that implements DAP can use a debugger that also implements DAP.
In practice, the adapter might be separate from the actual debugger. For example, one could implement an adapter that writes commands to the stdin of a gdb subprocess, then parses the output it receives (this is why it's called an "adapter" - it adapts the debugger to editors that know DAP).
License
This library is dual-licensed as MIT and Apache 2.0. That means users may choose either of these licenses. In general, these are non-restrictive, non-viral licenses, a.k.a. "do what you want but no guarantees from me".
Commercial support is available on a contract basis (contact me szelei.t@gmail.com).
Getting started
To get started, create a binary project and add dap to your Cargo.toml:
[]
= "dummy-server"
= "0.1.0"
= "2021"
[]
= "0.1.0"
Import the following types:
use ;
Create your Adapter which is going to be the heart of your implementation.
Its accept function will be called for each incoming request, and each return type will be
returned to the client in its serialized form.
;
The request will be deserialized and its command field will be one of the requests
variants. In practice, this function will likely contain a large match expression or
some other means of dispatching the requests to code that can handle them.
The currently unused _ctx parameter could be used to send events and reverse requests to the
client.
What about error handling?
As you may have noticed, we are unwrapping a result in the above example, which may cause
a panic. In real-life application code, you should seldom unwrap a Result this way.
For DAP clients, and ultimately, users, it is important to see the error that is happening,
instead of the adapter suddenly dying on them. The DAP protocol allows returning response messages
that contain errors. This is why the accept function does not return a result. Implementors
of the Adapter trait should map their errors to error responses.
After this, you will want to create the infrastructure for communicating with the client. First, an instance of your adapter:
let adapter = MyAdapter;
Then, a client. In this crate, the Client is responsible for sending the responses, events and reverse requests to the actual client that is connected.
let client = new;
BasicClient is a builtin implementation that takes a BufWriter where the serialized
responses, event and reverse requests are written. It is easy and typical to write to the
standard output, but some implementations may want to write to a socket instead.
The Client and Context traits can be implemented to provide different behavior.
Next, we create the Server. The Server ties together the Adapter and the Client. Most
importantly, it is the server's responsibility to deserialize the incoming JSON requests,
pass them to the Adapter, then take the return value and pass it to the Client (which
in turn will serialize it and write it to real client's buffer).
let mut server = new;
Finally, we create a BufReader for the server which serves as the input mechanism and run the
server.
let mut reader = new;
server.run?;
The server will run until EOF is encountered.