1const WINDOWS_DEVICES: [&str; 22] = [
8 "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8",
9 "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9",
10];
11
12pub fn check_name(name: &str) -> Result<(), String> {
16 let Some(first) = name.chars().next() else {
17 return Err("a package name cannot be empty".to_string());
18 };
19 if !first.is_ascii_alphabetic() {
20 return Err(format!(
21 "package name `{name}` must start with an ASCII letter"
22 ));
23 }
24 if let Some(bad) = name
25 .chars()
26 .find(|c| !(c.is_ascii_alphanumeric() || *c == '-' || *c == '_'))
27 {
28 return Err(format!(
29 "package name `{name}` contains {bad:?}; a name may use only ASCII letters, \
30 digits, `-` and `_`"
31 ));
32 }
33 if name.len() > 64 {
34 return Err(format!(
35 "package name `{name}` is {} characters long; the limit is 64",
36 name.len()
37 ));
38 }
39 if WINDOWS_DEVICES.contains(&name.to_ascii_lowercase().as_str()) {
40 return Err(format!("`{name}` is a name Windows reserves for a device"));
41 }
42 Ok(())
43}
44
45pub fn is_portable(component: &str) -> bool {
54 if component.is_empty() || component == "." || component == ".." {
55 return false;
56 }
57 if component
58 .chars()
59 .any(|c| c.is_control() || "/:\\<>\"|?*".contains(c))
60 {
61 return false;
62 }
63 if component.ends_with('.') || component.ends_with(' ') {
64 return false;
65 }
66 let stem = component.split('.').next().unwrap_or(component);
67 !WINDOWS_DEVICES.contains(&stem.to_ascii_lowercase().as_str())
68}
69
70pub fn import_name(name: &str) -> String {
74 name.replace('-', "_")
75}