Skip to content

Commit 3dc803f

Browse files
authored
Allow up to 5 extractors (#1382)
1 parent 1272cb4 commit 3dc803f

File tree

7 files changed

+340
-2
lines changed

7 files changed

+340
-2
lines changed

CHANGELOG.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
https://github.com/oxidecomputer/dropshot/compare/v0.16.2\...HEAD[Full list of commits]
1717

18+
* https://github.com/oxidecomputer/dropshot/pull/1382[#1382] Allow 2 more `SharedExtractor`s to be used with the `endpoint` macro
19+
1820
== 0.16.2 (released 2025-05-21)
1921

2022
https://github.com/oxidecomputer/dropshot/compare/v0.16.1\...v0.16.2[Full list of commits]

dropshot/src/extractor/common.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,9 @@ macro_rules! impl_rqextractor_for_tuple {
203203
}
204204
}}
205205

206-
// Implement `RequestExtractor` for any tuple consisting of 0-2 shared
206+
// Implement `RequestExtractor` for any tuple consisting of 0-4 shared
207207
// extractors and exactly one exclusive extractor.
208208
impl_rqextractor_for_tuple!(S1);
209209
impl_rqextractor_for_tuple!(S1, S2);
210+
impl_rqextractor_for_tuple!(S1, S2, S3);
211+
impl_rqextractor_for_tuple!(S1, S2, S3, S4);

dropshot/src/handler.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,14 @@ impl_HttpHandlerFunc_for_func_with_params!();
680680
impl_HttpHandlerFunc_for_func_with_params!((0, T0));
681681
impl_HttpHandlerFunc_for_func_with_params!((0, T1), (1, T2));
682682
impl_HttpHandlerFunc_for_func_with_params!((0, T1), (1, T2), (2, T3));
683+
impl_HttpHandlerFunc_for_func_with_params!((0, T1), (1, T2), (2, T3), (3, T4));
684+
impl_HttpHandlerFunc_for_func_with_params!(
685+
(0, T1),
686+
(1, T2),
687+
(2, T3),
688+
(3, T4),
689+
(4, T5)
690+
);
683691

684692
/// `RouteHandler` abstracts an `HttpHandlerFunc<FuncParams, ResponseType>` in a
685693
/// way that allows callers to invoke the handler without knowing the handler's

dropshot/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@
342342
//!
343343
//! `Query` and `Path` impl `SharedExtractor`. `TypedBody`, `UntypedBody`,
344344
//! `StreamingBody`, and `RawRequest` impl `ExclusiveExtractor`. Your function
345-
//! may accept 0-3 extractors, but only one can be `ExclusiveExtractor`, and it
345+
//! may accept 0-5 extractors, but only one can be `ExclusiveExtractor`, and it
346346
//! must be the last one. Otherwise, the order of extractor arguments does not
347347
//! matter.
348348
//!

dropshot/tests/integration-tests/openapi.rs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,78 @@ async fn handler30(
616616
todo!();
617617
}
618618

