1use core::fmt;
10
11#[derive(Clone, Debug, PartialEq, Eq)]
13pub enum PhysicsError {
14 InvalidBodyIndex {
16 index: usize,
18 count: usize,
20 },
21 DeserializationFailed,
23 InvalidConstraint {
25 reason: &'static str,
27 },
28 ZeroLengthVector {
30 context: &'static str,
32 },
33 #[cfg(feature = "std")]
35 IoError {
36 message: &'static str,
38 },
39 CapacityExceeded {
41 resource: &'static str,
43 limit: usize,
45 },
46 InvalidConfiguration {
48 reason: &'static str,
50 },
51}
52
53impl fmt::Display for PhysicsError {
54 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55 match self {
56 Self::InvalidBodyIndex { index, count } => {
57 write!(f, "body index {index} out of range (count={count})")
58 }
59 Self::DeserializationFailed => write!(f, "state deserialization failed"),
60 Self::InvalidConstraint { reason } => {
61 write!(f, "invalid constraint: {reason}")
62 }
63 Self::ZeroLengthVector { context } => {
64 write!(f, "zero-length vector in {context}")
65 }
66 #[cfg(feature = "std")]
67 Self::IoError { message } => write!(f, "I/O error: {message}"),
68 Self::CapacityExceeded { resource, limit } => {
69 write!(f, "{resource} capacity exceeded (limit={limit})")
70 }
71 Self::InvalidConfiguration { reason } => {
72 write!(f, "invalid configuration: {reason}")
73 }
74 }
75 }
76}
77
78#[cfg(feature = "std")]
79impl std::error::Error for PhysicsError {}
80
81#[cfg(all(test, feature = "std"))]
86mod tests {
87 use super::*;
88
89 #[cfg(feature = "std")]
90 #[test]
91 fn test_error_display() {
92 let e = PhysicsError::InvalidBodyIndex { index: 5, count: 3 };
93 let s = format!("{e}");
94 assert!(s.contains('5'), "Should contain index");
95 assert!(s.contains('3'), "Should contain count");
96 }
97
98 #[test]
99 fn test_error_debug() {
100 let e = PhysicsError::DeserializationFailed;
101 let s = format!("{e:?}");
102 assert!(s.contains("DeserializationFailed"));
103 }
104
105 #[test]
106 fn test_error_variants() {
107 let e1 = PhysicsError::InvalidBodyIndex { index: 0, count: 0 };
108 let e2 = PhysicsError::DeserializationFailed;
109 let e3 = PhysicsError::InvalidConstraint {
110 reason: "body A == body B",
111 };
112 let e4 = PhysicsError::ZeroLengthVector {
113 context: "ray direction",
114 };
115 assert_ne!(e1, e2);
116 assert_ne!(e3, e4);
117 }
118
119 #[test]
120 fn test_capacity_exceeded() {
121 let e = PhysicsError::CapacityExceeded {
122 resource: "bodies",
123 limit: 10000,
124 };
125 let s = format!("{e}");
126 assert!(s.contains("bodies"));
127 assert!(s.contains("10000"));
128 }
129
130 #[test]
131 fn test_invalid_configuration() {
132 let e = PhysicsError::InvalidConfiguration {
133 reason: "substeps must be > 0",
134 };
135 let s = format!("{e}");
136 assert!(s.contains("substeps"));
137 }
138
139 #[cfg(feature = "std")]
140 #[test]
141 fn test_io_error() {
142 let e = PhysicsError::IoError {
143 message: "file not found",
144 };
145 let s = format!("{e}");
146 assert!(s.contains("file not found"));
147 }
148}