sniff/
roles_python_names.rs1pub fn is_python_identifier_like(text: &str) -> bool {
2 let mut chars = text.chars();
3 match chars.next() {
4 Some(first) if first == '_' || first.is_ascii_alphabetic() => {}
5 _ => return false,
6 }
7 chars.all(|ch| ch == '_' || ch.is_ascii_alphanumeric())
8}
9
10pub fn is_python_alias_export_assignment(trimmed: &str) -> bool {
11 let Some((lhs, rhs)) = trimmed.split_once('=') else {
12 return false;
13 };
14 if trimmed.contains("==") || trimmed.contains("!=") {
15 return false;
16 }
17 let lhs = lhs.trim();
18 let rhs = rhs.trim().trim_end_matches(',').trim();
19 let target = lhs
20 .split_once(':')
21 .map(|(name, _)| name)
22 .unwrap_or(lhs)
23 .trim();
24 if !is_python_identifier_like(target) || rhs.is_empty() {
25 return false;
26 }
27 if rhs.contains('(')
28 || rhs.contains(')')
29 || rhs.contains('[')
30 || rhs.contains(']')
31 || rhs.contains('{')
32 || rhs.contains('}')
33 {
34 return false;
35 }
36 let mut saw_dot = false;
37 for part in rhs.split('.') {
38 if part.is_empty() || !is_python_identifier_like(part) {
39 return false;
40 }
41 saw_dot = saw_dot || rhs.contains('.');
42 }
43 saw_dot || is_python_identifier_like(rhs)
44}