[9/n] [dropshot] add API traits with support for endpoints#984
Conversation
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
| //! for each type of response (which can also include documentation). This is | ||
| //! largely known statically, though generated at runtime. | ||
| //! | ||
| //! ### As a trait server |
There was a problem hiding this comment.
This is where the end-user documentation starts.
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1 [skip ci]
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1 [skip ci]
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1 [skip ci]
Created using spr 1.3.6-beta.1 [skip ci]
Created using spr 1.3.6-beta.1
| error: endpoint `bad_endpoint` has invalid attributes: missing field `path` | ||
| --> tests/fail/bad_server_endpoint26.rs:12:1 | ||
| | | ||
| 12 | #[dropshot::server] | ||
| | ^^^^^^^^^^^^^^^^^^^ | ||
| | | ||
| = note: this error originates in the attribute macro `dropshot::server` (in Nightly builds, run with -Z macro-backtrace for more info) |
There was a problem hiding this comment.
This is addressed by oxidecomputer/serde_tokenstream#194.
Created using spr 1.3.6-beta.1 [skip ci]
Created using spr 1.3.6-beta.1 [skip ci]
Created using spr 1.3.6-beta.1 [skip ci]
Created using spr 1.3.6-beta.1 [skip ci]
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
ahl
left a comment
There was a problem hiding this comment.
I'd say I looked that code "some" i.e. in the places that I felt most familiar with. I scanned through the tests to think about coverage, and I looked at the docs for end-user clarity. If you'd like me to look more thoroughly at some part(s) please let me know but my general feeling is that the mechanics don't require intense scrutiny.
| //! implementations to override the base behavior if desired (similar to std's | ||
| //! `Read` and `Write`). | ||
| //! * The behavior lives in a base trait, and the server is an extension trait | ||
| //! on top of it, with a blanket impl prohibiting overrides (similar to | ||
| //! tokio's `AsyncReadExt` and `AsyncWriteExt`). |
There was a problem hiding this comment.
I didn't follow the Read or AsyncReadExt analogies
There was a problem hiding this comment.
Expanded the doc comment to explain this in more detail.
Variations
The general degrees of freedom available when dealing with traits in Rust
are almost all available here. The main bit of flexibility that isn't
available is that server traits can't be object-safe.
Same trait with default methods
For example:
#[dropshot::server]
trait CounterServer {
type Context;
fn get_counter_impl(&self) -> impl Future<Output = u64> + Send;
fn set_counter_impl(
&self,
value: u64,
) -> impl Future<Output = Result<(), String>> + Send;
#[endpoint { method = GET, path = "/counter" }
async fn get_counter() {} // as below
#[endpoint { method = PUT, path = "/counter" }
async fn put_counter() {} // as below
}This is similar to how [std::io::Write]'s write_all method is defined as
a default method over the required write.
Supertrait
trait CounterBase {
fn get_counter_impl(&self) -> impl Future<Output = u64> + Send;
fn set_counter_impl(
&self,
value: u64,
) -> impl Future<Output = Result<(), String>> + Send;
}
#[dropshot::server]
trait CounterServer: CounterBase {
#[endpoint { method = GET, path = "/counter" }
async fn get_counter() {} // as below
#[endpoint { method = PUT, path = "/counter" }
async fn put_counter() {} // as below
}
// And optionally, a blanket impl.
impl<T: CounterBase> CounterServer for T {}With the optional blanket impl, implementations cannot override
CounterServer's behavior. This is similar to how
[tokio::io::AsyncWriteExt] has a blanket impl for
AsyncWrite.
| @@ -1,8 +1,15 @@ | |||
| // Copyright 2021 Oxide Computer Company | |||
| // | |||
| // Portions of this file are adapted from syn (https://github.com/dtolnay/syn), | |||
There was a problem hiding this comment.
I've added "adapted from syn" comments to the relevant bits (which is most of the file).
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
Created using spr 1.3.6-beta.1
As discussed in RFD 479 and oxidecomputer/omicron#5247.
Add support for a new way to define Dropshot APIs: via a trait. This builds on work done in the prior PRs in this series, such that it reuses as much shared logic from endpoints as possible.
I've tried to structure the PR series such that this one is almost entirely added code. It's also mostly tests, with around 1400 lines of "real" code.
See RFD 479 for more information.
TODO