algosul_core/codegen/
ident.rs1use std::{
2 borrow::Borrow,
3 ffi::{CStr, CString, OsStr, OsString},
4 str::FromStr,
5};
6
7use unicode_xid::UnicodeXID;
8pub trait StrExt {
9 type Owned: Borrow<Self>;
10 fn is_valid_ident(&self) -> bool;
11 fn to_valid_ident(&self) -> Self::Owned;
12}
13impl StrExt for str {
14 type Owned = String;
15
16 fn is_valid_ident(&self) -> bool {
17 let mut chars = self.chars();
18 match chars.next() {
19 Some(c) if c.is_xid_start() || c == '_' => {
20 chars.all(|c| c.is_xid_continue())
21 }
22 _ => false,
23 }
24 }
25
26 fn to_valid_ident(&self) -> Self::Owned {
27 let mut chars = self.chars();
28 let mut buffer = String::new();
29 let first = chars.next();
30 let first = first
31 .filter(|&c| {
32 if c.is_ascii_digit() {
33 buffer.push('_');
34 return true;
35 };
36 c.is_xid_start()
37 })
38 .unwrap_or('_');
39 buffer.push(first);
40 chars.for_each(|c| {
41 if c.is_xid_continue() { buffer.push(c) } else { buffer.push('_') }
42 });
43 buffer
44 }
45}
46impl StrExt for OsStr {
47 type Owned = OsString;
48
49 fn is_valid_ident(&self) -> bool {
50 self.to_str().map(str::is_valid_ident).unwrap_or(false)
51 }
52
53 fn to_valid_ident(&self) -> Self::Owned {
54 OsString::from(self.to_string_lossy().to_valid_ident())
55 }
56}
57impl StrExt for CStr {
58 type Owned = CString;
59
60 fn is_valid_ident(&self) -> bool {
61 self.to_str().map(str::is_valid_ident).unwrap_or(false)
62 }
63
64 fn to_valid_ident(&self) -> Self::Owned {
65 CString::from_str(&self.to_string_lossy().to_valid_ident()).unwrap()
66 }
67}
68#[cfg(test)]
69mod tests {
70 use crate::codegen::ident::StrExt;
71 #[test]
72 fn test_is_valid_ident() {
73 let valids = ["_", "_abc", "_123", "abc_", "hello_world"];
74 for s in valids {
75 assert!(s.is_valid_ident(), "{s:?} should be valid");
76 }
77 assert!("_".is_valid_ident());
78 assert!("_abc".is_valid_ident());
79 assert!("_123".is_valid_ident());
80 assert!("abc".is_valid_ident());
81 let invalids = ["", "0", " ", "_0123-", "-", "!hello", "hello-world"];
82 for s in invalids {
83 assert!(!s.is_valid_ident(), "{s:?} should not be valid");
84 }
85 }
86}