THRIFT-2945 Add Rust support
Client: Rust
Patch: Allen George <allen.george@gmail.com>

This closes #1147
diff --git a/lib/rs/src/errors.rs b/lib/rs/src/errors.rs
new file mode 100644
index 0000000..a6049d5
--- /dev/null
+++ b/lib/rs/src/errors.rs
@@ -0,0 +1,678 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.
+
+use std::convert::{From, Into};
+use std::error::Error as StdError;
+use std::fmt::{Debug, Display, Formatter};
+use std::{error, fmt, io, string};
+use try_from::TryFrom;
+
+use ::protocol::{TFieldIdentifier, TInputProtocol, TOutputProtocol, TStructIdentifier, TType};
+
+// FIXME: should all my error structs impl error::Error as well?
+// FIXME: should all fields in TransportError, ProtocolError and ApplicationError be optional?
+
+/// Error type returned by all runtime library functions.
+///
+/// `thrift::Error` is used throughout this crate as well as in auto-generated
+/// Rust code. It consists of four variants defined by convention across Thrift
+/// implementations:
+///
+/// 1. `Transport`: errors encountered while operating on I/O channels
+/// 2. `Protocol`: errors encountered during runtime-library processing
+/// 3. `Application`: errors encountered within auto-generated code
+/// 4. `User`: IDL-defined exception structs
+///
+/// The `Application` variant also functions as a catch-all: all handler errors
+/// are automatically turned into application errors.
+///
+/// All error variants except `Error::User` take an eponymous struct with two
+/// required fields:
+///
+/// 1. `kind`: variant-specific enum identifying the error sub-type
+/// 2. `message`: human-readable error info string
+///
+/// `kind` is defined by convention while `message` is freeform. If none of the
+/// enumerated kinds are suitable use `Unknown`.
+///
+/// To simplify error creation convenience constructors are defined for all
+/// variants, and conversions from their structs (`thrift::TransportError`,
+/// `thrift::ProtocolError` and `thrift::ApplicationError` into `thrift::Error`.
+///
+/// # Examples
+///
+/// Create a `TransportError`.
+///
+/// ```
+/// use thrift;
+/// use thrift::{TransportError, TransportErrorKind};
+///
+/// // explicit
+/// let err0: thrift::Result<()> = Err(
+///   thrift::Error::Transport(
+///     TransportError {
+///       kind: TransportErrorKind::TimedOut,
+///       message: format!("connection to server timed out")
+///     }
+///   )
+/// );
+///
+/// // use conversion
+/// let err1: thrift::Result<()> = Err(
+///   thrift::Error::from(
+///     TransportError {
+///       kind: TransportErrorKind::TimedOut,
+///       message: format!("connection to server timed out")
+///     }
+///   )
+/// );
+///
+/// // use struct constructor
+/// let err2: thrift::Result<()> = Err(
+///   thrift::Error::Transport(
+///     TransportError::new(
+///       TransportErrorKind::TimedOut,
+///       "connection to server timed out"
+///     )
+///   )
+/// );
+///
+///
+/// // use error variant constructor
+/// let err3: thrift::Result<()> = Err(
+///   thrift::new_transport_error(
+///     TransportErrorKind::TimedOut,
+///     "connection to server timed out"
+///   )
+/// );
+/// ```
+///
+/// Create an error from a string.
+///
+/// ```
+/// use thrift;
+/// use thrift::{ApplicationError, ApplicationErrorKind};
+///
+/// // we just use `From::from` to convert a `String` into a `thrift::Error`
+/// let err0: thrift::Result<()> = Err(
+///   thrift::Error::from("This is an error")
+/// );
+///
+/// // err0 is equivalent to...
+/// let err1: thrift::Result<()> = Err(
+///   thrift::Error::Application(
+///     ApplicationError {
+///       kind: ApplicationErrorKind::Unknown,
+///       message: format!("This is an error")
+///     }
+///   )
+/// );
+/// ```
+///
+/// Return an IDL-defined exception.
+///
+/// ```text
+/// // Thrift IDL exception definition.
+/// exception Xception {
+///   1: i32 errorCode,
+///   2: string message
+/// }
+/// ```
+///
+/// ```
+/// use std::convert::From;
+/// use std::error::Error;
+/// use std::fmt;
+/// use std::fmt::{Display, Formatter};
+///
+/// // auto-generated by the Thrift compiler
+/// #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+/// pub struct Xception {
+///   pub error_code: Option<i32>,
+///   pub message: Option<String>,
+/// }
+///
+/// // auto-generated by the Thrift compiler
+/// impl Error for Xception {
+///   fn description(&self) -> &str {
+///     "remote service threw Xception"
+///   }
+/// }
+///
+/// // auto-generated by the Thrift compiler
+/// impl From<Xception> for thrift::Error {
+///   fn from(e: Xception) -> Self {
+///     thrift::Error::User(Box::new(e))
+///   }
+/// }
+///
+/// // auto-generated by the Thrift compiler
+/// impl Display for Xception {
+///   fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+///     self.description().fmt(f)
+///   }
+/// }
+///
+/// // in user code...
+/// let err: thrift::Result<()> = Err(
+///   thrift::Error::from(Xception { error_code: Some(1), message: None })
+/// );
+/// ```
+pub enum Error {
+    /// Errors encountered while operating on I/O channels.
+    ///
+    /// These include *connection closed* and *bind failure*.
+    Transport(TransportError),
+    /// Errors encountered during runtime-library processing.
+    ///
+    /// These include *message too large* and *unsupported protocol version*.
+    Protocol(ProtocolError),
+    /// Errors encountered within auto-generated code, or when incoming
+    /// or outgoing messages violate the Thrift spec.
+    ///
+    /// These include *out-of-order messages* and *missing required struct
+    /// fields*.
+    ///
+    /// This variant also functions as a catch-all: errors from handler
+    /// functions are automatically returned as an `ApplicationError`.
+    Application(ApplicationError),
+    /// IDL-defined exception structs.
+    User(Box<error::Error + Sync + Send>),
+}
+
+impl Error {
+    /// Create an `ApplicationError` from its wire representation.
+    ///
+    /// Application code **should never** call this method directly.
+    pub fn read_application_error_from_in_protocol(i: &mut TInputProtocol)
+                                                   -> ::Result<ApplicationError> {
+        let mut message = "general remote error".to_owned();
+        let mut kind = ApplicationErrorKind::Unknown;
+
+        i.read_struct_begin()?;
+
+        loop {
+            let field_ident = i.read_field_begin()?;
+
+            if field_ident.field_type == TType::Stop {
+                break;
+            }
+
+            let id = field_ident.id.expect("sender should always specify id for non-STOP field");
+
+            match id {
+                1 => {
+                    let remote_message = i.read_string()?;
+                    i.read_field_end()?;
+                    message = remote_message;
+                }
+                2 => {
+                    let remote_type_as_int = i.read_i32()?;
+                    let remote_kind: ApplicationErrorKind = TryFrom::try_from(remote_type_as_int)
+                        .unwrap_or(ApplicationErrorKind::Unknown);
+                    i.read_field_end()?;
+                    kind = remote_kind;
+                }
+                _ => {
+                    i.skip(field_ident.field_type)?;
+                }
+            }
+        }
+
+        i.read_struct_end()?;
+
+        Ok(ApplicationError {
+            kind: kind,
+            message: message,
+        })
+    }
+
+    /// Convert an `ApplicationError` into its wire representation and write
+    /// it to the remote.
+    ///
+    /// Application code **should never** call this method directly.
+    pub fn write_application_error_to_out_protocol(e: &ApplicationError,
+                                                   o: &mut TOutputProtocol)
+                                                   -> ::Result<()> {
+        o.write_struct_begin(&TStructIdentifier { name: "TApplicationException".to_owned() })?;
+
+        let message_field = TFieldIdentifier::new("message", TType::String, 1);
+        let type_field = TFieldIdentifier::new("type", TType::I32, 2);
+
+        o.write_field_begin(&message_field)?;
+        o.write_string(&e.message)?;
+        o.write_field_end()?;
+
+        o.write_field_begin(&type_field)?;
+        o.write_i32(e.kind as i32)?;
+        o.write_field_end()?;
+
+        o.write_field_stop()?;
+        o.write_struct_end()?;
+
+        o.flush()
+    }
+}
+
+impl error::Error for Error {
+    fn description(&self) -> &str {
+        match *self {
+            Error::Transport(ref e) => TransportError::description(e),
+            Error::Protocol(ref e) => ProtocolError::description(e),
+            Error::Application(ref e) => ApplicationError::description(e),
+            Error::User(ref e) => e.description(),
+        }
+    }
+}
+
+impl Debug for Error {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        match *self {
+            Error::Transport(ref e) => Debug::fmt(e, f),
+            Error::Protocol(ref e) => Debug::fmt(e, f),
+            Error::Application(ref e) => Debug::fmt(e, f),
+            Error::User(ref e) => Debug::fmt(e, f),
+        }
+    }
+}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        match *self {
+            Error::Transport(ref e) => Display::fmt(e, f),
+            Error::Protocol(ref e) => Display::fmt(e, f),
+            Error::Application(ref e) => Display::fmt(e, f),
+            Error::User(ref e) => Display::fmt(e, f),
+        }
+    }
+}
+
+impl From<String> for Error {
+    fn from(s: String) -> Self {
+        Error::Application(ApplicationError {
+            kind: ApplicationErrorKind::Unknown,
+            message: s,
+        })
+    }
+}
+
+impl<'a> From<&'a str> for Error {
+    fn from(s: &'a str) -> Self {
+        Error::Application(ApplicationError {
+            kind: ApplicationErrorKind::Unknown,
+            message: String::from(s),
+        })
+    }
+}
+
+impl From<TransportError> for Error {
+    fn from(e: TransportError) -> Self {
+        Error::Transport(e)
+    }
+}
+
+impl From<ProtocolError> for Error {
+    fn from(e: ProtocolError) -> Self {
+        Error::Protocol(e)
+    }
+}
+
+impl From<ApplicationError> for Error {
+    fn from(e: ApplicationError) -> Self {
+        Error::Application(e)
+    }
+}
+
+/// Create a new `Error` instance of type `Transport` that wraps a
+/// `TransportError`.
+pub fn new_transport_error<S: Into<String>>(kind: TransportErrorKind, message: S) -> Error {
+    Error::Transport(TransportError::new(kind, message))
+}
+
+/// Information about I/O errors.
+#[derive(Debug)]
+pub struct TransportError {
+    /// I/O error variant.
+    ///
+    /// If a specific `TransportErrorKind` does not apply use
+    /// `TransportErrorKind::Unknown`.
+    pub kind: TransportErrorKind,
+    /// Human-readable error message.
+    pub message: String,
+}
+
+impl TransportError {
+    /// Create a new `TransportError`.
+    pub fn new<S: Into<String>>(kind: TransportErrorKind, message: S) -> TransportError {
+        TransportError {
+            kind: kind,
+            message: message.into(),
+        }
+    }
+}
+
+/// I/O error categories.
+///
+/// This list may grow, and it is not recommended to match against it.
+#[derive(Clone, Copy, Eq, Debug, PartialEq)]
+pub enum TransportErrorKind {
+    /// Catch-all I/O error.
+    Unknown = 0,
+    /// An I/O operation was attempted when the transport channel was not open.
+    NotOpen = 1,
+    /// The transport channel cannot be opened because it was opened previously.
+    AlreadyOpen = 2,
+    /// An I/O operation timed out.
+    TimedOut = 3,
+    /// A read could not complete because no bytes were available.
+    EndOfFile = 4,
+    /// An invalid (buffer/message) size was requested or received.
+    NegativeSize = 5,
+    /// Too large a buffer or message size was requested or received.
+    SizeLimit = 6,
+}
+
+impl TransportError {
+    fn description(&self) -> &str {
+        match self.kind {
+            TransportErrorKind::Unknown => "transport error",
+            TransportErrorKind::NotOpen => "not open",
+            TransportErrorKind::AlreadyOpen => "already open",
+            TransportErrorKind::TimedOut => "timed out",
+            TransportErrorKind::EndOfFile => "end of file",
+            TransportErrorKind::NegativeSize => "negative size message",
+            TransportErrorKind::SizeLimit => "message too long",
+        }
+    }
+}
+
+impl Display for TransportError {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        write!(f, "{}", self.description())
+    }
+}
+
+impl TryFrom<i32> for TransportErrorKind {
+    type Err = Error;
+    fn try_from(from: i32) -> Result<Self, Self::Err> {
+        match from {
+            0 => Ok(TransportErrorKind::Unknown),
+            1 => Ok(TransportErrorKind::NotOpen),
+            2 => Ok(TransportErrorKind::AlreadyOpen),
+            3 => Ok(TransportErrorKind::TimedOut),
+            4 => Ok(TransportErrorKind::EndOfFile),
+            5 => Ok(TransportErrorKind::NegativeSize),
+            6 => Ok(TransportErrorKind::SizeLimit),
+            _ => {
+                Err(Error::Protocol(ProtocolError {
+                    kind: ProtocolErrorKind::Unknown,
+                    message: format!("cannot convert {} to TransportErrorKind", from),
+                }))
+            }
+        }
+    }
+}
+
+impl From<io::Error> for Error {
+    fn from(err: io::Error) -> Self {
+        match err.kind() {
+            io::ErrorKind::ConnectionReset |
+            io::ErrorKind::ConnectionRefused |
+            io::ErrorKind::NotConnected => {
+                Error::Transport(TransportError {
+                    kind: TransportErrorKind::NotOpen,
+                    message: err.description().to_owned(),
+                })
+            }
+            io::ErrorKind::AlreadyExists => {
+                Error::Transport(TransportError {
+                    kind: TransportErrorKind::AlreadyOpen,
+                    message: err.description().to_owned(),
+                })
+            }
+            io::ErrorKind::TimedOut => {
+                Error::Transport(TransportError {
+                    kind: TransportErrorKind::TimedOut,
+                    message: err.description().to_owned(),
+                })
+            }
+            io::ErrorKind::UnexpectedEof => {
+                Error::Transport(TransportError {
+                    kind: TransportErrorKind::EndOfFile,
+                    message: err.description().to_owned(),
+                })
+            }
+            _ => {
+                Error::Transport(TransportError {
+                    kind: TransportErrorKind::Unknown,
+                    message: err.description().to_owned(), // FIXME: use io error's debug string
+                })
+            }
+        }
+    }
+}
+
+impl From<string::FromUtf8Error> for Error {
+    fn from(err: string::FromUtf8Error) -> Self {
+        Error::Protocol(ProtocolError {
+            kind: ProtocolErrorKind::InvalidData,
+            message: err.description().to_owned(), // FIXME: use fmt::Error's debug string
+        })
+    }
+}
+
+/// Create a new `Error` instance of type `Protocol` that wraps a
+/// `ProtocolError`.
+pub fn new_protocol_error<S: Into<String>>(kind: ProtocolErrorKind, message: S) -> Error {
+    Error::Protocol(ProtocolError::new(kind, message))
+}
+
+/// Information about errors that occur in the runtime library.
+#[derive(Debug)]
+pub struct ProtocolError {
+    /// Protocol error variant.
+    ///
+    /// If a specific `ProtocolErrorKind` does not apply use
+    /// `ProtocolErrorKind::Unknown`.
+    pub kind: ProtocolErrorKind,
+    /// Human-readable error message.
+    pub message: String,
+}
+
+impl ProtocolError {
+    /// Create a new `ProtocolError`.
+    pub fn new<S: Into<String>>(kind: ProtocolErrorKind, message: S) -> ProtocolError {
+        ProtocolError {
+            kind: kind,
+            message: message.into(),
+        }
+    }
+}
+
+/// Runtime library error categories.
+///
+/// This list may grow, and it is not recommended to match against it.
+#[derive(Clone, Copy, Eq, Debug, PartialEq)]
+pub enum ProtocolErrorKind {
+    /// Catch-all runtime-library error.
+    Unknown = 0,
+    /// An invalid argument was supplied to a library function, or invalid data
+    /// was received from a Thrift endpoint.
+    InvalidData = 1,
+    /// An invalid size was received in an encoded field.
+    NegativeSize = 2,
+    /// Thrift message or field was too long.
+    SizeLimit = 3,
+    /// Unsupported or unknown Thrift protocol version.
+    BadVersion = 4,
+    /// Unsupported Thrift protocol, server or field type.
+    NotImplemented = 5,
+    /// Reached the maximum nested depth to which an encoded Thrift field could
+    /// be skipped.
+    DepthLimit = 6,
+}
+
+impl ProtocolError {
+    fn description(&self) -> &str {
+        match self.kind {
+            ProtocolErrorKind::Unknown => "protocol error",
+            ProtocolErrorKind::InvalidData => "bad data",
+            ProtocolErrorKind::NegativeSize => "negative message size",
+            ProtocolErrorKind::SizeLimit => "message too long",
+            ProtocolErrorKind::BadVersion => "invalid thrift version",
+            ProtocolErrorKind::NotImplemented => "not implemented",
+            ProtocolErrorKind::DepthLimit => "maximum skip depth reached",
+        }
+    }
+}
+
+impl Display for ProtocolError {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        write!(f, "{}", self.description())
+    }
+}
+
+impl TryFrom<i32> for ProtocolErrorKind {
+    type Err = Error;
+    fn try_from(from: i32) -> Result<Self, Self::Err> {
+        match from {
+            0 => Ok(ProtocolErrorKind::Unknown),
+            1 => Ok(ProtocolErrorKind::InvalidData),
+            2 => Ok(ProtocolErrorKind::NegativeSize),
+            3 => Ok(ProtocolErrorKind::SizeLimit),
+            4 => Ok(ProtocolErrorKind::BadVersion),
+            5 => Ok(ProtocolErrorKind::NotImplemented),
+            6 => Ok(ProtocolErrorKind::DepthLimit),
+            _ => {
+                Err(Error::Protocol(ProtocolError {
+                    kind: ProtocolErrorKind::Unknown,
+                    message: format!("cannot convert {} to ProtocolErrorKind", from),
+                }))
+            }
+        }
+    }
+}
+
+/// Create a new `Error` instance of type `Application` that wraps an
+/// `ApplicationError`.
+pub fn new_application_error<S: Into<String>>(kind: ApplicationErrorKind, message: S) -> Error {
+    Error::Application(ApplicationError::new(kind, message))
+}
+
+/// Information about errors in auto-generated code or in user-implemented
+/// service handlers.
+#[derive(Debug)]
+pub struct ApplicationError {
+    /// Application error variant.
+    ///
+    /// If a specific `ApplicationErrorKind` does not apply use
+    /// `ApplicationErrorKind::Unknown`.
+    pub kind: ApplicationErrorKind,
+    /// Human-readable error message.
+    pub message: String,
+}
+
+impl ApplicationError {
+    /// Create a new `ApplicationError`.
+    pub fn new<S: Into<String>>(kind: ApplicationErrorKind, message: S) -> ApplicationError {
+        ApplicationError {
+            kind: kind,
+            message: message.into(),
+        }
+    }
+}
+
+/// Auto-generated or user-implemented code error categories.
+///
+/// This list may grow, and it is not recommended to match against it.
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub enum ApplicationErrorKind {
+    /// Catch-all application error.
+    Unknown = 0,
+    /// Made service call to an unknown service method.
+    UnknownMethod = 1,
+    /// Received an unknown Thrift message type. That is, not one of the
+    /// `thrift::protocol::TMessageType` variants.
+    InvalidMessageType = 2,
+    /// Method name in a service reply does not match the name of the
+    /// receiving service method.
+    WrongMethodName = 3,
+    /// Received an out-of-order Thrift message.
+    BadSequenceId = 4,
+    /// Service reply is missing required fields.
+    MissingResult = 5,
+    /// Auto-generated code failed unexpectedly.
+    InternalError = 6,
+    /// Thrift protocol error. When possible use `Error::ProtocolError` with a
+    /// specific `ProtocolErrorKind` instead.
+    ProtocolError = 7,
+    /// *Unknown*. Included only for compatibility with existing Thrift implementations.
+    InvalidTransform = 8, // ??
+    /// Thrift endpoint requested, or is using, an unsupported encoding.
+    InvalidProtocol = 9, // ??
+    /// Thrift endpoint requested, or is using, an unsupported auto-generated client type.
+    UnsupportedClientType = 10, // ??
+}
+
+impl ApplicationError {
+    fn description(&self) -> &str {
+        match self.kind {
+            ApplicationErrorKind::Unknown => "service error",
+            ApplicationErrorKind::UnknownMethod => "unknown service method",
+            ApplicationErrorKind::InvalidMessageType => "wrong message type received",
+            ApplicationErrorKind::WrongMethodName => "unknown method reply received",
+            ApplicationErrorKind::BadSequenceId => "out of order sequence id",
+            ApplicationErrorKind::MissingResult => "missing method result",
+            ApplicationErrorKind::InternalError => "remote service threw exception",
+            ApplicationErrorKind::ProtocolError => "protocol error",
+            ApplicationErrorKind::InvalidTransform => "invalid transform",
+            ApplicationErrorKind::InvalidProtocol => "invalid protocol requested",
+            ApplicationErrorKind::UnsupportedClientType => "unsupported protocol client",
+        }
+    }
+}
+
+impl Display for ApplicationError {
+    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+        write!(f, "{}", self.description())
+    }
+}
+
+impl TryFrom<i32> for ApplicationErrorKind {
+    type Err = Error;
+    fn try_from(from: i32) -> Result<Self, Self::Err> {
+        match from {
+            0 => Ok(ApplicationErrorKind::Unknown),
+            1 => Ok(ApplicationErrorKind::UnknownMethod),
+            2 => Ok(ApplicationErrorKind::InvalidMessageType),
+            3 => Ok(ApplicationErrorKind::WrongMethodName),
+            4 => Ok(ApplicationErrorKind::BadSequenceId),
+            5 => Ok(ApplicationErrorKind::MissingResult),
+            6 => Ok(ApplicationErrorKind::InternalError),
+            7 => Ok(ApplicationErrorKind::ProtocolError),
+            8 => Ok(ApplicationErrorKind::InvalidTransform),
+            9 => Ok(ApplicationErrorKind::InvalidProtocol),
+            10 => Ok(ApplicationErrorKind::UnsupportedClientType),
+            _ => {
+                Err(Error::Application(ApplicationError {
+                    kind: ApplicationErrorKind::Unknown,
+                    message: format!("cannot convert {} to ApplicationErrorKind", from),
+                }))
+            }
+        }
+    }
+}