#[inline]
pub fn assert_non_empty<T>(name: &str, slice: &[T]) -> crate::Result<()> {
if slice.is_empty() {
return Err(crate::TechnicalIndicatorError::EmptyData {
name: name.to_string(),
});
}
Ok(())
}
#[inline]
pub fn assert_same_len<T>(slices: &[(&str, &[T])]) -> crate::Result<()> {
if slices.is_empty() {
return Ok(());
}
let expected_len = slices[0].1.len();
for (_name, slice) in slices {
let len = slice.len();
if len != expected_len {
let names: Vec<(String, usize)> = slices
.iter()
.map(|(n, s)| (n.to_string(), s.len()))
.collect();
return Err(crate::TechnicalIndicatorError::MismatchedLength { names });
}
}
Ok(())
}
#[inline]
pub fn assert_period(period: usize, data_len: usize) -> crate::Result<()> {
if period == 0 {
return Err(crate::TechnicalIndicatorError::InvalidPeriod {
period,
data_len,
reason: "must be greater than 0".to_string(),
});
}
if period > data_len {
return Err(crate::TechnicalIndicatorError::InvalidPeriod {
period,
data_len,
reason: "cannot be longer than data length".to_string(),
});
}
Ok(())
}
#[inline]
pub fn assert_positive(name: &str, value: f64) -> crate::Result<()> {
if value <= 0.0 || value.is_nan() {
return Err(crate::TechnicalIndicatorError::InvalidValue {
name: name.to_string(),
value,
reason: "must be greater than 0".to_string(),
});
}
Ok(())
}
#[inline]
pub fn assert_min_value(name: &str, value: f64, min: f64) -> crate::Result<()> {
if value <= min || value.is_nan() {
return Err(crate::TechnicalIndicatorError::InvalidValue {
name: name.to_string(),
value,
reason: format!("must be greater than {}", min),
});
}
Ok(())
}
#[inline]
pub fn assert_min_period(period: usize, min_period: usize, data_len: usize) -> crate::Result<()> {
if period < min_period {
return Err(crate::TechnicalIndicatorError::InvalidPeriod {
period,
data_len,
reason: format!("must be at least {}", min_period),
});
}
assert_period(period, data_len)?;
Ok(())
}
#[inline]
pub fn assert_min_length(name: &str, min_length: usize, actual_length: usize) -> crate::Result<()> {
if actual_length < min_length {
return Err(crate::TechnicalIndicatorError::InvalidPeriod {
period: min_length,
data_len: actual_length,
reason: format!("{} must be at least {} in length", name, min_length),
});
}
Ok(())
}
#[inline]
pub fn assert_positive_usize(name: &str, value: usize) -> crate::Result<()> {
if value == 0 {
return Err(crate::TechnicalIndicatorError::InvalidValue {
name: name.to_string(),
value: value as f64,
reason: "must be greater than 0".to_string(),
});
}
Ok(())
}
#[inline]
pub fn unsupported_type(type_name: &str) -> crate::TechnicalIndicatorError {
crate::TechnicalIndicatorError::UnsupportedType {
type_name: type_name.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_assert_non_empty_ok() {
let non_empty = vec![1.0, 2.0, 3.0];
assert!(assert_non_empty("prices", &non_empty).is_ok());
}
#[test]
fn test_assert_non_empty_fail() {
let empty: Vec<f64> = vec![];
let result = assert_non_empty("prices", &empty);
assert!(result.is_err());
match result {
Err(crate::TechnicalIndicatorError::EmptyData { name }) => {
assert_eq!(name, "prices");
}
_ => panic!("Expected EmptyData error"),
}
}
#[test]
fn test_assert_same_len_ok() {
let a = vec![1.0, 2.0, 3.0];
let b = vec![4.0, 5.0, 6.0];
assert!(assert_same_len(&[("a", &a), ("b", &b)]).is_ok());
}
#[test]
fn test_assert_same_len_fail() {
let a = vec![1.0, 2.0, 3.0];
let c = vec![7.0, 8.0];
let result = assert_same_len(&[("a", &a), ("c", &c)]);
assert!(result.is_err());
match result {
Err(crate::TechnicalIndicatorError::MismatchedLength { names }) => {
assert_eq!(names.len(), 2);
}
_ => panic!("Expected MismatchedLength error"),
}
}
#[test]
fn test_assert_period_ok() {
assert!(assert_period(5, 10).is_ok());
}
#[test]
fn test_assert_period_zero() {
let result = assert_period(0, 10);
assert!(result.is_err());
match result {
Err(crate::TechnicalIndicatorError::InvalidPeriod { period, .. }) => {
assert_eq!(period, 0);
}
_ => panic!("Expected InvalidPeriod error"),
}
}
#[test]
fn test_assert_period_too_long() {
let result = assert_period(11, 10);
assert!(result.is_err());
match result {
Err(crate::TechnicalIndicatorError::InvalidPeriod {
period, data_len, ..
}) => {
assert_eq!(period, 11);
assert_eq!(data_len, 10);
}
_ => panic!("Expected InvalidPeriod error"),
}
}
#[test]
fn test_assert_positive_ok() {
assert!(assert_positive("value", 1.0).is_ok());
}
#[test]
fn test_assert_positive_zero() {
let result = assert_positive("value", 0.0);
assert!(result.is_err());
match result {
Err(crate::TechnicalIndicatorError::InvalidValue { name, value, .. }) => {
assert_eq!(name, "value");
assert_eq!(value, 0.0);
}
_ => panic!("Expected InvalidValue error"),
}
}
#[test]
fn test_assert_min_period_ok() {
assert!(assert_min_period(4, 4, 10).is_ok());
}
#[test]
fn test_assert_min_period_too_small() {
let result = assert_min_period(3, 4, 10);
assert!(result.is_err());
match result {
Err(crate::TechnicalIndicatorError::InvalidPeriod { period, .. }) => {
assert_eq!(period, 3);
}
_ => panic!("Expected InvalidPeriod error"),
}
}
}