open_dis_rust/common/
dis_error.rs

1//     open-dis-rust - Rust implementation of the IEEE 1278.1-2012 Distributed Interactive
2//                     Simulation (DIS) application protocol
3//     Copyright (C) 2025 Cameron Howell
4//
5//     Licensed under the BSD 2-Clause License
6
7use thiserror::Error;
8
9use crate::common::enums::ProtocolVersion;
10
11/// Protocol version and header related states
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum PduState {
14    Uninitialized,
15    HeaderValid,
16    BodyValid,
17    Complete,
18}
19
20#[derive(Error, Debug)]
21/// Enhanced error types for DIS protocol operations
22pub enum DISError {
23    #[error("Invalid PDU header: {reason}")]
24    InvalidHeader {
25        reason: String,
26        #[source]
27        source: Option<Box<dyn std::error::Error + Send + Sync>>,
28    },
29
30    #[error("PDU deserialization error: {msg}")]
31    DeserializationError {
32        msg: String,
33        #[source]
34        source: Option<Box<dyn std::error::Error + Send + Sync>>,
35    },
36
37    #[error("PDU serialization error: {msg}")]
38    SerializationError {
39        msg: String,
40        #[source]
41        source: Option<Box<dyn std::error::Error + Send + Sync>>,
42    },
43
44    #[error("Invalid PDU state transition: {current_state:?} -> {target_state:?}")]
45    InvalidStateTransition {
46        current_state: PduState,
47        target_state: PduState,
48    },
49
50    #[error("Protocol version mismatch: expected {expected:?}, got {got:?}")]
51    ProtocolVersionMismatch {
52        expected: ProtocolVersion,
53        got: ProtocolVersion,
54    },
55
56    #[error("Invalid field value: {field} = {value}, {reason}")]
57    InvalidFieldValue {
58        field: String,
59        value: String,
60        reason: String,
61    },
62
63    #[error("Buffer underflow: tried to read {attempted} bytes, but only {available} bytes remain")]
64    BufferUnderflow { attempted: usize, available: usize },
65
66    #[error("Network error: {0}")]
67    NetworkError(#[from] std::io::Error),
68
69    #[error("PDU size exceeds maximum allowed: {size} > {max_size}")]
70    PduSizeExceeded { size: usize, max_size: usize },
71}
72
73impl DISError {
74    /// Create a new `InvalidHeader` error with optional source error
75    pub fn invalid_header<S: Into<String>>(
76        reason: S,
77        source: Option<Box<dyn std::error::Error + Send + Sync>>,
78    ) -> Self {
79        Self::InvalidHeader {
80            reason: reason.into(),
81            source,
82        }
83    }
84
85    /// Create a new `DeserializationError` with optional source error
86    pub fn deserialization_error<S: Into<String>>(
87        msg: S,
88        source: Option<Box<dyn std::error::Error + Send + Sync>>,
89    ) -> Self {
90        Self::DeserializationError {
91            msg: msg.into(),
92            source,
93        }
94    }
95
96    /// Create a new `SerializationError` with optional source error
97    pub fn serialization_error<S: Into<String>>(
98        msg: S,
99        source: Option<Box<dyn std::error::Error + Send + Sync>>,
100    ) -> Self {
101        Self::SerializationError {
102            msg: msg.into(),
103            source,
104        }
105    }
106
107    /// Create a new `InvalidFieldValue` error
108    pub fn invalid_field<S: Into<String>>(field: S, value: S, reason: S) -> Self {
109        Self::InvalidFieldValue {
110            field: field.into(),
111            value: value.into(),
112            reason: reason.into(),
113        }
114    }
115
116    #[must_use]
117    /// Create a new `BufferUnderflow` error
118    pub const fn buffer_underflow(attempted: usize, available: usize) -> Self {
119        Self::BufferUnderflow {
120            attempted,
121            available,
122        }
123    }
124
125    #[must_use]
126    /// Create a new `PduSizeExceeded` error
127    pub const fn pdu_size_exceeded(size: usize, max_size: usize) -> Self {
128        Self::PduSizeExceeded { size, max_size }
129    }
130}
131
132/// Result type for DIS operations
133pub type DISResult<T> = Result<T, DISError>;