github.com/openconfig/goyang@v1.4.5/pkg/yang/camelcase.go (about) 1 // Copyright 2015 Google Inc. 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 yang 16 17 var knownWords = map[string]string{ 18 "Ietf": "IETF", 19 } 20 21 // Is c an ASCII lower-case letter? 22 func isASCIILower(c byte) bool { 23 return 'a' <= c && c <= 'z' 24 } 25 26 // Is c an ASCII digit? 27 func isASCIIDigit(c byte) bool { 28 return '0' <= c && c <= '9' 29 } 30 31 // CamelCase returns a CamelCased name for a YANG identifier. 32 // Currently this supports the output being used for a Go or proto identifier. 33 // Dash and dot are first converted to underscore, and then any underscores 34 // before a lower-case letter are removed, and the letter converted to 35 // upper-case. Any input characters not part of the YANG identifier 36 // specification (https://tools.ietf.org/html/rfc7950#section-6.2) are treated 37 // as lower-case characters. 38 // The first letter is always upper-case in order to be an exported name in Go. 39 // There is a remote possibility of this rewrite causing a name collision, but 40 // it's so remote we're prepared to pretend it's nonexistent - since the C++ 41 // generator lowercases names, it's extremely unlikely to have two fields with 42 // different capitalizations. In short, _my_field-name_2 becomes XMyFieldName_2. 43 func CamelCase(s string) string { 44 if s == "" { 45 return "" 46 } 47 48 fix := func(c byte) byte { 49 if c == '-' || c == '.' { 50 return '_' 51 } 52 return c 53 } 54 55 t := make([]byte, 0, 32) 56 i := 0 57 if fix(s[0]) == '_' { 58 // Need a capital letter; drop the '_'. 59 t = append(t, 'X') 60 i++ 61 } 62 63 // Invariant: if the next letter is lower case, it must be converted 64 // to upper case. 65 // That is, we process a word at a time, where words are marked by _ or 66 // upper case letter. Digits are treated as words. 67 for ; i < len(s); i++ { 68 c := fix(s[i]) 69 if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) { 70 continue // Skip the underscore in s. 71 } 72 if isASCIIDigit(c) { 73 t = append(t, c) 74 continue 75 } 76 // Assume we have a letter now - if not, it's a bogus identifier. 77 // The next word is a sequence of characters that must start upper case. 78 if isASCIILower(c) { 79 c ^= ' ' // Make it a capital letter. 80 } 81 start := len(t) 82 t = append(t, c) // Guaranteed not lower case. 83 // Accept lower case sequence that follows. 84 for i+1 < len(s) && isASCIILower(s[i+1]) { 85 i++ 86 t = append(t, s[i]) 87 } 88 // If the word turns out to be a special word, then use that instead. 89 if kn := knownWords[string(t[start:])]; kn != "" { 90 t = append(t[:start], []byte(kn)...) 91 } 92 } 93 return string(t) 94 }