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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use proc_macro2::{Ident, Span, TokenStream};
use quote::{quote, ToTokens};

pub(crate) fn to_doc_attr(text: &str) -> TokenStream {
    let text = text.lines().map(str::trim).collect::<Vec<_>>().join("\n");
    let text = text.trim();

    quote!(#[doc = #text])
}

pub(crate) fn description_to_doc_attr((short, long): &(String, String)) -> TokenStream {
    to_doc_attr(&format!("{}\n\n{}", short, long))
}

pub fn is_keyword(txt: &str) -> bool {
    matches!(
        txt,
        "abstract"
            | "alignof"
            | "as"
            | "become"
            | "box"
            | "break"
            | "const"
            | "continue"
            | "crate"
            | "do"
            | "else"
            | "enum"
            | "extern"
            | "false"
            | "final"
            | "fn"
            | "for"
            | "if"
            | "impl"
            | "in"
            | "let"
            | "loop"
            | "macro"
            | "match"
            | "mod"
            | "move"
            | "mut"
            | "offsetof"
            | "override"
            | "priv"
            | "proc"
            | "pub"
            | "pure"
            | "ref"
            | "return"
            | "Self"
            | "self"
            | "sizeof"
            | "static"
            | "struct"
            | "super"
            | "trait"
            | "true"
            | "type"
            | "typeof"
            | "unsafe"
            | "unsized"
            | "use"
            | "virtual"
            | "where"
            | "while"
            | "yield"
            | "__handler"
            | "__object"
    )
}

pub fn is_camel_keyword(txt: &str) -> bool {
    matches!(txt, "Self")
}

pub fn snake_to_camel(input: &str) -> String {
    let result = input
        .split('_')
        .flat_map(|s| {
            let mut first = true;
            s.chars().map(move |c| {
                if first {
                    first = false;
                    c.to_ascii_uppercase()
                } else {
                    c
                }
            })
        })
        .collect::<String>();

    if is_camel_keyword(&result) {
        format!("_{}", &result)
    } else {
        result
    }
}

pub fn dotted_to_relname(input: &str) -> TokenStream {
    let mut it = input.split('.');
    match (it.next(), it.next()) {
        (Some(module), Some(name)) => {
            let module = Ident::new(module, Span::call_site());
            let ident = Ident::new(&snake_to_camel(name), Span::call_site());
            quote::quote!(super::#module::#ident)
        }
        (Some(name), None) => {
            Ident::new(&snake_to_camel(name), Span::call_site()).into_token_stream()
        }
        _ => unreachable!(),
    }
}