1use entelix_core::{Error, Result};
4
5#[must_use]
11pub fn first_non_finite_vector_value(vector: &[f32]) -> Option<(usize, f32)> {
12 vector
13 .iter()
14 .copied()
15 .enumerate()
16 .find(|(_, value)| !value.is_finite())
17}
18
19pub fn validate_vector_shape(
28 surface: &str,
29 label: &str,
30 vector: &[f32],
31 expected_dimension: usize,
32) -> Result<()> {
33 if vector.len() != expected_dimension {
34 return Err(Error::invalid_request(format!(
35 "{surface}: {label} dimension {} does not match index dimension {expected_dimension}",
36 vector.len()
37 )));
38 }
39 if let Some((index, value)) = first_non_finite_vector_value(vector) {
40 return Err(Error::invalid_request(format!(
41 "{surface}: {label} contains non-finite value at index {index}: {value}"
42 )));
43 }
44 Ok(())
45}
46
47#[cfg(test)]
48mod tests {
49 use super::{first_non_finite_vector_value, validate_vector_shape};
50 use entelix_core::Error;
51
52 #[test]
53 fn first_non_finite_vector_value_reports_position_and_value() {
54 assert_eq!(first_non_finite_vector_value(&[1.0, 2.0]), None);
55 let Some((index, value)) = first_non_finite_vector_value(&[1.0, f32::INFINITY, f32::NAN])
56 else {
57 panic!("expected non-finite vector value");
58 };
59 assert_eq!(index, 1);
60 assert!(value.is_infinite());
61 }
62
63 #[test]
64 fn validate_vector_shape_rejects_dimension_mismatch_and_non_finite_values() {
65 assert!(validate_vector_shape("Store::add", "vector", &[1.0, 2.0], 2).is_ok());
66
67 let Err(err) = validate_vector_shape("Store::add", "vector", &[1.0], 2) else {
68 panic!("dimension mismatch should be rejected");
69 };
70 assert!(matches!(err, Error::InvalidRequest(msg) if msg.contains("dimension")));
71
72 let Err(err) = validate_vector_shape("Store::add", "vector", &[f32::NAN, 2.0], 2) else {
73 panic!("non-finite vector should be rejected");
74 };
75 assert!(matches!(err, Error::InvalidRequest(msg) if msg.contains("non-finite")));
76 }
77}