619+
#[derive(Deserialize, JsonSchema)]
620+
struct PathArgs31 {
621+
#[expect(unused)]
622+
aa: String,
623+
}
624+
625+
#[derive(Deserialize, JsonSchema)]
626+
struct Headers31 {
627+
#[expect(unused)]
628+
header_a: String,
629+
}
630+
631+
#[derive(Deserialize, JsonSchema)]
632+
struct Query31 {
633+
#[expect(unused)]
634+
query_a: String,
635+
}
636+
637+
#[endpoint {
638+
method = GET,
639+
path = "/testing/{aa}",
640+
tags = ["it"]
641+
}]
642+
async fn handler31(
643+
_: RequestContext<()>,
644+
_: Path<PathArgs31>,
645+
_: Header<Headers31>,
646+
_: Query<Query31>,
647+
_: UntypedBody,
648+
) -> Result<HttpResponseOk<CoolStruct>, HttpError> {
649+
todo!();
650+
}
651+
652+
#[derive(Debug, Clone, Serialize, JsonSchema)]
653+
pub struct CustomShared32 {
654+
pub a: String,
655+
}
656+
657+
#[async_trait::async_trait]
658+
impl dropshot::SharedExtractor for CustomShared32 {
659+
async fn from_request<Context: dropshot::ServerContext>(
660+
_rqctx: &RequestContext<Context>,
661+
) -> Result<Self, HttpError> {
662+
Ok(Self { a: "test".to_string() })
663+
}
664+
665+
fn metadata(
666+
_body_content_type: dropshot::ApiEndpointBodyContentType,
667+
) -> dropshot::ExtractorMetadata {
668+
dropshot::ExtractorMetadata {
669+
extension_mode: dropshot::ExtensionMode::None,
670+
parameters: vec![],
671+
}
672+
}
673+
}
674+
675+
#[endpoint {
676+
method = GET,
677+
path = "/testing32/{aa}",
678+
tags = ["it"]
679+
}]
680+
async fn handler32(
681+
_: RequestContext<()>,
682+
_: Path<PathArgs31>,
683+
_: Header<Headers31>,
684+
_: Query<Query31>,
685+
_: CustomShared32,
686+
_: UntypedBody,
687+
) -> Result<HttpResponseOk<CoolStruct>, HttpError> {
688+
todo!();
689+
}
690+
619691
fn make_api(
620692
maybe_tag_config: Option<TagConfig>,
621693
) -> Result<ApiDescription<()>, ApiDescriptionRegisterError> {
@@ -655,6 +727,8 @@ fn make_api(
655727
api.register(handler28)?;
656728
api.register(handler29)?;
657729
api.register(handler30)?;
730+
api.register(handler31)?;
731+
api.register(handler32)?;
658732
Ok(api)
659733
}
660734

dropshot/tests/test_openapi.json

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -764,6 +764,69 @@
764764
}
765765
}
766766
},
767+
"/testing/{aa}": {
768+
"get": {
769+
"tags": [
770+
"it"
771+
],
772+
"operationId": "handler31",
773+
"parameters": [
774+
{
775+
"in": "path",
776+
"name": "aa",
777+
"required": true,
778+
"schema": {
779+
"type": "string"
780+
}
781+
},
782+
{
783+
"in": "header",
784+
"name": "header_a",
785+
"required": true,
786+
"schema": {
787+
"type": "string"
788+
}
789+
},
790+
{
791+
"in": "query",
792+
"name": "query_a",
793+
"required": true,
794+
"schema": {
795+
"type": "string"
796+
}
797+
}
798+
],
799+
"requestBody": {
800+
"content": {
801+
"application/octet-stream": {
802+
"schema": {
803+
"type": "string",
804+
"format": "binary"
805+
}
806+
}
807+
},
808+
"required": true
809+
},
810+
"responses": {
811+
"200": {
812+
"description": "successful operation",
813+
"content": {
814+
"application/json": {
815+
"schema": {
816+
"$ref": "#/components/schemas/CoolStruct"
817+
}
818+
}
819+
}
820+
},
821+
"4XX": {
822+
"$ref": "#/components/responses/Error"
823+
},
824+
"5XX": {
825+
"$ref": "#/components/responses/Error"
826+
}
827+
}
828+
}
829+
},
767830
"/testing/{aa}/{bb}": {
768831
"put": {
769832
"tags": [
@@ -808,6 +871,69 @@
808871
}
809872
}
810873
},
874+
"/testing32/{aa}": {
875+
"get": {
876+
"tags": [
877+
"it"
878+
],
879+
"operationId": "handler32",
880+
"parameters": [
881+
{
882+
"in": "path",
883+
"name": "aa",
884+
"required": true,
885+
"schema": {
886+
"type": "string"
887+
}
888+
},
889+
{
890+
"in": "header",
891+
"name": "header_a",
892+
"required": true,
893+
"schema": {
894+
"type": "string"
895+
}
896+
},
897+
{
898+
"in": "query",
899+
"name": "query_a",
900+
"required": true,
901+
"schema": {
902+
"type": "string"
903+
}
904+
}
905+
],
906+
"requestBody": {
907+
"content": {
908+
"application/octet-stream": {
909+
"schema": {
910+
"type": "string",
911+
"format": "binary"
912+
}
913+
}
914+
},
915+
"required": true
916+
},
917+
"responses": {
918+
"200": {
919+
"description": "successful operation",
920+
"content": {
921+
"application/json": {
922+
"schema": {
923+
"$ref": "#/components/schemas/CoolStruct"
924+
}
925+
}
926+
}
927+
},
928+
"4XX": {
929+
"$ref": "#/components/responses/Error"
930+
},
931+
"5XX": {
932+
"$ref": "#/components/responses/Error"
933+
}
934+
}
935+
}
936+
},
811937
"/thing_with_headers": {
812938
"get": {
813939
"tags": [

0 commit comments

Comments
 (0)