use crate::encoding::EncodingError;
use crate::service::{AbortReason, RejectReason};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum ClientError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("encoding error: {0}")]
Encoding(#[from] EncodingError),
#[error("failed to decode response: {0}")]
Decode(String),
#[error("request timed out")]
Timeout,
#[error("no response from device")]
NoResponse,
#[error("request rejected: {0}")]
Rejected(RejectReason),
#[error("transaction aborted: {0}")]
Abort(AbortReason),
#[error("{}", describe_bacnet_error(*class, *code))]
PropertyError {
class: u32,
code: u32,
},
#[error("invalid address: {0}")]
AddressParse(String),
}
fn error_class_name(class: u32) -> Option<&'static str> {
Some(match class {
0 => "device",
1 => "object",
2 => "property",
3 => "resources",
4 => "security",
5 => "services",
6 => "vt",
7 => "communication",
_ => return None,
})
}
fn error_code_name(code: u32) -> Option<&'static str> {
Some(match code {
0 => "other",
9 => "invalid-data-type",
20 => "no-space-to-write-property",
23 => "object-deletion-not-permitted",
27 => "read-access-denied",
29 => "service-request-denied",
30 => "timeout",
31 => "unknown-object",
32 => "unknown-property",
37 => "value-out-of-range",
40 => "write-access-denied",
44 => "not-cov-property",
45 => "optional-functionality-not-supported",
47 => "datatype-not-supported",
50 => "property-is-not-an-array",
70 => "unknown-device",
_ => return None,
})
}
fn describe_bacnet_error(class: u32, code: u32) -> String {
let class_label = match error_class_name(class) {
Some(name) => format!("{name}[{class}]"),
None => format!("{class}"),
};
match error_code_name(code) {
Some(name) => format!("{name} (class {class_label}, code {code})"),
None => format!("BACnet error (class {class_label}, code {code})"),
}
}