#![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>;
#[inline]
#[must_use = "Handle the Result or propagate the error"]
pub fn parse_usize(s: &str, context: &str) -> Result<usize> {
s.parse().map_err(|_| {
Error::new(
ErrorCode::CBKP001_SYNTAX,
format!("Invalid numeric value '{s}' in {context}"),
)
})
}
#[inline]
#[must_use = "Handle the Result or propagate the error"]
pub fn parse_isize(s: &str, context: &str) -> Result<isize> {
s.parse().map_err(|_| {
Error::new(
ErrorCode::CBKP001_SYNTAX,
format!("Invalid signed numeric value '{s}' in {context}"),
)
})
}
#[inline]
#[must_use = "Handle the Result or propagate the error"]
pub fn safe_parse_u16(s: &str, context: &str) -> Result<u16> {
s.parse().map_err(|_| {
Error::new(
ErrorCode::CBKP001_SYNTAX,
format!("Invalid u16 value '{s}' in {context}"),
)
})
}
#[inline]
#[must_use = "Handle the Result or propagate the error"]
pub fn safe_string_char_at(s: &str, index: usize, context: &str) -> Result<char> {
s.chars().nth(index).ok_or_else(|| {
Error::new(
ErrorCode::CBKP001_SYNTAX,
format!(
"String character access out of bounds in {context}: index {index} >= length {}",
s.len()
),
)
})
}
#[inline]
#[must_use = "Handle the Result or propagate the error"]
pub fn safe_write(buffer: &mut String, args: std::fmt::Arguments<'_>) -> Result<()> {
use std::fmt::Write;
buffer.write_fmt(args).map_err(|e| {
Error::new(
ErrorCode::CBKD101_INVALID_FIELD_TYPE,
format!("String formatting error: {e}"),
)
})
}
#[inline]
#[must_use = "Handle the Result or propagate the error"]
pub fn safe_write_str(buffer: &mut String, s: &str) -> Result<()> {
use std::fmt::Write;
buffer.write_str(s).map_err(|e| {
Error::new(
ErrorCode::CBKD101_INVALID_FIELD_TYPE,
format!("String write error: {e}"),
)
})
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used)]
mod tests {
use super::*;
#[test]
fn parse_usize_ok() {
assert_eq!(parse_usize("123", "test").expect("parse usize"), 123);
}
#[test]
fn parse_usize_err() {
assert!(matches!(
parse_usize("invalid", "test"),
Err(error) if error.code == ErrorCode::CBKP001_SYNTAX
));
}
#[test]
fn parse_isize_ok() {
assert_eq!(parse_isize("-42", "test").expect("parse isize"), -42);
}
#[test]
fn safe_parse_u16_ok_and_err() {
assert_eq!(safe_parse_u16("42", "test").expect("parse u16"), 42);
assert!(matches!(
safe_parse_u16("99999", "test"),
Err(error) if error.code == ErrorCode::CBKP001_SYNTAX
));
}
#[test]
fn safe_string_char_at_ok() {
assert_eq!(
safe_string_char_at("abc", 1, "test").expect("char index"),
'b'
);
}
#[test]
fn safe_string_char_at_err() {
assert!(matches!(
safe_string_char_at("abc", 3, "test"),
Err(error) if error.code == ErrorCode::CBKP001_SYNTAX
));
}
#[test]
fn test_safe_write_basic() {
let mut buf = String::new();
safe_write(&mut buf, format_args!("hello {}", 42)).unwrap();
assert_eq!(buf, "hello 42");
}
#[test]
fn test_safe_write_empty_format() {
let mut buf = String::new();
safe_write(&mut buf, format_args!("")).unwrap();
assert_eq!(buf, "");
}
#[test]
fn test_safe_write_append() {
let mut buf = String::from("prefix:");
safe_write(&mut buf, format_args!("value")).unwrap();
assert_eq!(buf, "prefix:value");
}
#[test]
fn test_safe_write_str_basic() {
let mut buf = String::new();
safe_write_str(&mut buf, "hello").unwrap();
assert_eq!(buf, "hello");
}
#[test]
fn test_safe_write_str_empty() {
let mut buf = String::new();
safe_write_str(&mut buf, "").unwrap();
assert_eq!(buf, "");
}
#[test]
fn test_safe_write_str_append() {
let mut buf = String::from("first");
safe_write_str(&mut buf, " second").unwrap();
assert_eq!(buf, "first second");
}
#[test]
fn test_safe_write_str_unicode() {
let mut buf = String::new();
safe_write_str(&mut buf, "日本語").unwrap();
assert_eq!(buf, "日本語");
}
#[test]
fn parse_usize_zero() {
assert_eq!(parse_usize("0", "test").unwrap(), 0);
}
#[test]
fn parse_usize_whitespace_err() {
assert!(parse_usize(" 123", "test").is_err());
}
#[test]
fn parse_usize_negative_err() {
assert!(parse_usize("-1", "test").is_err());
}
#[test]
fn parse_isize_zero() {
assert_eq!(parse_isize("0", "test").unwrap(), 0);
}
#[test]
fn parse_isize_positive() {
assert_eq!(parse_isize("42", "test").unwrap(), 42);
}
#[test]
fn parse_isize_empty_err() {
assert!(parse_isize("", "test").is_err());
}
#[test]
fn safe_parse_u16_zero() {
assert_eq!(safe_parse_u16("0", "test").unwrap(), 0);
}
#[test]
fn safe_parse_u16_max() {
assert_eq!(safe_parse_u16("65535", "test").unwrap(), u16::MAX);
}
#[test]
fn safe_parse_u16_negative_err() {
assert!(safe_parse_u16("-1", "test").is_err());
}
#[test]
fn safe_string_char_at_empty_string() {
assert!(safe_string_char_at("", 0, "test").is_err());
}
#[test]
fn safe_string_char_at_first_char() {
assert_eq!(safe_string_char_at("x", 0, "test").unwrap(), 'x');
}
#[test]
fn safe_string_char_at_unicode() {
assert_eq!(safe_string_char_at("日本", 1, "test").unwrap(), '本');
}
}