fbthrift_git/
application_exception.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
17use std::any::Any;
18
19use crate::deserialize::Deserialize;
20use crate::exceptions::ExceptionInfo;
21use crate::exceptions::ResultInfo;
22use crate::exceptions::ResultType;
23use crate::protocol::Field;
24use crate::protocol::ProtocolReader;
25use crate::protocol::ProtocolWriter;
26use crate::serialize::Serialize;
27use crate::thrift_protocol::ProtocolID;
28use crate::ttype::TType;
29use crate::Result;
30
31// Reference is thrift/lib/cpp/TApplicationException.h
32#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
33#[repr(i32)]
34pub enum ApplicationExceptionErrorCode {
35    #[default]
36    Unknown = 0,
37    UnknownMethod = 1,
38    InvalidMessageType = 2,
39    WrongMethodName = 3,
40    BadSequenceID = 4,
41    MissingResult = 5,
42    InternalError = 6,
43    ProtocolError = 7,
44    InvalidTransform = 8,
45    InvalidProtocol = 9,
46    UnsupportedClientType = 10,
47    Loadshedding = 11,
48    Timeout = 12,
49    InjectedFailure = 13,
50    ChecksumMismatch = 14,
51    Interruption = 15,
52}
53
54const TAPPLICATION_EXCEPTION_ERROR_CODE: &str = "ApplicationExceptionErrorCode";
55
56#[derive(
57    Debug,
58    Clone,
59    Eq,
60    PartialEq,
61    Ord,
62    PartialOrd,
63    Hash,
64    Default,
65    thiserror::Error
66)]
67#[error("{type_:?}: {message}")]
68pub struct ApplicationException {
69    pub message: String,
70    pub type_: ApplicationExceptionErrorCode,
71}
72
73impl ApplicationException {
74    #[cold]
75    pub fn new(type_: ApplicationExceptionErrorCode, message: String) -> Self {
76        ApplicationException { message, type_ }
77    }
78
79    // Used for methods which should exist but don't
80    #[cold]
81    pub fn unimplemented_method(handler: &str, method: &str) -> Self {
82        ApplicationException {
83            type_: ApplicationExceptionErrorCode::UnknownMethod,
84            message: format!("Method {} not implemented for {}", method, handler),
85        }
86    }
87
88    /// Indicator that this service doesn't have the method, but another might
89    #[inline]
90    pub fn unknown_method() -> Self {
91        ApplicationException {
92            type_: ApplicationExceptionErrorCode::UnknownMethod,
93            message: String::new(), // no allocation
94        }
95    }
96
97    #[cold]
98    pub fn missing_arg(method: &str, arg: &str) -> Self {
99        ApplicationException {
100            type_: ApplicationExceptionErrorCode::ProtocolError,
101            message: format!("{} missing arg {}", method, arg),
102        }
103    }
104
105    #[cold]
106    pub fn missing_field(method: &str, field: &str) -> Self {
107        ApplicationException {
108            type_: ApplicationExceptionErrorCode::ProtocolError,
109            message: format!("Struct {} missing field {}", method, field),
110        }
111    }
112
113    #[cold]
114    pub fn invalid_protocol(badproto: ProtocolID) -> Self {
115        ApplicationException {
116            type_: ApplicationExceptionErrorCode::InvalidProtocol,
117            message: format!("Invalid protocol {:?}", badproto),
118        }
119    }
120
121    /// An undeclared "exception"/panic etc
122    #[cold]
123    pub fn handler_panic(method: &str, exn: Box<dyn Any + Send + 'static>) -> Self {
124        ApplicationException {
125            type_: ApplicationExceptionErrorCode::Unknown,
126            message: format!(
127                "Handler for `{}` panicked with: {}",
128                method,
129                panic_message::get_panic_message(&exn).unwrap_or("<panic>")
130            ),
131        }
132    }
133}
134
135impl ExceptionInfo for ApplicationException {
136    #[rustfmt::skip]
137    fn exn_name(&self) -> &'static str {
138        match self.type_ {
139            ApplicationExceptionErrorCode::Unknown => "ApplicationExceptionErrorCode::Unknown",
140            ApplicationExceptionErrorCode::UnknownMethod => "ApplicationExceptionErrorCode::UnknownMethod",
141            ApplicationExceptionErrorCode::InvalidMessageType => "ApplicationExceptionErrorCode::InvalidMessageType",
142            ApplicationExceptionErrorCode::WrongMethodName => "ApplicationExceptionErrorCode::WrongMethodName",
143            ApplicationExceptionErrorCode::BadSequenceID => "ApplicationExceptionErrorCode::BadSequenceID",
144            ApplicationExceptionErrorCode::MissingResult => "ApplicationExceptionErrorCode::MissingResult",
145            ApplicationExceptionErrorCode::InternalError => "ApplicationExceptionErrorCode::InternalError",
146            ApplicationExceptionErrorCode::ProtocolError => "ApplicationExceptionErrorCode::ProtocolError",
147            ApplicationExceptionErrorCode::InvalidTransform => "ApplicationExceptionErrorCode::InvalidTransform",
148            ApplicationExceptionErrorCode::InvalidProtocol => "ApplicationExceptionErrorCode::InvalidProtocol",
149            ApplicationExceptionErrorCode::UnsupportedClientType => "ApplicationExceptionErrorCode::UnsupportedClientType",
150            ApplicationExceptionErrorCode::Loadshedding => "ApplicationExceptionErrorCode::Loadshedding",
151            ApplicationExceptionErrorCode::Timeout => "ApplicationExceptionErrorCode::Timeout",
152            ApplicationExceptionErrorCode::InjectedFailure => "ApplicationExceptionErrorCode::InjectedFailure",
153            ApplicationExceptionErrorCode::ChecksumMismatch => "ApplicationExceptionErrorCode::ChecksumMismatch",
154            ApplicationExceptionErrorCode::Interruption => "ApplicationExceptionErrorCode::Interruption",
155        }
156    }
157
158    fn exn_value(&self) -> String {
159        self.message.clone()
160    }
161
162    #[inline]
163    fn exn_is_declared(&self) -> bool {
164        false
165    }
166}
167
168impl ResultInfo for ApplicationException {
169    fn result_type(&self) -> ResultType {
170        ResultType::Exception
171    }
172}
173
174impl<P> Deserialize<P> for ApplicationException
175where
176    P: ProtocolReader,
177{
178    /// Decodes a ApplicationException message from a Protocol stream
179    fn read(iprot: &mut P) -> Result<Self> {
180        iprot.read_struct_begin(|_| ())?;
181
182        let mut message = String::from("");
183        let mut type_ = ApplicationExceptionErrorCode::Unknown;
184
185        loop {
186            // Start reading the next field
187            static FIELDS: &[Field] = &[
188                Field::new("message", TType::String, 1),
189                Field::new("type", TType::I32, 2),
190            ];
191            let (_, ttype, id) = iprot.read_field_begin(|_| (), FIELDS)?;
192
193            match (ttype, id) {
194                (TType::Stop, _) => break,
195                (TType::String, 1) => message = iprot.read_string()?,
196                (TType::I32, 2) => {
197                    type_ = match iprot.read_i32()? {
198                        1 => ApplicationExceptionErrorCode::UnknownMethod,
199                        2 => ApplicationExceptionErrorCode::InvalidMessageType,
200                        3 => ApplicationExceptionErrorCode::WrongMethodName,
201                        4 => ApplicationExceptionErrorCode::BadSequenceID,
202                        5 => ApplicationExceptionErrorCode::MissingResult,
203                        6 => ApplicationExceptionErrorCode::InternalError,
204                        7 => ApplicationExceptionErrorCode::ProtocolError,
205                        8 => ApplicationExceptionErrorCode::InvalidTransform,
206                        9 => ApplicationExceptionErrorCode::InvalidProtocol,
207                        10 => ApplicationExceptionErrorCode::UnsupportedClientType,
208                        11 => ApplicationExceptionErrorCode::Loadshedding,
209                        12 => ApplicationExceptionErrorCode::Timeout,
210                        13 => ApplicationExceptionErrorCode::InjectedFailure,
211                        14 => ApplicationExceptionErrorCode::ChecksumMismatch,
212                        15 => ApplicationExceptionErrorCode::Interruption,
213
214                        _ => ApplicationExceptionErrorCode::Unknown,
215                    }
216                }
217                (ttype, _) => iprot.skip(ttype)?,
218            };
219
220            // Finished reading the end of the field
221            iprot.read_field_end()?;
222        }
223        iprot.read_struct_end()?;
224        Ok(ApplicationException::new(type_, message))
225    }
226}
227
228impl<P> Serialize<P> for ApplicationException
229where
230    P: ProtocolWriter,
231{
232    /// Writes an application exception to the Protocol stream
233    fn write(&self, oprot: &mut P) {
234        oprot.write_struct_begin(TAPPLICATION_EXCEPTION_ERROR_CODE);
235
236        if !self.message.is_empty() {
237            oprot.write_field_begin("message", TType::String, 1);
238            oprot.write_string(&self.message);
239            oprot.write_field_end();
240        }
241        oprot.write_field_begin("type", TType::I32, 2);
242        oprot.write_i32(self.type_ as i32);
243        oprot.write_field_end();
244        oprot.write_field_stop();
245        oprot.write_struct_end();
246    }
247}