astarte_message_hub/
error.rs1use std::io;
22use std::path::PathBuf;
23
24use astarte_device_sdk::introspection::AddInterfaceError;
25use astarte_interfaces::error::Error as InterfaceError;
26use astarte_message_hub_proto::prost::UnknownEnumValue;
27use tonic::{Code, Status};
28use tracing::error;
29use uuid::Uuid;
30
31use crate::astarte::handler::DeviceError;
32use crate::config::dynamic::http::HttpError;
33
34#[derive(thiserror::Error, Debug)]
36pub enum AstarteMessageHubError {
37 #[error(transparent)]
39 Astarte(#[from] astarte_device_sdk::error::Error),
40
41 #[error("{0}")]
43 AstarteInvalidData(String),
44
45 #[error(transparent)]
47 Io(#[from] std::io::Error),
48
49 #[error("unrecoverable error ({0})")]
51 Fatal(String),
52
53 #[error(transparent)]
55 Transport(#[from] tonic::transport::Error),
56
57 #[error(transparent)]
59 Zbus(#[from] zbus::Error),
60
61 #[error("HTTP server error, {0}")]
63 HttpServer(#[from] HttpError),
64
65 #[error("couldn't convert timestamp, {0}")]
67 Timestamp(&'static str),
68
69 #[error("error returned by the device")]
71 Device(#[from] DeviceError),
72
73 #[error("couldn't parse node id")]
75 Uuid(#[from] uuid::Error),
76
77 #[error("node id not found {0}")]
79 NodeId(Uuid),
80
81 #[error("failed to parse am Interface")]
83 ParseInterface(#[from] InterfaceError),
84
85 #[error("failed to add interfaces while building an Astarte device")]
87 AddInterface(#[from] AddInterfaceError),
88
89 #[error("coudln't read the configuration")]
91 Config(#[from] ConfigError),
92
93 #[error("received an invalid ownership enumeration field, {0}")]
95 InvalidProtoOwnership(#[source] UnknownEnumValue),
96
97 #[error("interface {interface} not present in the introspection of node {node_id}")]
99 MissingInterface {
100 interface: String,
102 node_id: Uuid,
104 },
105
106 #[error("couldn't convert value {ctx}")]
108 Conversion {
109 ctx: &'static str,
111 },
112}
113
114impl From<AstarteMessageHubError> for Status {
115 fn from(value: AstarteMessageHubError) -> Self {
116 error!(error = %value, "grpc call failed");
117
118 let code = match value {
119 AstarteMessageHubError::Astarte(_)
120 | AstarteMessageHubError::Io(_)
121 | AstarteMessageHubError::Fatal(_)
122 | AstarteMessageHubError::Config(_)
123 | AstarteMessageHubError::Transport(_)
124 | AstarteMessageHubError::Zbus(_)
125 | AstarteMessageHubError::HttpServer(_)
126 | AstarteMessageHubError::ParseInterface(_)
127 | AstarteMessageHubError::AddInterface(_) => Code::Internal,
128 AstarteMessageHubError::Device(ref err) => err.into(),
129 AstarteMessageHubError::AstarteInvalidData(_)
130 | AstarteMessageHubError::Timestamp(_)
131 | AstarteMessageHubError::Uuid(_)
132 | AstarteMessageHubError::NodeId(_)
133 | AstarteMessageHubError::InvalidProtoOwnership(_)
134 | AstarteMessageHubError::Conversion { .. }
135 | AstarteMessageHubError::MissingInterface { .. } => Code::InvalidArgument,
136 };
137
138 Status::new(code, value.to_string())
139 }
140}
141
142#[derive(thiserror::Error, Debug)]
144pub enum ConfigError {
145 #[error("{0} field is missing")]
147 MissingField(&'static str),
148 #[error("either the pairing token or credential secret must be provided")]
150 Credentials,
151 #[error("interface path {0:?} is not a directory")]
153 InvalidInterfaceDirectory(Option<PathBuf>),
154 #[error("coudln't deserialize the configuration file")]
156 Toml(#[from] toml::de::Error),
157 #[error("couldn't read configuration file")]
159 File(#[from] io::Error),
160 #[error("coudldn't parse url")]
162 Url(#[from] url::ParseError),
163 #[error("coudldn't read dynamic configuration {0}")]
165 Dynamic(PathBuf),
166}