From 8eefb0d9c7e02f3f3af3bd53c679df269a4706cf Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 7 Jul 2023 14:29:29 -0400 Subject: [PATCH 1/3] Add a runtime for server logic --- .../src/example_types/another/test_service.rs | 2 + conjure-codegen/src/servers.rs | 5 +- conjure-http/src/private/server.rs | 84 ++++++- conjure-http/src/server.rs | 224 +++++++++++++----- conjure-macros/src/endpoints.rs | 53 +++-- conjure-test/src/test/servers.rs | 31 ++- example-api/src/another/test_service.rs | 2 + 7 files changed, 310 insertions(+), 91 deletions(-) diff --git a/conjure-codegen/src/example_types/another/test_service.rs b/conjure-codegen/src/example_types/another/test_service.rs index 05209690..e741d725 100644 --- a/conjure-codegen/src/example_types/another/test_service.rs +++ b/conjure-codegen/src/example_types/another/test_service.rs @@ -1456,6 +1456,7 @@ where { fn endpoints( &self, + _: &conjure_http::private::Arc, ) -> Vec + Sync + Send>> { vec![ Box::new(GetFileSystemsEndpoint_(self.0.clone())), @@ -1490,6 +1491,7 @@ where { fn endpoints( &self, + _: &conjure_http::private::Arc, ) -> Vec + Sync + Send>> { vec![ Box::new(GetFileSystemsEndpoint_(self.0.clone())), diff --git a/conjure-codegen/src/servers.rs b/conjure-codegen/src/servers.rs index 6b3a640b..53c99ac6 100644 --- a/conjure-codegen/src/servers.rs +++ b/conjure-codegen/src/servers.rs @@ -288,7 +288,10 @@ fn generate_service_impl(ctx: &Context, def: &ServiceDefinition, style: Style) - T: #trait_name #params + 'static + #sync + #send, I: #input_trait> #i_traits, { - fn endpoints(&self) -> #vec<#box_ + Sync + Send>> + fn endpoints( + &self, + _: &conjure_http::private::Arc, + ) -> #vec<#box_ + Sync + Send>> { vec![ #(#endpoint_instances,)* diff --git a/conjure-http/src/private/server.rs b/conjure-http/src/private/server.rs index 74133674..9b94a5f8 100644 --- a/conjure-http/src/private/server.rs +++ b/conjure-http/src/private/server.rs @@ -1,6 +1,8 @@ use crate::private::{async_read_body, read_body, APPLICATION_JSON, APPLICATION_OCTET_STREAM}; use crate::server::{ - AsyncResponseBody, AsyncWriteBody, DecodeHeader, DecodeParam, ResponseBody, WriteBody, + AsyncDeserializeRequest, AsyncResponseBody, AsyncSerializeResponse, AsyncWriteBody, + ConjureRuntime, DecodeHeader, DecodeParam, DeserializeRequest, ResponseBody, SerializeResponse, + WriteBody, }; use crate::PathParams; use bytes::Bytes; @@ -9,7 +11,7 @@ use conjure_object::{BearerToken, FromPlain}; use conjure_serde::json; use futures_core::Stream; use http::header::{HeaderName, HeaderValue, AUTHORIZATION, CONTENT_LENGTH, CONTENT_TYPE, COOKIE}; -use http::request; +use http::{request, HeaderMap}; use http::{Response, StatusCode}; use serde::de::DeserializeOwned; use serde::Serialize; @@ -37,9 +39,13 @@ where from_plain(&value, param) } -pub fn path_param(parts: &request::Parts, param: &str) -> Result +pub fn path_param<'a, T, D>( + runtime: &'a ConjureRuntime, + parts: &request::Parts, + param: &str, +) -> Result where - D: DecodeParam, + D: DecodeParam<'a, T>, { let path_params = parts .extensions @@ -50,7 +56,9 @@ where .split('/') .map(percent_encoding::percent_decode_str) .map(|v| v.decode_utf8_lossy()); - D::decode(params).map_err(|e| e.with_safe_param("param", param)) + D::new(runtime) + .decode(params) + .map_err(|e| e.with_safe_param("param", param)) } fn from_plain(s: &str, param: &str) -> Result @@ -76,16 +84,19 @@ pub fn parse_query_params(parts: &request::Parts) -> HashMap, Vec( +pub fn query_param<'a, T, D>( + runtime: &'a ConjureRuntime, query_params: &HashMap, Vec>>, key: &str, param: &str, ) -> Result where - D: DecodeParam, + D: DecodeParam<'a, T>, { let values = query_params.get(key).into_iter().flatten(); - D::decode(values).map_err(|e| e.with_safe_param("param", param)) + D::new(runtime) + .decode(values) + .map_err(|e| e.with_safe_param("param", param)) } pub fn parse_query_param( @@ -186,11 +197,18 @@ where Ok(()) } -pub fn header_param(parts: &request::Parts, header: &str, param: &str) -> Result +pub fn header_param<'a, T, D>( + runtime: &'a ConjureRuntime, + parts: &request::Parts, + header: &str, + param: &str, +) -> Result where - D: DecodeHeader, + D: DecodeHeader<'a, T>, { - D::decode(parts.headers.get_all(header)).map_err(|e| e.with_safe_param("param", param)) + D::new(runtime) + .decode(parts.headers.get_all(header)) + .map_err(|e| e.with_safe_param("param", param)) } pub fn parse_required_header( @@ -277,6 +295,28 @@ fn parse_auth_inner( .map_err(|e| Error::service_safe(e, PermissionDenied::new())) } +pub fn body_arg<'a, D, T, I>( + runtime: &'a ConjureRuntime, + headers: &HeaderMap, + body: I, +) -> Result +where + D: DeserializeRequest<'a, T, I>, +{ + D::new(runtime).deserialize(headers, body) +} + +pub async fn async_body_arg<'a, D, T, I>( + runtime: &'a ConjureRuntime, + headers: &HeaderMap, + body: I, +) -> Result +where + D: AsyncDeserializeRequest<'a, T, I>, +{ + D::new(runtime).deserialize(headers, body).await +} + pub fn decode_empty_request(_parts: &request::Parts, _body: I) -> Result<(), Error> { // nothing to do, just consume the body Ok(()) @@ -359,6 +399,28 @@ pub fn decode_binary_request(parts: &request::Parts, body: I) -> Result( + runtime: &'a ConjureRuntime, + request_headers: &HeaderMap, + value: T, +) -> Result>, Error> +where + S: SerializeResponse<'a, T, W>, +{ + S::new(runtime).serialize(request_headers, value) +} + +pub fn async_response<'a, S, T, W>( + runtime: &'a ConjureRuntime, + request_headers: &HeaderMap, + value: T, +) -> Result>, Error> +where + S: AsyncSerializeResponse<'a, T, W>, +{ + S::new(runtime).serialize(request_headers, value) +} + pub fn encode_empty_response() -> Response> { inner_encode_empty_response(ResponseBody::Empty) } diff --git a/conjure-http/src/server.rs b/conjure-http/src/server.rs index e96d7cc3..8406303d 100644 --- a/conjure-http/src/server.rs +++ b/conjure-http/src/server.rs @@ -35,6 +35,7 @@ use std::ops::Deref; use std::pin::Pin; use std::str; use std::str::FromStr; +use std::sync::Arc; /// Metadata about an HTTP endpoint. pub trait EndpointMetadata { @@ -207,13 +208,35 @@ pub enum AsyncResponseBody { /// A blocking Conjure service. pub trait Service { /// Returns the endpoints in the service. - fn endpoints(&self) -> Vec + Sync + Send>>; + fn endpoints( + &self, + runtime: &Arc, + ) -> Vec + Sync + Send>>; } /// An async Conjure service. pub trait AsyncService { /// Returns the endpoints in the service. - fn endpoints(&self) -> Vec + Sync + Send>>; + fn endpoints( + &self, + runtime: &Arc, + ) -> Vec + Sync + Send>>; +} + +/// A type providing server logic that is configured at runtime. +pub struct ConjureRuntime(()); + +impl ConjureRuntime { + /// Creates a new runtime with default settings. + pub fn new() -> Self { + ConjureRuntime(()) + } +} + +impl Default for ConjureRuntime { + fn default() -> Self { + Self::new() + } } /// A trait implemented by streaming bodies. @@ -347,26 +370,34 @@ impl Deref for MaybeBorrowed<'_, T> { /// A trait implemented by request body deserializers used by custom Conjure server trait /// implementations. -pub trait DeserializeRequest { +pub trait DeserializeRequest<'a, T, R> { + /// Creates a new deserializer. + fn new(runtime: &'a ConjureRuntime) -> Self; + /// Deserializes the request body. - fn deserialize(headers: &HeaderMap, body: R) -> Result; + fn deserialize(&self, headers: &HeaderMap, body: R) -> Result; } /// A trait implemented by response deserializers used by custom async Conjure server trait /// implementations. #[async_trait] -pub trait AsyncDeserializeRequest { +pub trait AsyncDeserializeRequest<'a, T, R> { + /// Creates a new deserializer. + fn new(runtime: &'a ConjureRuntime) -> Self; + /// Deserializes the request body. - async fn deserialize(headers: &HeaderMap, body: R) -> Result + async fn deserialize(&self, headers: &HeaderMap, body: R) -> Result where R: 'async_trait; } /// A request deserializer which acts as a Conjure-generated endpoint would. -pub enum ConjureRequestDeserializer {} +pub struct ConjureRequestDeserializer<'a> { + _runtime: &'a ConjureRuntime, +} -impl ConjureRequestDeserializer { - fn check_content_type(headers: &HeaderMap) -> Result<(), Error> { +impl ConjureRequestDeserializer<'_> { + fn check_content_type(&self, headers: &HeaderMap) -> Result<(), Error> { if headers.get(CONTENT_TYPE) != Some(&APPLICATION_JSON) { return Err(Error::service_safe( "invalid request Content-Type", @@ -378,79 +409,113 @@ impl ConjureRequestDeserializer { } } -impl DeserializeRequest for ConjureRequestDeserializer +impl<'a, T, R> DeserializeRequest<'a, T, R> for ConjureRequestDeserializer<'a> where T: DeserializeOwned, R: Iterator>, { - fn deserialize(headers: &HeaderMap, body: R) -> Result { - Self::check_content_type(headers)?; + #[inline] + fn new(runtime: &'a ConjureRuntime) -> Self { + ConjureRequestDeserializer { _runtime: runtime } + } + + fn deserialize(&self, headers: &HeaderMap, body: R) -> Result { + self.check_content_type(headers)?; let buf = private::read_body(body, Some(SERIALIZABLE_REQUEST_SIZE_LIMIT))?; json::server_from_slice(&buf).map_err(|e| Error::service(e, InvalidArgument::new())) } } #[async_trait] -impl AsyncDeserializeRequest for ConjureRequestDeserializer +impl<'a, T, R> AsyncDeserializeRequest<'a, T, R> for ConjureRequestDeserializer<'a> where T: DeserializeOwned, R: Stream> + Send, { - async fn deserialize(headers: &HeaderMap, body: R) -> Result + #[inline] + fn new(runtime: &'a ConjureRuntime) -> Self { + ConjureRequestDeserializer { _runtime: runtime } + } + + async fn deserialize(&self, headers: &HeaderMap, body: R) -> Result where R: 'async_trait, { - Self::check_content_type(headers)?; + self.check_content_type(headers)?; let buf = private::async_read_body(body, Some(SERIALIZABLE_REQUEST_SIZE_LIMIT)).await?; json::server_from_slice(&buf).map_err(|e| Error::service(e, InvalidArgument::new())) } } /// A trait implemented by response serializers used by custom Conjure server trait implementations. -pub trait SerializeResponse { +pub trait SerializeResponse<'a, T, W> { + /// Creates a new serializer. + fn new(runtime: &'a ConjureRuntime) -> Self; + /// Serializes the response. - fn serialize(request_headers: &HeaderMap, value: T) - -> Result>, Error>; + fn serialize( + &self, + request_headers: &HeaderMap, + value: T, + ) -> Result>, Error>; } /// A trait implemented by response serializers used by custom async Conjure server trait /// implementations. -pub trait AsyncSerializeResponse { +pub trait AsyncSerializeResponse<'a, T, W> { + /// Creates a new serializer. + fn new(runtime: &'a ConjureRuntime) -> Self; + /// Serializes the response. fn serialize( + &self, request_headers: &HeaderMap, value: T, ) -> Result>, Error>; } /// A serializer which encodes `()` as an empty body and status code of `204 No Content`. -pub enum EmptyResponseSerializer {} +pub struct EmptyResponseSerializer<'a> { + _runtime: &'a ConjureRuntime, +} -impl EmptyResponseSerializer { - fn serialize_inner(body: T) -> Result, Error> { +impl EmptyResponseSerializer<'_> { + fn serialize_inner(&self, body: T) -> Result, Error> { let mut response = Response::new(body); *response.status_mut() = StatusCode::NO_CONTENT; Ok(response) } } -impl SerializeResponse<(), W> for EmptyResponseSerializer { - fn serialize(_: &HeaderMap, _: ()) -> Result>, Error> { - Self::serialize_inner(ResponseBody::Empty) +impl<'a, W> SerializeResponse<'a, (), W> for EmptyResponseSerializer<'a> { + #[inline] + fn new(runtime: &'a ConjureRuntime) -> Self { + EmptyResponseSerializer { _runtime: runtime } + } + + fn serialize(&self, _: &HeaderMap, _: ()) -> Result>, Error> { + self.serialize_inner(ResponseBody::Empty) } } -impl AsyncSerializeResponse<(), W> for EmptyResponseSerializer { - fn serialize(_: &HeaderMap, _: ()) -> Result>, Error> { - Self::serialize_inner(AsyncResponseBody::Empty) +impl<'a, W> AsyncSerializeResponse<'a, (), W> for EmptyResponseSerializer<'a> { + fn new(runtime: &'a ConjureRuntime) -> Self { + EmptyResponseSerializer { _runtime: runtime } + } + + fn serialize(&self, _: &HeaderMap, _: ()) -> Result>, Error> { + self.serialize_inner(AsyncResponseBody::Empty) } } /// A serializer which acts like a Conjure-generated client would. -pub enum ConjureResponseSerializer {} +pub struct ConjureResponseSerializer<'a> { + _runtime: &'a ConjureRuntime, +} -impl ConjureResponseSerializer { +impl ConjureResponseSerializer<'_> { fn serialize_inner( + &self, value: T, make_body: impl FnOnce(Bytes) -> B, ) -> Result, Error> @@ -468,61 +533,86 @@ impl ConjureResponseSerializer { } } -impl SerializeResponse for ConjureResponseSerializer +impl<'a, T, W> SerializeResponse<'a, T, W> for ConjureResponseSerializer<'a> where T: Serialize, { + #[inline] + fn new(runtime: &'a ConjureRuntime) -> Self { + ConjureResponseSerializer { _runtime: runtime } + } + fn serialize( + &self, _request_headers: &HeaderMap, value: T, ) -> Result>, Error> { - Self::serialize_inner(value, ResponseBody::Fixed) + self.serialize_inner(value, ResponseBody::Fixed) } } -impl AsyncSerializeResponse for ConjureResponseSerializer +impl<'a, T, W> AsyncSerializeResponse<'a, T, W> for ConjureResponseSerializer<'a> where T: Serialize, { + #[inline] + fn new(runtime: &'a ConjureRuntime) -> Self { + ConjureResponseSerializer { _runtime: runtime } + } + fn serialize( + &self, _request_headers: &HeaderMap, value: T, ) -> Result>, Error> { - Self::serialize_inner(value, AsyncResponseBody::Fixed) + self.serialize_inner(value, AsyncResponseBody::Fixed) } } /// A trait implemented by header decoders used by custom Conjure server trait implementations. -pub trait DecodeHeader { +pub trait DecodeHeader<'a, T> { + /// Creates a new decoder. + fn new(runtime: &'a ConjureRuntime) -> Self; + /// Decodes the value from headers. - fn decode<'a, I>(headers: I) -> Result + fn decode<'b, I>(&self, headers: I) -> Result where - I: IntoIterator; + I: IntoIterator; } /// A trait implemented by URL parameter decoders used by custom Conjure server trait /// implementations. -pub trait DecodeParam { +pub trait DecodeParam<'a, T> { + /// Creates a new decoder. + fn new(runtime: &'a ConjureRuntime) -> Self; + /// Decodes the value from the sequence of values. /// /// The values have already been percent-decoded. - fn decode(params: I) -> Result + fn decode(&self, params: I) -> Result where I: IntoIterator, I::Item: AsRef; } /// A decoder which converts a single value using its [`FromStr`] implementation. -pub enum FromStrDecoder {} +pub struct FromStrDecoder<'a> { + _runtime: &'a ConjureRuntime, +} -impl DecodeHeader for FromStrDecoder +impl<'a, T> DecodeHeader<'a, T> for FromStrDecoder<'a> where T: FromStr, T::Err: Into>, { - fn decode<'a, I>(headers: I) -> Result + #[inline] + fn new(runtime: &'a ConjureRuntime) -> Self { + FromStrDecoder { _runtime: runtime } + } + + fn decode<'b, I>(&self, headers: I) -> Result where - I: IntoIterator, + I: IntoIterator, { only_item(headers)? .to_str() @@ -532,12 +622,17 @@ where } } -impl DecodeParam for FromStrDecoder +impl<'a, T> DecodeParam<'a, T> for FromStrDecoder<'a> where T: FromStr, T::Err: Into>, { - fn decode<'a, I>(params: I) -> Result + #[inline] + fn new(runtime: &'a ConjureRuntime) -> Self { + FromStrDecoder { _runtime: runtime } + } + + fn decode(&self, params: I) -> Result where I: IntoIterator, I::Item: AsRef, @@ -550,16 +645,23 @@ where } /// A decoder which converts an optional value using its [`FromStr`] implementation. -pub enum FromStrOptionDecoder {} +pub struct FromStrOptionDecoder<'a> { + _runtime: &'a ConjureRuntime, +} -impl DecodeHeader> for FromStrOptionDecoder +impl<'a, T> DecodeHeader<'a, Option> for FromStrOptionDecoder<'a> where T: FromStr, T::Err: Into>, { - fn decode<'a, I>(headers: I) -> Result, Error> + #[inline] + fn new(runtime: &'a ConjureRuntime) -> Self { + FromStrOptionDecoder { _runtime: runtime } + } + + fn decode<'b, I>(&self, headers: I) -> Result, Error> where - I: IntoIterator, + I: IntoIterator, { let Some(header) = optional_item(headers)? else { return Ok(None) }; let value = header @@ -571,12 +673,17 @@ where } } -impl DecodeParam> for FromStrOptionDecoder +impl<'a, T> DecodeParam<'a, Option> for FromStrOptionDecoder<'a> where T: FromStr, T::Err: Into>, { - fn decode(params: I) -> Result, Error> + #[inline] + fn new(runtime: &'a ConjureRuntime) -> Self { + FromStrOptionDecoder { _runtime: runtime } + } + + fn decode(&self, params: I) -> Result, Error> where I: IntoIterator, I::Item: AsRef, @@ -610,17 +717,26 @@ where /// A decoder which converts a sequence of values via its [`FromStr`] implementation into a /// collection via a [`FromIterator`] implementation. -pub struct FromStrSeqDecoder { +pub struct FromStrSeqDecoder<'a, U> { + _runtime: &'a ConjureRuntime, _p: PhantomData, } -impl DecodeParam for FromStrSeqDecoder +impl<'a, T, U> DecodeParam<'a, T> for FromStrSeqDecoder<'a, U> where T: FromIterator, U: FromStr, U::Err: Into>, { - fn decode(params: I) -> Result + #[inline] + fn new(runtime: &'a ConjureRuntime) -> Self { + FromStrSeqDecoder { + _runtime: runtime, + _p: PhantomData, + } + } + + fn decode(&self, params: I) -> Result where I: IntoIterator, I::Item: AsRef, diff --git a/conjure-macros/src/endpoints.rs b/conjure-macros/src/endpoints.rs index 72e1e19e..805d90fc 100644 --- a/conjure-macros/src/endpoints.rs +++ b/conjure-macros/src/endpoints.rs @@ -96,7 +96,12 @@ fn generate_endpoints(service: &Service) -> TokenStream { let endpoint_values = service.endpoints.iter().map(|e| { let name = endpoint_name(e); - quote!(conjure_http::private::Box::new(#name(self.0.clone()))) + quote! { + conjure_http::private::Box::new(#name { + handler: self.0.clone(), + runtime: runtime.clone(), + }) + } }); quote! { @@ -114,6 +119,7 @@ fn generate_endpoints(service: &Service) -> TokenStream { { fn endpoints( &self, + runtime: &conjure_http::private::Arc, ) -> conjure_http::private::Vec + Sync + Send, >> { @@ -210,7 +216,10 @@ fn generate_endpoint(service: &Service, endpoint: &Endpoint) -> TokenStream { let handler = generate_endpoint_handler(service, endpoint); quote! { - struct #name(conjure_http::private::Arc); + struct #name { + handler: conjure_http::private::Arc, + runtime: conjure_http::private::Arc, + } #metadata #handler @@ -371,7 +380,7 @@ fn generate_endpoint_handler(service: &Service, endpoint: &Endpoint) -> TokenStr #generate_query_params #generate_safe_params #(#generate_args)* - let #response = self.0.#method(#(#args),*) #await_ ?; + let #response = self.handler.#method(#(#args),*) #await_ ?; #generate_response } } @@ -428,7 +437,11 @@ fn generate_path_arg(parts: &TokenStream, arg: &Arg) -> TokenStream { |d| quote!(#d), ); quote! { - let #name = conjure_http::private::path_param::<_, #decoder>(&#parts, #param)?; + let #name = conjure_http::private::path_param::<_, #decoder>( + &self.runtime, + &#parts, + #param, + )?; } } @@ -441,7 +454,12 @@ fn generate_query_arg(query_params: &TokenStream, arg: &Arg) -> TokenS |d| quote!(#d), ); quote! { - let #name = conjure_http::private::query_param::<_, #decoder>(&#query_params, #key, #param)?; + let #name = conjure_http::private::query_param::<_, #decoder>( + &self.runtime, + &#query_params, + #key, + #param, + )?; } } @@ -454,7 +472,12 @@ fn generate_header_arg(parts: &TokenStream, arg: &Arg) -> TokenStream |d| quote!(#d), ); quote! { - let #name = conjure_http::private::header_param::<_, #decoder>(&#parts, #header, #param)?; + let #name = conjure_http::private::header_param::<_, #decoder>( + &self.runtime, + &#parts, + #header, + #param, + )?; } } @@ -480,9 +503,9 @@ fn generate_body_arg( arg: &Arg, ) -> TokenStream { let name = &arg.ident; - let trait_ = match service.asyncness { - Asyncness::Sync => quote!(DeserializeRequest), - Asyncness::Async => quote!(AsyncDeserializeRequest), + let function = match service.asyncness { + Asyncness::Sync => quote!(body_arg), + Asyncness::Async => quote!(async_body_arg), }; let deserializer = arg.params.deserializer.as_ref().map_or_else( || quote!(conjure_http::server::ConjureRequestDeserializer), @@ -493,7 +516,8 @@ fn generate_body_arg( Asyncness::Async => quote!(.await), }; quote! { - let #name = <#deserializer as conjure_http::server::#trait_<_, _>>::deserialize( + let #name = conjure_http::private::#function::<#deserializer, _, _>( + &self.runtime, &#parts.headers, #body, ) #await_ ?; @@ -517,9 +541,9 @@ fn generate_response( service: &Service, endpoint: &Endpoint, ) -> TokenStream { - let trait_ = match service.asyncness { - Asyncness::Sync => quote!(SerializeResponse), - Asyncness::Async => quote!(AsyncSerializeResponse), + let function = match service.asyncness { + Asyncness::Sync => quote!(response), + Asyncness::Async => quote!(async_response), }; let serializer = endpoint.params.produces.as_ref().map_or_else( || quote!(conjure_http::server::EmptyResponseSerializer), @@ -527,7 +551,8 @@ fn generate_response( ); quote! { - <#serializer as conjure_http::server::#trait_<_, _>>::serialize( + conjure_http::private::#function::<#serializer, _, _>( + &self.runtime, &#parts.headers, #response, ) diff --git a/conjure-test/src/test/servers.rs b/conjure-test/src/test/servers.rs index c1cc6692..bcbebb21 100644 --- a/conjure-test/src/test/servers.rs +++ b/conjure-test/src/test/servers.rs @@ -18,9 +18,9 @@ use crate::types::*; use async_trait::async_trait; use conjure_error::Error; use conjure_http::server::{ - AsyncResponseBody, AsyncService, AsyncWriteBody, ConjureResponseSerializer, DeserializeRequest, - FromStrOptionDecoder, FromStrSeqDecoder, RequestContext, ResponseBody, SerializeResponse, - Service, WriteBody, + AsyncResponseBody, AsyncService, AsyncWriteBody, ConjureResponseSerializer, ConjureRuntime, + DeserializeRequest, FromStrOptionDecoder, FromStrSeqDecoder, RequestContext, ResponseBody, + SerializeResponse, Service, WriteBody, }; use conjure_http::{PathParams, SafeParams}; use conjure_macros::{conjure_endpoints, endpoint}; @@ -33,6 +33,7 @@ use serde::Serialize; use std::collections::{BTreeMap, BTreeSet}; use std::io::Write; use std::pin::Pin; +use std::sync::Arc; macro_rules! test_service_handler { ($( @@ -246,7 +247,7 @@ where T: Service>, { fn send_sync(&self, name: &str) { - let endpoint = Service::endpoints(&self.service) + let endpoint = Service::endpoints(&self.service, &Arc::new(ConjureRuntime::new())) .into_iter() .find(|e| e.name() == name) .unwrap(); @@ -282,7 +283,7 @@ where T: AsyncService>, { async fn send_async(&self, name: &str) { - let endpoint = AsyncService::endpoints(&self.service) + let endpoint = AsyncService::endpoints(&self.service, &Arc::new(ConjureRuntime::new())) .into_iter() .find(|e| e.name() == name) .unwrap(); @@ -970,21 +971,29 @@ mock! { } } -enum RawRequestDeserializer {} +struct RawRequestDeserializer; -impl DeserializeRequest for RawRequestDeserializer { - fn deserialize(_: &HeaderMap, body: I) -> Result { +impl<'a, I> DeserializeRequest<'a, I, I> for RawRequestDeserializer { + fn new(_: &'a ConjureRuntime) -> Self { + Self + } + + fn deserialize(&self, _: &HeaderMap, body: I) -> Result { Ok(body) } } -enum RawResponseSerializer {} +struct RawResponseSerializer; -impl SerializeResponse for RawResponseSerializer +impl<'a, T, O> SerializeResponse<'a, T, O> for RawResponseSerializer where T: WriteBody + 'static + Send, { - fn serialize(_: &HeaderMap, value: T) -> Result>, Error> { + fn new(_: &'a ConjureRuntime) -> Self { + Self + } + + fn serialize(&self, _: &HeaderMap, value: T) -> Result>, Error> { Ok(Response::new(ResponseBody::Streaming(Box::new(value)))) } } diff --git a/example-api/src/another/test_service.rs b/example-api/src/another/test_service.rs index b5c79faf..fab098fa 100644 --- a/example-api/src/another/test_service.rs +++ b/example-api/src/another/test_service.rs @@ -1456,6 +1456,7 @@ where { fn endpoints( &self, + _: &conjure_http::private::Arc, ) -> Vec + Sync + Send>> { vec![ Box::new(GetFileSystemsEndpoint_(self.0.clone())), @@ -1490,6 +1491,7 @@ where { fn endpoints( &self, + _: &conjure_http::private::Arc, ) -> Vec + Sync + Send>> { vec![ Box::new(GetFileSystemsEndpoint_(self.0.clone())), From 0d4575e12895b09c890baee4ca490ef638724765 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 7 Jul 2023 14:36:02 -0400 Subject: [PATCH 2/3] fix docs --- conjure-macros/src/lib.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/conjure-macros/src/lib.rs b/conjure-macros/src/lib.rs index 4920e4a3..fa1f7fdf 100644 --- a/conjure-macros/src/lib.rs +++ b/conjure-macros/src/lib.rs @@ -232,8 +232,8 @@ pub fn conjure_client(attr: TokenStream, item: TokenStream) -> TokenStream { /// use conjure_error::Error; /// use conjure_http::{conjure_endpoints, endpoint}; /// use conjure_http::server::{ -/// ConjureResponseSerializer, DeserializeRequest, FromStrOptionDecoder, ResponseBody, -/// SerializeResponse, WriteBody, +/// ConjureResponseSerializer, ConjureRuntime, DeserializeRequest, FromStrOptionDecoder, +/// ResponseBody, SerializeResponse, WriteBody, /// }; /// use conjure_object::BearerToken; /// use http::Response; @@ -292,10 +292,14 @@ pub fn conjure_client(attr: TokenStream, item: TokenStream) -> TokenStream { /// fn stream_response(&self) -> Result; /// } /// -/// enum StreamingRequestDeserializer {} +/// struct StreamingRequestDeserializer; /// -/// impl DeserializeRequest for StreamingRequestDeserializer { -/// fn deserialize(_headers: &HeaderMap, body: I) -> Result { +/// impl<'a, I> DeserializeRequest<'a, I, I> for StreamingRequestDeserializer { +/// fn new(_: &'a ConjureRuntime) -> Self { +/// StreamingRequestDeserializer +/// } +/// +/// fn deserialize(&self, _headers: &HeaderMap, body: I) -> Result { /// Ok(body) /// } /// } @@ -312,13 +316,18 @@ pub fn conjure_client(attr: TokenStream, item: TokenStream) -> TokenStream { /// } /// } /// -/// enum StreamingResponseSerializer {} +/// struct StreamingResponseSerializer; /// -/// impl SerializeResponse for StreamingResponseSerializer +/// impl<'a, O> SerializeResponse<'a, StreamingResponse, O> for StreamingResponseSerializer /// where /// O: Write, /// { +/// fn new(_: &'a ConjureRuntime) -> Self { +/// StreamingResponseSerializer +/// } +/// /// fn serialize( +/// &self, /// _request_headers: &HeaderMap, /// body: StreamingResponse, /// ) -> Result>, Error> { From 437ca361ee3184195724b9e16e8088d12aca722f Mon Sep 17 00:00:00 2001 From: svc-changelog Date: Fri, 7 Jul 2023 19:09:02 +0000 Subject: [PATCH 3/3] Add generated changelog entries --- changelog/@unreleased/pr-258.v2.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 changelog/@unreleased/pr-258.v2.yml diff --git a/changelog/@unreleased/pr-258.v2.yml b/changelog/@unreleased/pr-258.v2.yml new file mode 100644 index 00000000..cdb02ae8 --- /dev/null +++ b/changelog/@unreleased/pr-258.v2.yml @@ -0,0 +1,6 @@ +type: break +break: + description: Added a new argument to Conjure endpoints to enable future runtime + behavior customization. + links: + - https://github.com/palantir/conjure-rust/pull/258