Skip to main content

component_shape_mcp/
names.rs

1pub 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}