1pub const MAX_PROJECT_NAME_LEN: usize = 100;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub enum NameError {
5 Empty,
6 TooLong { len: usize },
7 InvalidChar { ch: char },
8}
9
10impl std::fmt::Display for NameError {
11 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12 match self {
13 NameError::Empty => write!(f, "name cannot be empty"),
14 NameError::TooLong { len } => {
15 write!(f, "name too long: {len} chars (max {MAX_PROJECT_NAME_LEN})")
16 }
17 NameError::InvalidChar { ch } => write!(
18 f,
19 "name contains invalid character {ch:?}; allowed: letters, digits, '.', '_', '-'"
20 ),
21 }
22 }
23}
24
25impl std::error::Error for NameError {}
26
27pub fn validate_project_name(s: &str) -> std::result::Result<(), NameError> {
28 let len = s.chars().count();
29 if len == 0 {
30 return Err(NameError::Empty);
31 }
32 if len > MAX_PROJECT_NAME_LEN {
33 return Err(NameError::TooLong { len });
34 }
35 if let Some(ch) = s
36 .chars()
37 .find(|c| !c.is_ascii_alphanumeric() && *c != '.' && *c != '_' && *c != '-')
38 {
39 return Err(NameError::InvalidChar { ch });
40 }
41 Ok(())
42}
43
44pub fn is_valid_project_name(s: &str) -> bool {
45 validate_project_name(s).is_ok()
46}