Optimize socketclient adapter and add various code improvements
[govpp.git] / binapigen / definitions.go
1 //  Copyright (c) 2020 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 binapigen
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         // NOTE: There are only two occurences of the word 'ascii' and
27         // these already have initialism before and after ASCII part,
28         // thus disabling initialism for this case.
29         "ASCII": false,
30         "CPU":   true,
31         "CSS":   true,
32         "DNS":   true,
33         "DHCP":  true,
34         "EOF":   true,
35         "GUID":  true,
36         "HTML":  true,
37         "HTTP":  true,
38         "HTTPS": true,
39         "ID":    true,
40         "IP":    true,
41         "ICMP":  true,
42         "JSON":  true,
43         "LHS":   true,
44         "QPS":   true,
45         "PID":   true,
46         "RAM":   true,
47         "RHS":   true,
48         "RPC":   true,
49         "SLA":   true,
50         "SMTP":  true,
51         "SQL":   true,
52         "SSH":   true,
53         "TCP":   true,
54         "TLS":   true,
55         "TTL":   true,
56         "UDP":   true,
57         "UI":    true,
58         "UID":   true,
59         "UUID":  true,
60         "URI":   true,
61         "URL":   true,
62         "UTF8":  true,
63         "VM":    true,
64         "VPN":   true,
65         "XML":   true,
66         "XMPP":  true,
67         "XSRF":  true,
68         "XSS":   true,
69 }
70
71 // specialInitialisms is a set of special initialisms that need part to stay in upper case.
72 var specialInitialisms = map[string]string{
73         "IPV": "IPv",
74 }
75
76 func usesInitialism(s string) string {
77         if u := strings.ToUpper(s); commonInitialisms[u] {
78                 return u
79         } else if su, ok := specialInitialisms[u]; ok {
80                 return su
81         }
82         return ""
83 }
84
85 // camelCaseName returns correct name identifier (camelCase).
86 func camelCaseName(name string) (should string) {
87         name = strings.Title(name)
88
89         // Fast path for simple cases: "_" and all lowercase.
90         if name == "_" {
91                 return name
92         }
93         allLower := true
94         for _, r := range name {
95                 if !unicode.IsLower(r) {
96                         allLower = false
97                         break
98                 }
99         }
100         if allLower {
101                 return name
102         }
103
104         // Split camelCase at any lower->upper transition, and split on underscores.
105         // Check each word for common initialisms.
106         runes := []rune(name)
107         w, i := 0, 0 // index of start of word, scan
108         for i+1 <= len(runes) {
109                 eow := false // whether we hit the end of a word
110                 if i+1 == len(runes) {
111                         eow = true
112                 } else if runes[i+1] == '_' {
113                         // underscore; shift the remainder forward over any run of underscores
114                         eow = true
115                         n := 1
116                         for i+n+1 < len(runes) && runes[i+n+1] == '_' {
117                                 n++
118                         }
119
120                         // Leave at most one underscore if the underscore is between two digits
121                         if i+n+1 < len(runes) && unicode.IsDigit(runes[i]) && unicode.IsDigit(runes[i+n+1]) {
122                                 n--
123                         }
124
125                         copy(runes[i+1:], runes[i+n+1:])
126                         runes = runes[:len(runes)-n]
127                 } else if unicode.IsLower(runes[i]) && !unicode.IsLower(runes[i+1]) {
128                         // lower->non-lower
129                         eow = true
130                 }
131                 i++
132                 if !eow {
133                         continue
134                 }
135
136                 // [w,i) is a word.
137                 word := string(runes[w:i])
138                 if u := usesInitialism(word); u != "" {
139                         // Keep consistent case, which is lowercase only at the start.
140                         if w == 0 && unicode.IsLower(runes[w]) {
141                                 u = strings.ToLower(u)
142                         }
143                         // All the common initialisms are ASCII,
144                         // so we can replace the bytes exactly.
145                         copy(runes[w:], []rune(u))
146                 } else if w > 0 && strings.ToLower(word) == word {
147                         // already all lowercase, and not the first word, so uppercase the first character.
148                         runes[w] = unicode.ToUpper(runes[w])
149                 }
150                 w = i
151         }
152         return string(runes)
153 }