1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use proc_macro2::{Span, TokenStream};
use std::str::FromStr;
use syn::{parse2, Ident};
use witx::Id;

pub(crate) trait ToIdent {
    fn to_ident(&self) -> Ident;
    fn to_ident_native(&self, prefix: Option<&str>) -> Ident;
    fn to_ident_upper(&self) -> Ident;
}

impl ToIdent for str {
    fn to_ident(&self) -> Ident {
        let mut out_buf;

        let out = match self {
            "2big" => "TooBig", // identifiers must not start with a digit
            _ => {
                let mut upper = true;
                out_buf = String::with_capacity(self.len() + 2);

                for c in self.chars() {
                    if c == '_' {
                        upper = true;
                    } else if upper {
                        let c_upper = c.to_uppercase();
                        out_buf.extend(c_upper);
                        upper = false;
                    } else {
                        out_buf.push(c);
                    }
                }
                &out_buf
            }
        };

        Ident::new(out, Span::call_site())
    }

    fn to_ident_native(&self, prefix: Option<&str>) -> Ident {
        let mut name_buf;

        let mut name = match prefix {
            Some(prefix) => {
                name_buf = format!("{}_{}", prefix, self);
                &name_buf
            }
            None => self,
        };

        if is_keyword(name) {
            name_buf = format!("r#{}", name);
            name = &name_buf;
        }

        let stream = TokenStream::from_str(name).expect("Could not parse identifier");
        parse2(stream).expect(&format!("Could not create identifier"))
    }

    fn to_ident_upper(&self) -> Ident {
        Ident::new(&self.to_uppercase(), Span::call_site())
    }
}

impl ToIdent for Id {
    fn to_ident(&self) -> Ident {
        self.as_str().to_ident()
    }

    fn to_ident_native(&self, prefix: Option<&str>) -> Ident {
        self.as_str().to_ident_native(prefix)
    }

    fn to_ident_upper(&self) -> Ident {
        self.as_str().to_ident_upper()
    }
}

fn is_keyword(id: &str) -> bool {
    id == "type" || id == "in"
}