Skip to main content

vtcode_commons/
validation.rs

1//! Validation utilities for common operations
2
3use anyhow::{Result, bail};
4use std::path::Path;
5
6/// Validate that a string is non-empty
7pub fn validate_non_empty(value: &str, field_name: &str) -> Result<()> {
8    if value.trim().is_empty() {
9        bail!("{} cannot be empty", field_name);
10    }
11    Ok(())
12}
13
14/// Validate and return non-empty string
15pub fn validate_non_empty_string(value: String, field_name: &str) -> Result<String> {
16    if value.trim().is_empty() {
17        bail!("{} cannot be empty", field_name);
18    }
19    Ok(value)
20}
21
22/// Validate optional non-empty string
23pub fn validate_optional_non_empty(value: &Option<String>, field_name: &str) -> Result<()> {
24    if let Some(v) = value {
25        validate_non_empty(v, field_name)?;
26    }
27    Ok(())
28}
29
30/// Validate collection is not empty
31pub fn validate_non_empty_collection<T>(collection: &[T], field_name: &str) -> Result<()> {
32    if collection.is_empty() {
33        bail!("{} collection cannot be empty", field_name);
34    }
35    Ok(())
36}
37
38/// Validate that all strings in a slice are non-empty
39pub fn validate_all_non_empty(values: &[String], field_name: &str) -> Result<()> {
40    for (i, value) in values.iter().enumerate() {
41        if value.trim().is_empty() {
42            bail!("{}[{}] cannot be empty", field_name, i);
43        }
44    }
45    Ok(())
46}
47
48/// Validate path exists
49pub fn validate_path_exists(path: &Path, field_name: &str) -> Result<()> {
50    if !path.exists() {
51        bail!("{} path does not exist: {}", field_name, path.display());
52    }
53    Ok(())
54}
55
56/// Validate path is a file
57pub fn validate_is_file(path: &Path, field_name: &str) -> Result<()> {
58    validate_path_exists(path, field_name)?;
59    if !path.is_file() {
60        bail!("{} is not a file: {}", field_name, path.display());
61    }
62    Ok(())
63}
64
65/// Validate path is a directory
66pub fn validate_is_directory(path: &Path, field_name: &str) -> Result<()> {
67    validate_path_exists(path, field_name)?;
68    if !path.is_dir() {
69        bail!("{} is not a directory: {}", field_name, path.display());
70    }
71    Ok(())
72}
73
74/// Basic URL format validation
75pub fn validate_url_format(url: &str, field_name: &str) -> Result<()> {
76    if !url.starts_with("http://") && !url.starts_with("https://") {
77        bail!(
78            "{} must be a valid URL starting with http:// or https://",
79            field_name
80        );
81    }
82    Ok(())
83}
84
85/// Validate alphanumeric identifier
86pub fn validate_identifier(id: &str, field_name: &str) -> Result<()> {
87    if id.is_empty() {
88        bail!("{} cannot be empty", field_name);
89    }
90    if !id
91        .chars()
92        .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
93    {
94        bail!("{} must be alphanumeric (can include _ or -)", field_name);
95    }
96    Ok(())
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102
103    #[test]
104    fn test_validate_non_empty() {
105        assert!(validate_non_empty("test", "field").is_ok());
106        assert!(validate_non_empty("", "field").is_err());
107        assert!(validate_non_empty("   ", "field").is_err());
108    }
109
110    #[test]
111    fn test_validate_all_non_empty() {
112        assert!(validate_all_non_empty(&["a".to_string(), "b".to_string()], "field").is_ok());
113        assert!(validate_all_non_empty(&["a".to_string(), "".to_string()], "field").is_err());
114        assert!(validate_all_non_empty(&[], "field").is_ok());
115    }
116}