allframe_forge/
validation.rs

1//! Project name validation
2//!
3//! Validates that project names follow Rust package naming conventions.
4
5use anyhow::Result;
6
7/// Validate project name (must be valid Rust identifier)
8///
9/// # Rules
10/// - No spaces allowed
11/// - Cannot start with a number
12/// - Only alphanumeric characters, underscores, and hyphens allowed
13///
14/// # Examples
15/// ```
16/// # use allframe_forge::validation::validate_project_name;
17/// assert!(validate_project_name("my-project").is_ok());
18/// assert!(validate_project_name("my_project").is_ok());
19/// assert!(validate_project_name("myproject123").is_ok());
20///
21/// assert!(validate_project_name("my project").is_err());
22/// assert!(validate_project_name("123project").is_err());
23/// assert!(validate_project_name("my@project").is_err());
24/// ```
25pub fn validate_project_name(name: &str) -> Result<()> {
26    // Check for spaces
27    if name.contains(' ') {
28        anyhow::bail!("Invalid project name: project names cannot contain spaces");
29    }
30
31    // Check if it starts with a number
32    if name.chars().next().is_some_and(|c| c.is_ascii_digit()) {
33        anyhow::bail!("Invalid project name: project names cannot start with a number");
34    }
35
36    // Check if it's a valid Rust identifier (alphanumeric + underscore + hyphen)
37    if !name
38        .chars()
39        .all(|c| c.is_alphanumeric() || c == '_' || c == '-')
40    {
41        anyhow::bail!(
42            "Invalid project name: only alphanumeric characters, underscores, and hyphens are \
43             allowed"
44        );
45    }
46
47    Ok(())
48}
49
50#[cfg(test)]
51mod tests {
52    use super::*;
53
54    #[test]
55    fn test_valid_names() {
56        assert!(validate_project_name("my-project").is_ok());
57        assert!(validate_project_name("my_project").is_ok());
58        assert!(validate_project_name("myproject").is_ok());
59        assert!(validate_project_name("my-project-123").is_ok());
60    }
61
62    #[test]
63    fn test_invalid_spaces() {
64        let result = validate_project_name("my project");
65        assert!(result.is_err());
66        assert!(result.unwrap_err().to_string().contains("spaces"));
67    }
68
69    #[test]
70    fn test_invalid_starts_with_number() {
71        let result = validate_project_name("123project");
72        assert!(result.is_err());
73        assert!(result.unwrap_err().to_string().contains("number"));
74    }
75
76    #[test]
77    fn test_invalid_special_chars() {
78        assert!(validate_project_name("my@project").is_err());
79        assert!(validate_project_name("my$project").is_err());
80        assert!(validate_project_name("my project!").is_err());
81    }
82}