fbthrift/
help.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17// Functions only intended to be called from code generated by the Rust Thrift
18// code generator.
19
20use std::ffi::CStr;
21use std::fmt;
22use std::future::Future;
23use std::marker::PhantomData;
24use std::pin::Pin;
25
26use anyhow::bail;
27use anyhow::Result;
28use futures::future::FutureExt;
29
30use crate::serialize;
31use crate::ApplicationException;
32use crate::ContextStack;
33use crate::Deserialize;
34use crate::MessageType;
35use crate::Protocol;
36use crate::ProtocolEncodedFinal;
37use crate::ProtocolReader;
38use crate::ProtocolWriter;
39use crate::RequestContext;
40use crate::ResultInfo;
41use crate::ResultType;
42use crate::Serialize;
43use crate::SerializedMessage;
44
45// Note: `variants_by_number` must be sorted by the i32 values.
46pub fn enum_display(
47    variants_by_number: &[(&str, i32)],
48    formatter: &mut fmt::Formatter,
49    number: i32,
50) -> fmt::Result {
51    match variants_by_number.binary_search_by_key(&number, |entry| entry.1) {
52        Ok(i) => formatter.write_str(variants_by_number[i].0),
53        Err(_) => write!(formatter, "{}", number),
54    }
55}
56
57// Note: `variants_by_name` must be sorted by the string values.
58pub fn enum_from_str(
59    variants_by_name: &[(&str, i32)],
60    value: &str,
61    type_name: &'static str,
62) -> Result<i32> {
63    match variants_by_name.binary_search_by_key(&value, |entry| entry.0) {
64        Ok(i) => Ok(variants_by_name[i].1),
65        Err(_) => bail!("Unable to parse {} as {}", value, type_name),
66    }
67}
68
69pub fn type_name_of_val<T>(_: &T) -> &'static str {
70    std::any::type_name::<T>()
71}
72
73/// Serialize a result as encoded into a generated *Exn type, wrapped in an envelope.
74pub fn serialize_result_envelope<P, CTXT, RES>(
75    name: &str,
76    name_cstr: &<CTXT::ContextStack as ContextStack>::Name,
77    seqid: u32,
78    rctxt: &CTXT,
79    ctx_stack: &mut CTXT::ContextStack,
80    res: RES,
81) -> anyhow::Result<ProtocolEncodedFinal<P>>
82where
83    P: Protocol,
84    RES: ResultInfo + Serialize<P::Sizer> + Serialize<P::Serializer>,
85    CTXT: RequestContext<Name = CStr>,
86{
87    let res_type = res.result_type();
88
89    if matches!(res_type, ResultType::Error | ResultType::Exception) {
90        assert_eq!(res.exn_is_declared(), res_type == ResultType::Error);
91
92        rctxt.set_user_exception_header(res.exn_name(), &res.exn_value())?;
93    }
94
95    ctx_stack.pre_write()?;
96    let envelope = serialize!(P, |p| {
97        p.write_message_begin(name, res_type.message_type(), seqid);
98        res.write(p);
99        p.write_message_end();
100    });
101
102    ctx_stack.on_write_data(&SerializedMessage {
103        protocol: P::PROTOCOL_ID,
104        method_name: &name_cstr,
105        buffer: PhantomData,
106    })?;
107    ctx_stack.post_write(0)?;
108
109    Ok(envelope)
110}
111
112pub fn serialize_stream_item<P, RES>(res: RES) -> anyhow::Result<ProtocolEncodedFinal<P>>
113where
114    P: Protocol,
115    RES: ResultInfo + Serialize<P::Sizer> + Serialize<P::Serializer>,
116{
117    Ok(serialize!(P, |p| {
118        res.write(p);
119    }))
120}
121
122/// Serialize a request with envelope.
123pub fn serialize_request_envelope<P, ARGS>(
124    name: &str,
125    args: &ARGS,
126) -> anyhow::Result<ProtocolEncodedFinal<P>>
127where
128    P: Protocol,
129    ARGS: Serialize<P::Sizer> + Serialize<P::Serializer>,
130{
131    let envelope = serialize!(P, |p| {
132        // Note: we send a 0 message sequence ID from clients because
133        // this field should not be used by the server (except for some
134        // language implementations).
135        p.write_message_begin(name, MessageType::Call, 0);
136        args.write(p);
137        p.write_message_end();
138    });
139
140    Ok(envelope)
141}
142
143/// Deserialize a client response. This deserializes the envelope then
144/// deserializes either a reply or an ApplicationException.
145pub fn deserialize_response_envelope<P, T>(
146    de: &mut P::Deserializer,
147) -> anyhow::Result<Result<T, ApplicationException>>
148where
149    P: Protocol,
150    T: Deserialize<P::Deserializer>,
151{
152    let (_, message_type, _) = de.read_message_begin(|_| ())?;
153
154    let res = match message_type {
155        MessageType::Reply => Ok(T::read(de)?),
156        MessageType::Exception => Err(ApplicationException::read(de)?),
157        MessageType::Call | MessageType::Oneway | MessageType::InvalidMessageType => {
158            bail!("Unwanted message type `{:?}`", message_type)
159        }
160    };
161
162    de.read_message_end()?;
163
164    Ok(res)
165}
166
167/// Abstract spawning some potentially CPU-heavy work onto a CPU thread
168pub trait Spawner: 'static {
169    fn spawn<F, R>(func: F) -> Pin<Box<dyn Future<Output = R> + Send>>
170    where
171        F: FnOnce() -> R + Send + 'static,
172        R: Send + 'static;
173}
174
175/// No-op implementation of Spawner - just run on current thread
176pub struct NoopSpawner;
177impl Spawner for NoopSpawner {
178    #[inline]
179    fn spawn<F, R>(func: F) -> Pin<Box<dyn Future<Output = R> + Send>>
180    where
181        F: FnOnce() -> R + Send + 'static,
182        R: Send + 'static,
183    {
184        async { func() }.boxed()
185    }
186}
187
188pub async fn async_deserialize_response_envelope<P, T, S>(
189    de: P::Deserializer,
190) -> anyhow::Result<(Result<T, ApplicationException>, P::Deserializer)>
191where
192    P: Protocol,
193    P::Deserializer: Send,
194    T: Deserialize<P::Deserializer> + Send + 'static,
195    S: Spawner,
196{
197    S::spawn(move || {
198        let mut de = de;
199        let res = deserialize_response_envelope::<P, T>(&mut de);
200        res.map(|res| (res, de))
201    })
202    .await
203}