Skip to main content

instro_ethernetip/
error.rs

1use std::error::Error as StdError;
2
3use rust_ethernet_ip::BatchError;
4use thiserror::Error;
5
6/// Per-tag failure surfaced by a batched read.
7///
8/// Mirrors the variants of the upstream batch error so callers can branch on the specific
9/// failure kind without depending on the backend transport types. Used as the typed `source`
10/// of [`Error::BatchReadItem`].
11#[derive(Debug, Clone, Error)]
12pub enum BatchReadError {
13    #[error("tag not found: {0}")]
14    TagNotFound(String),
15    #[error("data type mismatch: expected {expected}, got {actual}")]
16    DataTypeMismatch { expected: String, actual: String },
17    #[error("network error: {0}")]
18    Network(String),
19    #[error("CIP error (0x{status:02X}): {message}")]
20    Cip { status: u8, message: String },
21    #[error("tag path error: {0}")]
22    TagPath(String),
23    #[error("serialization error: {0}")]
24    Serialization(String),
25    #[error("operation timeout")]
26    Timeout,
27    #[error("error: {0}")]
28    Other(String),
29}
30
31impl From<BatchError> for BatchReadError {
32    fn from(value: BatchError) -> Self {
33        match value {
34            BatchError::TagNotFound(tag) => Self::TagNotFound(tag),
35            BatchError::DataTypeMismatch { expected, actual } => {
36                Self::DataTypeMismatch { expected, actual }
37            }
38            BatchError::NetworkError(msg) => Self::Network(msg),
39            BatchError::CipError { status, message } => Self::Cip { status, message },
40            BatchError::TagPathError(msg) => Self::TagPath(msg),
41            BatchError::SerializationError(msg) => Self::Serialization(msg),
42            BatchError::Timeout => Self::Timeout,
43            BatchError::Other(msg) => Self::Other(msg),
44        }
45    }
46}
47
48impl BatchReadError {
49    pub(crate) fn is_retriable(&self) -> bool {
50        matches!(self, Self::Network(_) | Self::Timeout)
51    }
52}
53
54/// Errors returned by the explicit EtherNet/IP tag API.
55#[derive(Debug, Error)]
56pub enum Error {
57    #[cfg(feature = "blocking")]
58    #[error("failed to create Tokio runtime for blocking EtherNet/IP session: {source}")]
59    CreateRuntime {
60        #[source]
61        source: Box<dyn StdError + Send + Sync>,
62    },
63    #[error("failed to connect to EtherNet/IP device at {addr}: {source}")]
64    Connect {
65        addr: String,
66        // Preserve the backend error as a source while keeping it out of the public API surface.
67        #[source]
68        source: Box<dyn StdError + Send + Sync>,
69    },
70    #[error("failed to read tag '{tag_name}' from {addr}: {source}")]
71    ReadTag {
72        addr: String,
73        tag_name: String,
74        #[source]
75        source: Box<dyn StdError + Send + Sync>,
76    },
77    #[error("failed to read batch of tags from {addr}: {source}")]
78    BatchRead {
79        addr: String,
80        #[source]
81        source: Box<dyn StdError + Send + Sync>,
82    },
83    #[error("failed to read tag '{tag_name}' from {addr}: {source}")]
84    BatchReadItem {
85        addr: String,
86        tag_name: String,
87        #[source]
88        source: BatchReadError,
89    },
90    #[error("failed to decode structured tag '{tag_name}' from {addr} as {target_type}: {source}")]
91    DecodeStructuredTag {
92        addr: String,
93        tag_name: String,
94        target_type: &'static str,
95        #[source]
96        source: Box<dyn StdError + Send + Sync>,
97    },
98    #[error(
99        "failed to decode structured tag '{tag_name}' from {addr}: expected structured value, got {actual_type}"
100    )]
101    UnexpectedValueType {
102        addr: String,
103        tag_name: String,
104        actual_type: &'static str,
105    },
106    #[error("failed to write tag '{tag_name}' on {addr}: {source}")]
107    WriteTag {
108        addr: String,
109        tag_name: String,
110        #[source]
111        source: Box<dyn StdError + Send + Sync>,
112    },
113    #[error("failed to unregister explicit EtherNet/IP session for {addr}: {source}")]
114    Unregister {
115        addr: String,
116        #[source]
117        source: Box<dyn StdError + Send + Sync>,
118    },
119}