component_shape_mcp/
names.rs1pub fn tool_name(source_module_path: &str, subject_id: &str, fallback_prefix: &str) -> String {
2 #[derive(Clone, Copy, Eq, PartialEq)]
3 enum CharKind {
4 Upper,
5 Lower,
6 Digit,
7 }
8
9 let input: Vec<char> = source_module_path
10 .chars()
11 .chain(['_'])
12 .chain(subject_id.chars())
13 .collect();
14 let mut output = String::new();
15 let mut last_was_separator = false;
16 let mut last_kind = None;
17
18 for (index, ch) in input.iter().copied().enumerate() {
19 let kind = if ch.is_ascii_uppercase() {
20 CharKind::Upper
21 } else if ch.is_ascii_lowercase() {
22 CharKind::Lower
23 } else if ch.is_ascii_digit() {
24 CharKind::Digit
25 } else if !last_was_separator && !output.is_empty() {
26 output.push('_');
27 last_was_separator = true;
28 last_kind = None;
29 continue;
30 } else {
31 last_kind = None;
32 continue;
33 };
34
35 if kind == CharKind::Upper && !last_was_separator && !output.is_empty() {
36 let next_is_lower = input
37 .get(index + 1)
38 .is_some_and(|next| next.is_ascii_lowercase());
39 if matches!(last_kind, Some(CharKind::Lower | CharKind::Digit))
40 || (matches!(last_kind, Some(CharKind::Upper)) && next_is_lower)
41 {
42 output.push('_');
43 }
44 }
45
46 if ch.is_ascii_alphanumeric() {
47 output.push(ch.to_ascii_lowercase());
48 last_was_separator = false;
49 last_kind = Some(kind);
50 }
51 }
52
53 while output.ends_with('_') {
54 output.pop();
55 }
56
57 if output.is_empty() || output.starts_with(|ch: char| ch.is_ascii_digit()) {
58 output.insert_str(0, fallback_prefix);
59 }
60
61 output
62}