toon_format/decode/
validation.rs

1use crate::types::{
2    ToonError,
3    ToonResult,
4};
5
6/// Validate that array length matches expected value (strict mode only).
7pub fn validate_array_length(expected: usize, actual: usize, strict: bool) -> ToonResult<()> {
8    if strict && expected != actual {
9        return Err(ToonError::length_mismatch(expected, actual));
10    }
11    Ok(())
12}
13
14/// Validate field list for tabular arrays (no duplicates, non-empty names).
15pub fn validate_field_list(fields: &[String]) -> ToonResult<()> {
16    if fields.is_empty() {
17        return Err(ToonError::InvalidInput(
18            "Field list cannot be empty for tabular arrays".to_string(),
19        ));
20    }
21
22    for i in 0..fields.len() {
23        for j in (i + 1)..fields.len() {
24            if fields[i] == fields[j] {
25                return Err(ToonError::InvalidInput(format!(
26                    "Duplicate field name: '{}'",
27                    fields[i]
28                )));
29            }
30        }
31    }
32
33    for field in fields {
34        if field.is_empty() {
35            return Err(ToonError::InvalidInput(
36                "Field name cannot be empty".to_string(),
37            ));
38        }
39    }
40
41    Ok(())
42}
43
44/// Validate that a tabular row has the expected number of values.
45pub fn validate_row_length(
46    row_index: usize,
47    expected_fields: usize,
48    actual_values: usize,
49) -> ToonResult<()> {
50    if expected_fields != actual_values {
51        return Err(ToonError::InvalidStructure(format!(
52            "Row {row_index} has {actual_values} values but expected {expected_fields} fields"
53        )));
54    }
55    Ok(())
56}
57
58/// Validate that detected and expected delimiters match.
59pub fn validate_delimiter_consistency(
60    detected: Option<crate::types::Delimiter>,
61    expected: Option<crate::types::Delimiter>,
62) -> ToonResult<()> {
63    if let (Some(detected), Some(expected)) = (detected, expected) {
64        if detected != expected {
65            return Err(ToonError::InvalidDelimiter(format!(
66                "Detected delimiter {detected} but expected {expected}"
67            )));
68        }
69    }
70    Ok(())
71}
72
73#[cfg(test)]
74mod tests {
75    use super::*;
76
77    #[test]
78    fn test_validate_array_length() {
79        assert!(validate_array_length(5, 3, false).is_ok());
80        assert!(validate_array_length(3, 5, false).is_ok());
81
82        assert!(validate_array_length(5, 5, true).is_ok());
83        assert!(validate_array_length(5, 3, true).is_err());
84        assert!(validate_array_length(3, 5, true).is_err());
85    }
86
87    #[test]
88    fn test_validate_field_list() {
89        assert!(validate_field_list(&["id".to_string(), "name".to_string()]).is_ok());
90        assert!(validate_field_list(&["field1".to_string()]).is_ok());
91
92        assert!(validate_field_list(&[]).is_err());
93
94        assert!(
95            validate_field_list(&["id".to_string(), "name".to_string(), "id".to_string()]).is_err()
96        );
97
98        assert!(
99            validate_field_list(&["id".to_string(), "".to_string(), "name".to_string()]).is_err()
100        );
101    }
102
103    #[test]
104    fn test_validate_row_length() {
105        assert!(validate_row_length(0, 3, 3).is_ok());
106        assert!(validate_row_length(1, 5, 5).is_ok());
107
108        assert!(validate_row_length(0, 3, 2).is_err());
109        assert!(validate_row_length(1, 3, 4).is_err());
110    }
111
112    #[test]
113    fn test_validate_delimiter_consistency() {
114        use crate::types::Delimiter;
115
116        assert!(
117            validate_delimiter_consistency(Some(Delimiter::Comma), Some(Delimiter::Comma)).is_ok()
118        );
119
120        assert!(
121            validate_delimiter_consistency(Some(Delimiter::Comma), Some(Delimiter::Pipe)).is_err()
122        );
123
124        assert!(validate_delimiter_consistency(None, Some(Delimiter::Comma)).is_ok());
125        assert!(validate_delimiter_consistency(Some(Delimiter::Comma), None).is_ok());
126        assert!(validate_delimiter_consistency(None, None).is_ok());
127    }
128}