Skip to main content

fdf_component/
error.rs

1// Copyright 2026 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use zx::Status;
6
7/// Error type returned by drivers on startup.
8///
9/// Driver authors should not usually need to construct or convert errors to this type
10/// manually. Instead, they can use the `?` operator to automatically propagate and convert
11/// higher-level error types (like [`zx::Status`], [`anyhow::Error`], and [`fidl::Error`])
12/// into `DriverError` at the `Driver::start` boundary.
13#[derive(Debug)]
14pub enum DriverError {
15    /// A zx status error.
16    Status(Status),
17    /// An anyhow error.
18    Anyhow(anyhow::Error),
19    /// A FIDL error.
20    Fidl(fidl::Error),
21}
22
23impl DriverError {
24    /// Convert the error into a [`Status`], logging any internal or FIDL errors.
25    pub fn log_to_status(self) -> Status {
26        match self {
27            DriverError::Status(status) => status,
28            DriverError::Anyhow(err) => {
29                if let Some(status) = err.root_cause().downcast_ref::<Status>() {
30                    *status
31                } else {
32                    log::error!("Driver failed with internal error: {:?}", err);
33                    Status::INTERNAL
34                }
35            }
36            DriverError::Fidl(err) => {
37                log::error!("Driver failed with FIDL error: {:?}", err);
38                Status::INTERNAL
39            }
40        }
41    }
42}
43
44impl From<Status> for DriverError {
45    fn from(status: Status) -> Self {
46        DriverError::Status(status)
47    }
48}
49
50impl From<anyhow::Error> for DriverError {
51    fn from(err: anyhow::Error) -> Self {
52        DriverError::Anyhow(err)
53    }
54}
55
56impl From<fidl::Error> for DriverError {
57    fn from(err: fidl::Error) -> Self {
58        DriverError::Fidl(err)
59    }
60}
61
62impl std::fmt::Display for DriverError {
63    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64        match self {
65            DriverError::Status(status) => write!(f, "Status: {status}"),
66            DriverError::Anyhow(err) => write!(f, "Anyhow: {err}"),
67            DriverError::Fidl(err) => write!(f, "FIDL: {err}"),
68        }
69    }
70}
71
72impl std::error::Error for DriverError {
73    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
74        match self {
75            DriverError::Status(status) => Some(status),
76            DriverError::Anyhow(err) => Some(err.as_ref()),
77            DriverError::Fidl(err) => Some(err),
78        }
79    }
80}
81
82impl From<fidl_next::Error<Status>> for DriverError {
83    fn from(err: fidl_next::Error<Status>) -> Self {
84        match err {
85            fidl_next::Error::Encode(encode_err) => {
86                log::error!("FIDL encode error: {:?}", encode_err);
87                DriverError::Status(Status::INTERNAL)
88            }
89            fidl_next::Error::Decode(decode_err) => {
90                log::error!("FIDL decode error: {:?}", decode_err);
91                DriverError::Status(Status::INTERNAL)
92            }
93            fidl_next::Error::Protocol(protocol_err) => match protocol_err {
94                fidl_next::ProtocolError::TransportError(status) => DriverError::Status(status),
95                fidl_next::ProtocolError::PeerClosed => DriverError::Status(Status::PEER_CLOSED),
96                _ => {
97                    log::error!("FIDL protocol error: {:?}", protocol_err);
98                    DriverError::Status(Status::INTERNAL)
99                }
100            },
101        }
102    }
103}
104
105/// Extension trait for `fidl_next::FlexibleResult` to convert it into a `Result` with `DriverError`.
106pub trait FlexibleResultExt<T> {
107    /// Convert the flexible result into a `Result` with `DriverError`.
108    fn into_driver_result(self) -> Result<T, DriverError>;
109}
110
111impl<T> FlexibleResultExt<T> for fidl_next::FlexibleResult<T, i32> {
112    fn into_driver_result(self) -> Result<T, DriverError> {
113        match self {
114            fidl_next::FlexibleResult::Ok(t) => Ok(t),
115            fidl_next::FlexibleResult::Err(raw_status) => {
116                Err(DriverError::Status(Status::from_raw(raw_status)))
117            }
118            fidl_next::FlexibleResult::FrameworkErr(err) => {
119                log::error!("FIDL framework error: {:?}", err);
120                Err(DriverError::Status(Status::INTERNAL))
121            }
122        }
123    }
124}
125
126/// Extension trait for `fidl_next::Flexible` to convert it into a `Result` with `DriverError`.
127pub trait FlexibleExt<T> {
128    /// Convert the flexible response into a `Result` with `DriverError`.
129    fn into_driver_result(self) -> Result<T, DriverError>;
130}
131
132impl<T> FlexibleExt<T> for fidl_next::Flexible<T> {
133    fn into_driver_result(self) -> Result<T, DriverError> {
134        match self {
135            fidl_next::Flexible::Ok(t) => Ok(t),
136            fidl_next::Flexible::FrameworkErr(err) => {
137                log::error!("FIDL framework error: {:?}", err);
138                Err(DriverError::Status(Status::INTERNAL))
139            }
140        }
141    }
142}