1#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))]
2use copybook_error::{Error, ErrorCode};
9
10pub type Result<T> = std::result::Result<T, Error>;
12
13pub trait OptionExt<T> {
15 fn ok_or_cbkp_error(self, code: ErrorCode, message: impl Into<String>) -> Result<T>;
20
21 fn ok_or_error(self, error: Error) -> Result<T>;
26}
27
28impl<T> OptionExt<T> for Option<T> {
29 #[allow(clippy::inline_always)]
31 #[inline]
32 fn ok_or_cbkp_error(self, code: ErrorCode, message: impl Into<String>) -> Result<T> {
33 match self {
34 Some(value) => Ok(value),
35 None => {
36 Err(Error::new(code, message.into()))
38 }
39 }
40 }
41
42 #[allow(clippy::inline_always)]
43 #[inline(always)]
44 fn ok_or_error(self, error: Error) -> Result<T> {
45 match self {
46 Some(value) => Ok(value),
47 None => Err(error),
48 }
49 }
50}
51
52pub trait VecExt<T> {
54 fn pop_or_cbkp_error(&mut self, code: ErrorCode, message: impl Into<String>) -> Result<T>;
59
60 fn last_or_cbkp_error(&self, code: ErrorCode, message: impl Into<String>) -> Result<&T>;
65
66 fn last_mut_or_cbkp_error(
71 &mut self,
72 code: ErrorCode,
73 message: impl Into<String>,
74 ) -> Result<&mut T>;
75}
76
77impl<T> VecExt<T> for Vec<T> {
78 #[allow(clippy::inline_always)]
80 #[inline(always)]
81 fn pop_or_cbkp_error(&mut self, code: ErrorCode, message: impl Into<String>) -> Result<T> {
82 match self.pop() {
84 Some(value) => Ok(value),
85 None => Err(Error::new(code, message.into())),
86 }
87 }
88
89 #[allow(clippy::inline_always)]
90 #[inline(always)]
91 fn last_or_cbkp_error(&self, code: ErrorCode, message: impl Into<String>) -> Result<&T> {
92 #[allow(clippy::if_not_else)]
94 if !self.is_empty() {
95 Ok(&self[self.len() - 1])
97 } else {
98 Err(Error::new(code, message.into()))
99 }
100 }
101
102 #[allow(clippy::inline_always)]
103 #[inline(always)]
104 fn last_mut_or_cbkp_error(
105 &mut self,
106 code: ErrorCode,
107 message: impl Into<String>,
108 ) -> Result<&mut T> {
109 #[allow(clippy::if_not_else)]
111 if !self.is_empty() {
112 let len = self.len();
113 Ok(&mut self[len - 1])
115 } else {
116 Err(Error::new(code, message.into()))
117 }
118 }
119}
120
121pub trait SliceExt<T> {
123 fn get_or_cbkp_error(
128 &self,
129 index: usize,
130 code: ErrorCode,
131 message: impl Into<String>,
132 ) -> Result<&T>;
133
134 fn get_mut_or_cbkp_error(
139 &mut self,
140 index: usize,
141 code: ErrorCode,
142 message: impl Into<String>,
143 ) -> Result<&mut T>;
144}
145
146impl<T> SliceExt<T> for [T] {
147 #[allow(clippy::inline_always)]
149 #[inline(always)]
150 fn get_or_cbkp_error(
151 &self,
152 index: usize,
153 code: ErrorCode,
154 message: impl Into<String>,
155 ) -> Result<&T> {
156 if index < self.len() {
158 Ok(&self[index])
160 } else {
161 Err(Error::new(code, message.into()))
162 }
163 }
164
165 #[allow(clippy::inline_always)]
166 #[inline(always)]
167 fn get_mut_or_cbkp_error(
168 &mut self,
169 index: usize,
170 code: ErrorCode,
171 message: impl Into<String>,
172 ) -> Result<&mut T> {
173 if index < self.len() {
175 Ok(&mut self[index])
177 } else {
178 Err(Error::new(code, message.into()))
179 }
180 }
181}
182
183pub mod safe_ops {
185 pub use copybook_safe_ops::*;
186}
187
188#[cfg(test)]
189#[allow(clippy::expect_used)]
190#[allow(clippy::unwrap_used)]
191mod tests {
192 use super::{OptionExt, VecExt, safe_ops};
193 use copybook_error::{ErrorCode, Result};
194
195 #[test]
196 fn test_option_ext_some() -> Result<()> {
197 let opt = Some(42);
198 let value = opt.ok_or_cbkp_error(ErrorCode::CBKP001_SYNTAX, "test")?;
199 assert_eq!(value, 42);
200 Ok(())
201 }
202
203 #[test]
204 fn test_option_ext_none() {
205 let opt: Option<i32> = None;
206 let result = opt.ok_or_cbkp_error(ErrorCode::CBKP001_SYNTAX, "test error");
207 assert!(matches!(
208 result,
209 Err(error) if error.code == ErrorCode::CBKP001_SYNTAX
210 ));
211 }
212
213 #[test]
214 fn test_vec_ext_pop() -> Result<()> {
215 let mut vec = vec![1, 2, 3];
216 let value = vec.pop_or_cbkp_error(ErrorCode::CBKP001_SYNTAX, "test")?;
217 assert_eq!(value, 3);
218 Ok(())
219 }
220
221 #[test]
222 fn test_vec_ext_pop_empty() {
223 let mut empty_vec: Vec<i32> = vec![];
224 let result = empty_vec.pop_or_cbkp_error(ErrorCode::CBKP001_SYNTAX, "test error");
225 assert!(matches!(result, Err(error) if error.code == ErrorCode::CBKP001_SYNTAX));
226 }
227
228 #[test]
229 fn test_safe_parse() -> Result<()> {
230 let parsed = safe_ops::parse_usize("123", "test")?;
231 assert_eq!(parsed, 123);
232
233 let result = safe_ops::parse_usize("invalid", "test");
234 assert!(matches!(
235 result,
236 Err(error) if error.code == ErrorCode::CBKP001_SYNTAX
237 ));
238
239 Ok(())
240 }
241
242 #[test]
243 fn test_safe_divide() -> Result<()> {
244 let quotient = safe_ops::safe_divide(10, 2, "test")?;
245 assert_eq!(quotient, 5);
246
247 let result = safe_ops::safe_divide(10, 0, "test");
248 assert!(matches!(
249 result,
250 Err(error) if error.code == ErrorCode::CBKP001_SYNTAX
251 ));
252
253 Ok(())
254 }
255}