diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc index 67900b84..a859ad4c 100644 --- a/CHANGELOG.adoc +++ b/CHANGELOG.adoc @@ -15,6 +15,8 @@ https://github.com/oxidecomputer/dropshot/compare/v0.16.5\...HEAD[Full list of commits] +* https://github.com/oxidecomputer/dropshot/pull/1490[#1490] Added `map` and `try_map` to `Query`, `Path`, `Header`, `HttpResponseOk`, `HttpResponseCreated`, `HttpResponseAccepted`, and `HttpResponseHeaders`, similar to `map` and `try_map` that already exist on `TypedBody`. + == 0.16.5 (released 2025-11-20) https://github.com/oxidecomputer/dropshot/compare/v0.16.4\...v0.16.5[Full list of commits] diff --git a/dropshot/src/extractor/body.rs b/dropshot/src/extractor/body.rs index 0c239baa..4bbdec18 100644 --- a/dropshot/src/extractor/body.rs +++ b/dropshot/src/extractor/body.rs @@ -51,7 +51,7 @@ impl pub fn map(self, f: F) -> TypedBody where T: JsonSchema + DeserializeOwned + Send + Sync, - F: Fn(BodyType) -> T, + F: FnOnce(BodyType) -> T, { TypedBody { inner: f(self.inner) } } @@ -60,7 +60,7 @@ impl pub fn try_map(self, f: F) -> Result, E> where T: JsonSchema + DeserializeOwned + Send + Sync, - F: Fn(BodyType) -> Result, + F: FnOnce(BodyType) -> Result, { Ok(TypedBody { inner: f(self.inner)? }) } diff --git a/dropshot/src/extractor/header.rs b/dropshot/src/extractor/header.rs index 644db013..317b8dbb 100644 --- a/dropshot/src/extractor/header.rs +++ b/dropshot/src/extractor/header.rs @@ -43,6 +43,26 @@ impl pub fn into_inner(self) -> HeaderType { self.inner } + + /// Convert this `Header` into one with a different type parameter; this + /// may be useful when multiple, related endpoints take header parameters + /// that are similar and convertible into a common type. + pub fn map(self, f: F) -> Header + where + T: DeserializeOwned + JsonSchema + Send + Sync, + F: FnOnce(HeaderType) -> T, + { + Header { inner: f(self.inner) } + } + + /// Similar to [`Header::map`] but with support for fallibility. + pub fn try_map(self, f: F) -> Result, E> + where + T: DeserializeOwned + JsonSchema + Send + Sync, + F: FnOnce(HeaderType) -> Result, + { + Ok(Header { inner: f(self.inner)? }) + } } /// Given an HTTP request, pull out the headers and attempt to deserialize diff --git a/dropshot/src/extractor/path.rs b/dropshot/src/extractor/path.rs index 362eb7ae..ac4ab128 100644 --- a/dropshot/src/extractor/path.rs +++ b/dropshot/src/extractor/path.rs @@ -31,6 +31,26 @@ impl Path { pub fn into_inner(self) -> PathType { self.inner } + + /// Convert this `Path` into one with a different type parameter; this + /// may be useful when multiple, related endpoints take path parameters that + /// are similar and convertible into a common type. + pub fn map(self, f: F) -> Path + where + T: JsonSchema + Send + Sync, + F: FnOnce(PathType) -> T, + { + Path { inner: f(self.inner) } + } + + /// Similar to [`Path::map`] but with support for fallibility. + pub fn try_map(self, f: F) -> Result, E> + where + T: JsonSchema + Send + Sync, + F: FnOnce(PathType) -> Result, + { + Ok(Path { inner: f(self.inner)? }) + } } // The `SharedExtractor` implementation for Path describes how to diff --git a/dropshot/src/extractor/query.rs b/dropshot/src/extractor/query.rs index 8209a139..fa936c95 100644 --- a/dropshot/src/extractor/query.rs +++ b/dropshot/src/extractor/query.rs @@ -31,6 +31,26 @@ impl Query { pub fn into_inner(self) -> QueryType { self.inner } + + /// Convert this `Query` into one with a different type parameter; this + /// may be useful when multiple, related endpoints take query parameters + /// that are similar and convertible into a common type. + pub fn map(self, f: F) -> Query + where + T: DeserializeOwned + JsonSchema + Send + Sync, + F: FnOnce(QueryType) -> T, + { + Query { inner: f(self.inner) } + } + + /// Similar to [`Query::map`] but with support for fallibility. + pub fn try_map(self, f: F) -> Result, E> + where + T: DeserializeOwned + JsonSchema + Send + Sync, + F: FnOnce(QueryType) -> Result, + { + Ok(Query { inner: f(self.inner)? }) + } } /// Given an HTTP request, pull out the query string and attempt to deserialize diff --git a/dropshot/src/handler.rs b/dropshot/src/handler.rs index c978a4fd..539d912e 100644 --- a/dropshot/src/handler.rs +++ b/dropshot/src/handler.rs @@ -1077,6 +1077,30 @@ where pub struct HttpResponseCreated( pub T, ); + +impl HttpResponseCreated { + /// Convert this response into one with a different type parameter; this + /// may be useful when multiple, related endpoints return similar response + /// types that are convertible into a common type. + pub fn map(self, f: F) -> HttpResponseCreated + where + U: HttpResponseContent + Send + Sync + 'static, + F: FnOnce(T) -> U, + { + HttpResponseCreated(f(self.0)) + } + + /// Similar to [`HttpResponseCreated::map`] but with support for + /// fallibility. + pub fn try_map(self, f: F) -> Result, E> + where + U: HttpResponseContent + Send + Sync + 'static, + F: FnOnce(T) -> Result, + { + Ok(HttpResponseCreated(f(self.0)?)) + } +} + impl HttpCodedResponse for HttpResponseCreated { @@ -1099,6 +1123,30 @@ impl pub struct HttpResponseAccepted( pub T, ); + +impl HttpResponseAccepted { + /// Convert this response into one with a different type parameter; this + /// may be useful when multiple, related endpoints return similar response + /// types that are convertible into a common type. + pub fn map(self, f: F) -> HttpResponseAccepted + where + U: HttpResponseContent + Send + Sync + 'static, + F: FnOnce(T) -> U, + { + HttpResponseAccepted(f(self.0)) + } + + /// Similar to [`HttpResponseAccepted::map`] but with support for + /// fallibility. + pub fn try_map(self, f: F) -> Result, E> + where + U: HttpResponseContent + Send + Sync + 'static, + F: FnOnce(T) -> Result, + { + Ok(HttpResponseAccepted(f(self.0)?)) + } +} + impl HttpCodedResponse for HttpResponseAccepted { @@ -1120,6 +1168,29 @@ impl pub struct HttpResponseOk( pub T, ); + +impl HttpResponseOk { + /// Convert this response into one with a different type parameter; this + /// may be useful when multiple, related endpoints return similar response + /// types that are convertible into a common type. + pub fn map(self, f: F) -> HttpResponseOk + where + U: HttpResponseContent + Send + Sync + 'static, + F: FnOnce(T) -> U, + { + HttpResponseOk(f(self.0)) + } + + /// Similar to [`HttpResponseOk::map`] but with support for fallibility. + pub fn try_map(self, f: F) -> Result, E> + where + U: HttpResponseContent + Send + Sync + 'static, + F: FnOnce(T) -> Result, + { + Ok(HttpResponseOk(f(self.0)?)) + } +} + impl HttpCodedResponse for HttpResponseOk { @@ -1362,6 +1433,35 @@ impl< pub fn headers_mut(&mut self) -> &mut HeaderMap { &mut self.other_headers } + + /// Convert this response into one with a different body type parameter; + /// this may be useful when multiple, related endpoints return similar + /// response types that are convertible into a common type. + pub fn map(self, f: F) -> HttpResponseHeaders + where + U: HttpCodedResponse, + F: FnOnce(T) -> U, + { + HttpResponseHeaders { + body: f(self.body), + structured_headers: self.structured_headers, + other_headers: self.other_headers, + } + } + + /// Similar to [`HttpResponseHeaders::map`] but with support for + /// fallibility. + pub fn try_map(self, f: F) -> Result, E> + where + U: HttpCodedResponse, + F: FnOnce(T) -> Result, + { + Ok(HttpResponseHeaders { + body: f(self.body)?, + structured_headers: self.structured_headers, + other_headers: self.other_headers, + }) + } } impl< T: HttpCodedResponse,