#![cfg_attr(not(test), deny(clippy::unwrap_used, clippy::expect_used))]
use copybook_error::{Error, ErrorCode};
pub type Result<T> = std::result::Result<T, Error>;
pub trait OptionExt<T> {
fn ok_or_cbkp_error(self, code: ErrorCode, message: impl Into<String>) -> Result<T>;
fn ok_or_error(self, error: Error) -> Result<T>;
}
impl<T> OptionExt<T> for Option<T> {
#[allow(clippy::inline_always)]
#[inline]
fn ok_or_cbkp_error(self, code: ErrorCode, message: impl Into<String>) -> Result<T> {
match self {
Some(value) => Ok(value),
None => {
Err(Error::new(code, message.into()))
}
}
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn ok_or_error(self, error: Error) -> Result<T> {
match self {
Some(value) => Ok(value),
None => Err(error),
}
}
}
pub trait VecExt<T> {
fn pop_or_cbkp_error(&mut self, code: ErrorCode, message: impl Into<String>) -> Result<T>;
fn last_or_cbkp_error(&self, code: ErrorCode, message: impl Into<String>) -> Result<&T>;
fn last_mut_or_cbkp_error(
&mut self,
code: ErrorCode,
message: impl Into<String>,
) -> Result<&mut T>;
}
impl<T> VecExt<T> for Vec<T> {
#[allow(clippy::inline_always)]
#[inline(always)]
fn pop_or_cbkp_error(&mut self, code: ErrorCode, message: impl Into<String>) -> Result<T> {
match self.pop() {
Some(value) => Ok(value),
None => Err(Error::new(code, message.into())),
}
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn last_or_cbkp_error(&self, code: ErrorCode, message: impl Into<String>) -> Result<&T> {
#[allow(clippy::if_not_else)]
if !self.is_empty() {
Ok(&self[self.len() - 1])
} else {
Err(Error::new(code, message.into()))
}
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn last_mut_or_cbkp_error(
&mut self,
code: ErrorCode,
message: impl Into<String>,
) -> Result<&mut T> {
#[allow(clippy::if_not_else)]
if !self.is_empty() {
let len = self.len();
Ok(&mut self[len - 1])
} else {
Err(Error::new(code, message.into()))
}
}
}
pub trait SliceExt<T> {
fn get_or_cbkp_error(
&self,
index: usize,
code: ErrorCode,
message: impl Into<String>,
) -> Result<&T>;
fn get_mut_or_cbkp_error(
&mut self,
index: usize,
code: ErrorCode,
message: impl Into<String>,
) -> Result<&mut T>;
}
impl<T> SliceExt<T> for [T] {
#[allow(clippy::inline_always)]
#[inline(always)]
fn get_or_cbkp_error(
&self,
index: usize,
code: ErrorCode,
message: impl Into<String>,
) -> Result<&T> {
if index < self.len() {
Ok(&self[index])
} else {
Err(Error::new(code, message.into()))
}
}
#[allow(clippy::inline_always)]
#[inline(always)]
fn get_mut_or_cbkp_error(
&mut self,
index: usize,
code: ErrorCode,
message: impl Into<String>,
) -> Result<&mut T> {
if index < self.len() {
Ok(&mut self[index])
} else {
Err(Error::new(code, message.into()))
}
}
}
pub mod safe_ops {
pub use copybook_safe_ops::*;
}
#[cfg(test)]
#[allow(clippy::expect_used)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::{OptionExt, VecExt, safe_ops};
use copybook_error::{ErrorCode, Result};
#[test]
fn test_option_ext_some() -> Result<()> {
let opt = Some(42);
let value = opt.ok_or_cbkp_error(ErrorCode::CBKP001_SYNTAX, "test")?;
assert_eq!(value, 42);
Ok(())
}
#[test]
fn test_option_ext_none() {
let opt: Option<i32> = None;
let result = opt.ok_or_cbkp_error(ErrorCode::CBKP001_SYNTAX, "test error");
assert!(matches!(
result,
Err(error) if error.code == ErrorCode::CBKP001_SYNTAX
));
}
#[test]
fn test_vec_ext_pop() -> Result<()> {
let mut vec = vec![1, 2, 3];
let value = vec.pop_or_cbkp_error(ErrorCode::CBKP001_SYNTAX, "test")?;
assert_eq!(value, 3);
Ok(())
}
#[test]
fn test_vec_ext_pop_empty() {
let mut empty_vec: Vec<i32> = vec![];
let result = empty_vec.pop_or_cbkp_error(ErrorCode::CBKP001_SYNTAX, "test error");
assert!(matches!(result, Err(error) if error.code == ErrorCode::CBKP001_SYNTAX));
}
#[test]
fn test_safe_parse() -> Result<()> {
let parsed = safe_ops::parse_usize("123", "test")?;
assert_eq!(parsed, 123);
let result = safe_ops::parse_usize("invalid", "test");
assert!(matches!(
result,
Err(error) if error.code == ErrorCode::CBKP001_SYNTAX
));
Ok(())
}
#[test]
fn test_safe_divide() -> Result<()> {
let quotient = safe_ops::safe_divide(10, 2, "test")?;
assert_eq!(quotient, 5);
let result = safe_ops::safe_divide(10, 0, "test");
assert!(matches!(
result,
Err(error) if error.code == ErrorCode::CBKP001_SYNTAX
));
Ok(())
}
}