9bf9e539599a51298a4e44623d656a3b1aa6675d
[govpp.git] / cmd / binapi-generator / definitions.go
1 // Copyright (c) 2018 Cisco and/or its affiliates.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at:
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 package main
16
17 import (
18         "strings"
19         "unicode"
20 )
21
22 // commonInitialisms is a set of common initialisms that need to stay in upper case.
23 var commonInitialisms = map[string]bool{
24         "ACL": true,
25         "API": true,
26         //"ASCII": true, // there are only two use cases for ASCII which already have initialism before and after
27         "CPU":   true,
28         "CSS":   true,
29         "DNS":   true,
30         "DHCP":  true,
31         "EOF":   true,
32         "GUID":  true,
33         "HTML":  true,
34         "HTTP":  true,
35         "HTTPS": true,
36         "ID":    true,
37         "IP":    true,
38         "ICMP":  true,
39         "JSON":  true,
40         "LHS":   true,
41         "QPS":   true,
42         "PID":   true,
43         "RAM":   true,
44         "RHS":   true,
45         "RPC":   true,
46         "SLA":   true,
47         "SMTP":  true,
48         "SQL":   true,
49         "SSH":   true,
50         "TCP":   true,
51         "TLS":   true,
52         "TTL":   true,
53         "UDP":   true,
54         "UI":    true,
55         "UID":   true,
56         "UUID":  true,
57         "URI":   true,
58         "URL":   true,
59         "UTF8":  true,
60         "VM":    true,
61         "VPN":   true,
62         "XML":   true,
63         "XMPP":  true,
64         "XSRF":  true,
65         "XSS":   true,
66 }
67
68 // specialInitialisms is a set of special initialisms that need part to stay in upper case.
69 var specialInitialisms = map[string]string{
70         "IPV": "IPv",
71         //"IPV4": "IPv4",
72         //"IPV6": "IPv6",
73 }
74
75 func usesInitialism(s string) string {
76         if u := strings.ToUpper(s); commonInitialisms[u] {
77                 return u
78         } else if su, ok := specialInitialisms[u]; ok {
79                 return su
80         }
81         return ""
82 }
83
84 // camelCaseName returns correct name identifier (camelCase).
85 func camelCaseName(name string) (should string) {
86         name = strings.Title(name)
87
88         // Fast path for simple cases: "_" and all lowercase.
89         if name == "_" {
90                 return name
91         }
92         allLower := true
93         for _, r := range name {
94                 if !unicode.IsLower(r) {
95                         allLower = false
96                         break
97                 }
98         }
99         if allLower {
100                 return name
101         }
102
103         // Split camelCase at any lower->upper transition, and split on underscores.
104         // Check each word for common initialisms.
105         runes := []rune(name)
106         w, i := 0, 0 // index of start of word, scan
107         for i+1 <= len(runes) {
108                 eow := false // whether we hit the end of a word
109                 if i+1 == len(runes) {
110                         eow = true
111                 } else if runes[i+1] == '_' {
112                         // underscore; shift the remainder forward over any run of underscores
113                         eow = true
114                         n := 1
115                         for i+n+1 < len(runes) && runes[i+n+1] == '_' {
116                                 n++
117                         }
118
119                         // Leave at most one underscore if the underscore is between two digits
120                         if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
121                                 n--
122                         }
123
124                         copy(runes[i+1:], runes[i+n+1:])
125                         runes = runes[:len(runes)-n]
126                 } else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
127                         // lower->non-lower
128                         eow = true
129                 }
130                 i++
131                 if !eow {
132                         continue
133                 }
134
135                 // [w,i) is a word.
136                 word := string(runes[w:i])
137                 if u := usesInitialism(word); u != "" {
138                         // Keep consistent case, which is lowercase only at the start.
139                         if w == 0 && unicode.IsLower(runes[w]) {
140                                 u = strings.ToLower(u)
141                         }
142                         // All the common initialisms are ASCII,
143                         // so we can replace the bytes exactly.
144                         copy(runes[w:], []rune(u))
145                 } else if w > 0 && strings.ToLower(word) == word {
146                         // already all lowercase, and not the first word, so uppercase the first character.
147                         runes[w] = unicode.ToUpper(runes[w])
148                 }
149                 w = i
150         }
151         return string(runes)
152 }