thrift/compiler/generate/templates/rust/lib/client.mustache (490 lines of code) (raw):

{{! Copyright (c) Facebook, Inc. and its affiliates. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. }} {{#service:interactions}}{{>lib/client}} {{/service:interactions}} pub struct {{service:name}}Impl<P, T, S = ::fbthrift::NoopSpawner> { {{#service:extends}} parent: {{service:package}}::client::{{service:name}}Impl<P, T, S>, {{/service:extends}} {{^service:extends}} transport: T, _phantom: ::std::marker::PhantomData<fn() -> (P, S)>, {{/service:extends}} } impl<P, T, S> {{service:name}}Impl<P, T, S> where P: ::fbthrift::Protocol, T: ::fbthrift::Transport, {{! require P::Frame and T to have compatible DecBuf and EncBuf::Final }} P::Frame: ::fbthrift::Framing<DecBuf = ::fbthrift::FramingDecoded<T>>, ::fbthrift::ProtocolEncoded<P>: ::fbthrift::BufMutExt<Final = ::fbthrift::FramingEncodedFinal<T>>, P::Deserializer: ::std::marker::Send, S: ::fbthrift::help::Spawner, { pub fn new( transport: T, ) -> Self { {{#service:extends}} let parent = {{service:package}}::client::{{service:name}}Impl::<P, T, S>::new(transport); Self { parent } {{/service:extends}} {{^service:extends}} Self { transport, _phantom: ::std::marker::PhantomData, } {{/service:extends}} } pub fn transport(&self) -> &T { {{#service:extends}} self.parent.transport() {{/service:extends}} {{^service:extends}} &self.transport {{/service:extends}} } {{#service:rustFunctions}} {{^function:starts_interaction?}}{{^function:returns_streams?}} fn _{{function:rust_name}}_impl( &self,{{! }}{{#function:args}} arg_{{field:name}}: {{>lib/arg}},{{! }}{{/function:args}} rpc_options: T::RpcOptions, ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<{{! }}{{#function:return_type}}{{>lib/type}}{{/function:return_type}}, {{! }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error{{! }}>> + ::std::marker::Send + 'static>> { use ::const_cstr::const_cstr; use ::tracing::Instrument as _; use ::futures::FutureExt as _; const_cstr! { SERVICE_NAME = "{{service:name}}"; METHOD_NAME = "{{service:name}}.{{function:name}}"; } let args = self::Args_{{service:name}}_{{function:name}} { {{#function:args}} {{field:rust_name}}: arg_{{field:name}}, {{/function:args}} _phantom: ::std::marker::PhantomData, }; // need to do call setup outside of async block because T: Transport isn't Send let request_env = match ::fbthrift::help::serialize_request_envelope::<P, _>("{{#service:interaction?}}{{service:name}}.{{/service:interaction?}}{{function:name}}", &args) { ::std::result::Result::Ok(res) => res, ::std::result::Result::Err(err) => return ::futures::future::err(err.into()).boxed(), }; let call = self.transport() .call(SERVICE_NAME.as_cstr(), METHOD_NAME.as_cstr(), request_env, rpc_options) .instrument(::tracing::trace_span!("call", function = "{{service:name}}.{{function:name}}")); async move { let reply_env = call.await?; let de = P::deserializer(reply_env); let (res, _de): (::std::result::Result<{{program:crate}}::services::{{service:snake}}::{{function:upcamel}}Exn, _>, _) = ::fbthrift::help::async_deserialize_response_envelope::<P, _, S>(de).await?; match res { ::std::result::Result::Ok(exn) => ::std::convert::From::from(exn), ::std::result::Result::Err(aexn) => ::std::result::Result::Err({{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error::ApplicationException(aexn)) } } .instrument(::tracing::info_span!("{{service:name}}.{{function:name}}")) .boxed() } {{/function:returns_streams?}}{{#function:returns_streams?}} fn _{{function:rust_name}}_impl( &self,{{! }}{{#function:args}} arg_{{field:name}}: {{>lib/arg}},{{! }}{{/function:args}} rpc_options: T::RpcOptions, ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<{{! }}{{#function:return_type}}{{>lib/type}}{{/function:return_type}}, {{! }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error{{! }}>> + ::std::marker::Send + 'static>> { use ::const_cstr::const_cstr; use ::futures::future::FutureExt as _; use ::tracing::Instrument as _; use ::futures::StreamExt as _; use ::fbthrift::Deserialize as _; const_cstr! { SERVICE_NAME = "{{service:name}}"; METHOD_NAME = "{{service:name}}.{{function:name}}"; } let args = self::Args_{{service:name}}_{{function:name}} { {{#function:args}} {{field:rust_name}}: arg_{{field:name}}, {{/function:args}} _phantom: ::std::marker::PhantomData, }; let request_env = match ::fbthrift::help::serialize_request_envelope::<P, _>("{{#service:interaction?}}{{service:name}}.{{/service:interaction?}}{{function:name}}", &args) { ::std::result::Result::Ok(res) => res, ::std::result::Result::Err(err) => return ::futures::future::err(err.into()).boxed(), }; let call_stream = self.transport() .call_stream(SERVICE_NAME.as_cstr(), METHOD_NAME.as_cstr(), request_env, rpc_options) .instrument(::tracing::trace_span!("call_stream", method = "{{service:name}}.{{function:name}}")); async move { let (_initial, stream) = call_stream.await?; let new_stream = stream.then(|item_res| { async move { match item_res { ::std::result::Result::Err(err) => ::std::result::Result::Err({{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}StreamError::from(err)), ::std::result::Result::Ok(item_enc) => { let res = S::spawn(move || { let mut de = P::deserializer(item_enc); {{program:crate}}::services::{{service:snake}}::{{function:upcamel}}StreamExn::read(&mut de) }).await?; let item: ::std::result::Result<{{! }}{{>lib/function_stream_elem_type}}, {{! }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}StreamError> = ::std::convert::From::from(res); item } } } }) .boxed(); {{#function:stream_has_first_response?}} let de = P::deserializer(_initial); let res: {{program:crate}}::services::{{service:snake}}::{{function:upcamel}}Exn = ::fbthrift::help::async_deserialize_response_envelope::<P, _, S>(de).await?.0?; let initial: ::std::result::Result<{{>lib/function_stream_first_response_type}}, {{! }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error> = ::std::convert::From::from(res); initial.map(move |initial| (initial, new_stream)) {{/function:stream_has_first_response?}} {{^function:stream_has_first_response?}} ::std::result::Result::Ok(new_stream) {{/function:stream_has_first_response?}} } .instrument(::tracing::info_span!("{{service:name}}.{{function:name}}")) .boxed() } {{/function:returns_streams?}}{{/function:starts_interaction?}} {{/service:rustFunctions}} } {{#service:extendedServices}} impl<P, T, S> {{! }}{{#extendedService:service}}{{! }}::std::convert::AsRef<dyn {{extendedService:packagePrefix}}::client::{{service:name}} + 'static> {{! }}{{/extendedService:service}}{{! }}for {{service:name}}Impl<P, T, S> where P: ::fbthrift::Protocol, T: ::fbthrift::Transport, {{! require P::Frame and T to have compatible DecBuf and EncBuf::Final }} P::Frame: ::fbthrift::Framing<DecBuf = ::fbthrift::FramingDecoded<T>>, ::fbthrift::ProtocolEncoded<P>: ::fbthrift::BufMutExt<Final = ::fbthrift::FramingEncodedFinal<T>>, P::Deserializer: ::std::marker::Send, S: ::fbthrift::help::Spawner, { fn as_ref(&self) -> &(dyn {{#extendedService:service}}{{! }}{{extendedService:packagePrefix}}::client::{{service:name}}{{! }}{{/extendedService:service}}{{! }} + 'static) { {{extendedService:asRefImpl}} } } {{/service:extendedServices}} {{#service:docs?}} #[doc = {{service:docs}}] {{/service:docs?}} pub trait {{service:name}}: {{! }}{{#service:extends}}{{service:package}}::client::{{service:name}} + {{/service:extends}}{{! }}::std::marker::Send {{>lib/block}}{{! }}{{#service:rustFunctions}} {{#function:docs?}} #[doc = {{function:docs}}] {{/function:docs?}} {{^function:starts_interaction?}} fn {{function:rust_name}}( &self, {{#function:args}} arg_{{field:name}}: {{>lib/arg}}, {{/function:args}} ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<{{! }}{{#function:return_type}}{{>lib/type}}{{/function:return_type}}, {{! }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error{{! }}>> + ::std::marker::Send + 'static>>; {{/function:starts_interaction?}}{{#function:starts_interaction?}} fn {{function:rust_name}}( &self, ) -> ::std::result::Result<{{! }}::std::sync::Arc<dyn {{function:interaction_name}} + ::std::marker::Send + 'static>, {{! }}::anyhow::Error>; {{/function:starts_interaction?}}{{/service:rustFunctions}} } pub trait {{service:name}}Ext<T>: {{service:name}} where T: ::fbthrift::Transport, {{>lib/block}} {{#service:rustFunctions}} {{#function:docs?}} #[doc = {{function:docs}}] {{/function:docs?}} {{^function:starts_interaction?}} fn {{function:rust_name}}_with_rpc_opts( &self, {{#function:args}} arg_{{field:name}}: {{>lib/arg}}, {{/function:args}} rpc_options: T::RpcOptions, ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<{{! }}{{#function:return_type}}{{>lib/type}}{{/function:return_type}}, {{! }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error{{! }}>> + ::std::marker::Send + 'static>>; {{/function:starts_interaction?}} {{/service:rustFunctions}} } {{#service:rustFunctions}}{{^function:starts_interaction?}} struct Args_{{service:name}}_{{function:name}}<'a> { {{#function:args}} {{field:rust_name}}: {{>lib/arg_life}}, {{/function:args}} _phantom: ::std::marker::PhantomData<&'a ()>, } impl<'a, P: ::fbthrift::ProtocolWriter> ::fbthrift::Serialize<P> for self::Args_{{service:name}}_{{function:name}}<'a> { #[inline]{{! No cost because there's only one caller; with luck will mitigate move cost of args. }} #[::tracing::instrument(skip_all, level = "trace", name = "serialize_args", fields(method = "{{service:name}}.{{function:name}}"))] fn write(&self, p: &mut P) { p.write_struct_begin("args");{{! }}{{#function:args}} p.write_field_begin({{! }}"{{field:name}}", {{! }}{{#field:type}}{{>lib/ttype}}{{/field:type}}, {{! }}{{field:key}}i16{{! }}); {{#field:type}}{{>lib/write}}{{/field:type}}(&self.{{field:rust_name}}, p); p.write_field_end();{{! }}{{/function:args}} p.write_field_stop(); p.write_struct_end(); } } {{/function:starts_interaction?}}{{/service:rustFunctions}} impl<P, T, S> {{service:name}} for {{service:name}}Impl<P, T, S> where P: ::fbthrift::Protocol, T: ::fbthrift::Transport, {{! require P::Frame and T to have compatible DecBuf and EncBuf::Final }} P::Frame: ::fbthrift::Framing<DecBuf = ::fbthrift::FramingDecoded<T>>, ::fbthrift::ProtocolEncoded<P>: ::fbthrift::BufMutExt<Final = ::fbthrift::FramingEncodedFinal<T>>, P::Deserializer: ::std::marker::Send, S: ::fbthrift::help::Spawner, {{>lib/block}} {{#service:rustFunctions}} {{^function:starts_interaction?}} fn {{function:rust_name}}( &self,{{! }}{{#function:args}} arg_{{field:name}}: {{>lib/arg}},{{! }}{{/function:args}} ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<{{! }}{{#function:return_type}}{{>lib/type}}{{/function:return_type}}, {{! }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error{{! }}>> + ::std::marker::Send + 'static>> { let rpc_options = T::RpcOptions::default(); self._{{function:rust_name}}_impl( {{#function:args}} arg_{{field:name}}, {{/function:args}} rpc_options, ) } {{/function:starts_interaction?}}{{#function:starts_interaction?}} fn {{function:name}}( &self, ) -> ::std::result::Result<{{! }}::std::sync::Arc<dyn {{function:interaction_name}} + ::std::marker::Send + 'static>, {{! }}::anyhow::Error> { use ::const_cstr::const_cstr; const_cstr! { INTERACTION_NAME = "{{function:interaction_name}}"; } Ok( ::std::sync::Arc::new( {{function:interaction_name}}Impl::<P, T, S>::new( self.transport().create_interaction(INTERACTION_NAME.as_cstr())? ) ) ) } {{/function:starts_interaction?}}{{/service:rustFunctions}} } impl<P, T, S> {{service:name}}Ext<T> for {{service:name}}Impl<P, T, S> where P: ::fbthrift::Protocol, T: ::fbthrift::Transport, {{! require P::Frame and T to have compatible DecBuf and EncBuf::Final }} P::Frame: ::fbthrift::Framing<DecBuf = ::fbthrift::FramingDecoded<T>>, ::fbthrift::ProtocolEncoded<P>: ::fbthrift::BufMutExt<Final = ::fbthrift::FramingEncodedFinal<T>>, P::Deserializer: ::std::marker::Send, S: ::fbthrift::help::Spawner, {{>lib/block}} {{#service:rustFunctions}} {{^function:starts_interaction?}} fn {{function:rust_name}}_with_rpc_opts( &self,{{! }}{{#function:args}} arg_{{field:name}}: {{>lib/arg}},{{! }}{{/function:args}} rpc_options: T::RpcOptions, ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<{{! }}{{#function:return_type}}{{>lib/type}}{{/function:return_type}}, {{! }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error{{! }}>> + ::std::marker::Send + 'static>> { self._{{function:rust_name}}_impl( {{#function:args}} arg_{{field:name}}, {{/function:args}} rpc_options, ) } {{/function:starts_interaction?}} {{/service:rustFunctions}} } impl<'a, T> {{service:name}} for T where T: ::std::convert::AsRef<dyn {{service:name}} + 'a>, {{#service:extendedServices}} {{#extendedService:service}} T: {{extendedService:packagePrefix}}::client::{{service:name}}, {{/extendedService:service}} {{/service:extendedServices}} T: ::std::marker::Send, { {{#service:rustFunctions}} {{^function:starts_interaction?}} fn {{function:rust_name}}( &self,{{! }}{{#function:args}} arg_{{field:name}}: {{>lib/arg}},{{! }}{{/function:args}} ) -> ::std::pin::Pin<::std::boxed::Box<dyn ::std::future::Future<Output = ::std::result::Result<{{! }}{{#function:return_type}}{{>lib/type}}{{/function:return_type}}, {{! }}{{program:crate}}::errors::{{service:snake}}::{{function:upcamel}}Error{{! }}>> + ::std::marker::Send + 'static>> { self.as_ref().{{function:rust_name}}( {{#function:args}} arg_{{field:name}}, {{/function:args}} ) } {{/function:starts_interaction?}}{{#function:starts_interaction?}} fn {{function:rust_name}}( &self, ) -> ::std::result::Result<{{! }}::std::sync::Arc<dyn {{function:interaction_name}} + ::std::marker::Send + 'static>, {{! }}::anyhow::Error> { self.as_ref().{{function:rust_name}}() } {{/function:starts_interaction?}}{{/service:rustFunctions}} } #[derive(Clone)] pub struct make_{{service:name}}; /// To be called by user directly setting up a client. Avoids /// needing ClientFactory trait in scope, avoids unidiomatic /// make_Trait name. /// /// ``` /// # const _: &str = stringify! { /// use bgs::client::BuckGraphService; /// /// let protocol = BinaryProtocol::new(); /// let transport = HttpClient::new(); /// let client = <dyn BuckGraphService>::new(protocol, transport); /// # }; /// ``` impl dyn {{service:name}} { {{#service:annotations}} {{#annotation:value?}} pub const {{annotation:rust_name}}: &'static ::std::primitive::str = {{annotation:rust_value}}; {{/annotation:value?}} {{/service:annotations}} pub fn new<P, T>( protocol: P, transport: T, ) -> ::std::sync::Arc<impl {{service:name}} + ::std::marker::Send + 'static> where P: ::fbthrift::Protocol<Frame = T>, T: ::fbthrift::Transport, P::Deserializer: ::std::marker::Send, { let spawner = ::fbthrift::help::NoopSpawner; Self::with_spawner(protocol, transport, spawner) } pub fn with_spawner<P, T, S>( protocol: P, transport: T, spawner: S, ) -> ::std::sync::Arc<impl {{service:name}} + ::std::marker::Send + 'static> where P: ::fbthrift::Protocol<Frame = T>, T: ::fbthrift::Transport, P::Deserializer: ::std::marker::Send, S: ::fbthrift::help::Spawner, { let _ = protocol; let _ = spawner; ::std::sync::Arc::new({{service:name}}Impl::<P, T, S>::new(transport)) } } impl<T> dyn {{service:name}}Ext<T> where T: ::fbthrift::Transport, { pub fn new<P>( protocol: P, transport: T, ) -> ::std::sync::Arc<impl {{service:name}}Ext<T> + ::std::marker::Send + 'static> where P: ::fbthrift::Protocol<Frame = T>, P::Deserializer: ::std::marker::Send, { let spawner = ::fbthrift::help::NoopSpawner; Self::with_spawner(protocol, transport, spawner) } pub fn with_spawner<P, S>( protocol: P, transport: T, spawner: S, ) -> ::std::sync::Arc<impl {{service:name}}Ext<T> + ::std::marker::Send + 'static> where P: ::fbthrift::Protocol<Frame = T>, P::Deserializer: ::std::marker::Send, S: ::fbthrift::help::Spawner, { let _ = protocol; let _ = spawner; ::std::sync::Arc::new({{service:name}}Impl::<P, T, S>::new(transport)) } } pub type {{service:name}}DynClient = <make_{{service:name}} as ::fbthrift::ClientFactory>::Api; pub type {{service:name}}Client = ::std::sync::Arc<{{service:name}}DynClient>; /// The same thing, but to be called from generic contexts where we are /// working with a type parameter `C: ClientFactory` to produce clients. impl ::fbthrift::ClientFactory for make_{{service:name}} { type Api = dyn {{service:name}} + ::std::marker::Send + ::std::marker::Sync + 'static; fn with_spawner<P, T, S>(protocol: P, transport: T, spawner: S) -> ::std::sync::Arc<Self::Api> where P: ::fbthrift::Protocol<Frame = T>, T: ::fbthrift::Transport + ::std::marker::Sync, P::Deserializer: ::std::marker::Send, S: ::fbthrift::help::Spawner, { <dyn {{service:name}}>::with_spawner(protocol, transport, spawner) } } {{!newline}}