1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Error (common to all fidl operations)

use crate::handle::{ObjectType, Rights};
use std::sync::Arc;
use zx_status::Status;

/// A specialized `Result` type for FIDL operations.
pub type Result<T, E = Error> = std::result::Result<T, E>;

#[derive(Debug, Clone, thiserror::Error)]
#[allow(missing_docs)]
pub enum TransportError {
    #[error(transparent)]
    Status(#[from] Status),
    #[error(transparent)]
    Other(Arc<dyn std::error::Error + Send + Sync>),
}

// This prevents some binary size bloat.
impl Drop for TransportError {
    #[inline(never)]
    fn drop(&mut self) {}
}

/// The error type used by FIDL operations.
#[derive(Debug, Clone, thiserror::Error)]
#[non_exhaustive]
#[allow(missing_docs)]
pub enum Error {
    #[error("Unexpected response to synchronous FIDL query.")]
    UnexpectedSyncResponse,

    #[error("Invalid FIDL boolean.")]
    InvalidBoolean,

    #[error("Invalid header for a FIDL buffer.")]
    InvalidHeader,

    #[error("Incompatible wire format magic number: {0}.")]
    IncompatibleMagicNumber(u8),

    #[error("Unsupported wire format version")]
    UnsupportedWireFormatVersion,

    #[error("Invalid FIDL buffer.")]
    Invalid,

    #[error("The FIDL object could not fit within the provided buffer range")]
    OutOfRange,

    #[error("Decoding the FIDL object did not use all of the bytes provided.")]
    ExtraBytes,

    #[error("Decoding the FIDL object did not use all of the handles provided.")]
    ExtraHandles,

    #[error(
        "Decoding the FIDL object observed non-zero value in the padding region \
        starting at byte {padding_start}."
    )]
    NonZeroPadding {
        /// Index of the first byte of the padding, relative to the beginning of the message.
        padding_start: usize,
    },

    #[error("The FIDL object had too many layers of out-of-line recursion.")]
    MaxRecursionDepth,

    #[error(
        "There was an attempt to read or write a null-valued object as a non-nullable FIDL type."
    )]
    NotNullable,

    #[error("A FIDL object reference with nonzero byte length had a null data pointer.")]
    UnexpectedNullRef,

    #[error("A FIDL message contained incorrectly encoded UTF8.")]
    Utf8Error,

    #[error("Vector was too long. Expected at most {max_length} elements, got {actual_length}.")]
    VectorTooLong {
        /// Maximum length, i.e. the `N` in `vector<T>:N`.
        max_length: usize,
        /// Actual length of the vector (number of elements).
        actual_length: usize,
    },

    #[error("String was too long. Expected at most {max_bytes} bytes, got {actual_bytes}.")]
    StringTooLong {
        /// Maximum length in bytes, i.e. the `N` in `string:N`.
        max_bytes: usize,
        /// Actual length of the string in bytes.
        actual_bytes: usize,
    },

    #[error(
        "A message was received for ordinal value {ordinal} that the FIDL \
        protocol {protocol_name} does not understand."
    )]
    UnknownOrdinal { ordinal: u64, protocol_name: &'static str },

    #[error(
        "Server for the FIDL protocol {protocol_name} did not recognize method {method_name}."
    )]
    UnsupportedMethod { method_name: &'static str, protocol_name: &'static str },

    #[error("Invalid bits value for a strict bits type.")]
    InvalidBitsValue,

    #[error("Invalid enum value for a strict enum type.")]
    InvalidEnumValue,

    #[error("Unrecognized descriminant for a FIDL union type.")]
    UnknownUnionTag,

    #[error("A FIDL future was polled after it had already completed.")]
    PollAfterCompletion,

    #[error(
        "Received request with zero txid for two-way method ordinal, \
        or nonzero txid for one-way method ordinal."
    )]
    InvalidRequestTxid,

    #[error("Received response with unknown txid.")]
    InvalidResponseTxid,

    #[error("Received response with unexpected ordinal.")]
    InvalidResponseOrdinal,

    #[error("Invalid presence indicator.")]
    InvalidPresenceIndicator,

    #[error("Invalid inline bit in envelope.")]
    InvalidInlineBitInEnvelope,

    #[error("Invalid inline marker in envelope.")]
    InvalidInlineMarkerInEnvelope,

    #[error("Invalid number of bytes in FIDL envelope.")]
    InvalidNumBytesInEnvelope,

    #[error("Invalid number of handles in FIDL envelope.")]
    InvalidNumHandlesInEnvelope,

    #[error("Invalid FIDL handle used on the host.")]
    InvalidHostHandle,

    #[error("Incorrect handle subtype. Expected {}, but received {}", .expected.into_raw(), .received.into_raw())]
    IncorrectHandleSubtype { expected: ObjectType, received: ObjectType },

    #[error("Some expected handle rights are missing: {}", .missing_rights.bits())]
    MissingExpectedHandleRights { missing_rights: Rights },

    #[error("An error was encountered during handle replace()")]
    HandleReplace(#[source] Status),

    #[error("A server encountered an IO error writing a FIDL response to a channel: {0}")]
    ServerResponseWrite(#[source] TransportError),

    #[error(
        "A FIDL server encountered an IO error reading incoming FIDL requests from a channel: {0}"
    )]
    ServerRequestRead(#[source] TransportError),

    #[error("A FIDL server encountered an IO error writing an epitaph into a channel: {0}")]
    ServerEpitaphWrite(#[source] TransportError),

    #[error("A FIDL client encountered an IO error reading a response from a channel: {0}")]
    ClientRead(#[source] TransportError),

    #[error("A FIDL client encountered an IO error writing a request into a channel: {0}")]
    ClientWrite(#[source] TransportError),

    #[error("A FIDL client encountered an IO error issuing a channel call: {0}")]
    ClientCall(#[source] Status),

    #[error("A FIDL client encountered an IO error issuing a channel call: {0}")]
    ClientEvent(#[source] Status),

    #[cfg(not(target_os = "fuchsia"))]
    #[error("A FIDL client's channel to the protocol {protocol_name} was closed: {status}, reason: {}",
        .reason.as_ref().map(String::as_str).unwrap_or("not given")
    )]
    ClientChannelClosed {
        /// The epitaph or `Status::PEER_CLOSED`.
        #[source]
        status: Status,
        /// The name of the protocol at the other end of the channel.
        protocol_name: &'static str,
        /// Further details on why exactly the channel closed.
        reason: Option<String>,
    },

    #[cfg(target_os = "fuchsia")]
    #[error("A FIDL client's channel to the protocol {protocol_name} was closed: {status}")]
    ClientChannelClosed {
        /// The epitaph or `Status::PEER_CLOSED`.
        #[source]
        status: Status,
        /// The name of the protocol at the other end of the channel.
        protocol_name: &'static str,
    },

    #[error("There was an error attaching a FIDL channel to the async executor: {0}")]
    AsyncChannel(#[source] Status),

    #[cfg(target_os = "fuchsia")]
    #[cfg(test)]
    #[error("Test Status: {0}")]
    TestIo(#[source] Status),
}

impl Error {
    /// Returns `true` if the error was sourced by a closed channel.
    pub fn is_closed(&self) -> bool {
        matches!(self, Error::ClientChannelClosed { .. })
    }
}