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}}