123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158 |
- // Copyright 2018 Twitch Interactive, Inc. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License"). You may not
- // use this file except in compliance with the License. A copy of the License is
- // located at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // or in the "license" file accompanying this file. This file is distributed on
- // an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- // express or implied. See the License for the specific language governing
- // permissions and limitations under the License.
- //
- // This file contains some code from https://github.com/golang/protobuf:
- // Copyright 2010 The Go Authors. All rights reserved.
- // https://github.com/golang/protobuf
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are
- // met:
- //
- // * Redistributions of source code must retain the above copyright
- // notice, this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above
- // copyright notice, this list of conditions and the following disclaimer
- // in the documentation and/or other materials provided with the
- // distribution.
- // * Neither the name of Google Inc. nor the names of its
- // contributors may be used to endorse or promote products derived from
- // this software without specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
- // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
- // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
- // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- package stringutils
- import (
- "bytes"
- "fmt"
- "strings"
- "unicode"
- )
- // Is c an ASCII lower-case letter?
- func isASCIILower(c byte) bool {
- return 'a' <= c && c <= 'z'
- }
- // Is c an ASCII digit?
- func isASCIIDigit(c byte) bool {
- return '0' <= c && c <= '9'
- }
- // CamelCase converts a string from snake_case to CamelCased.
- //
- // If there is an interior underscore followed by a lower case letter, drop the
- // underscore and convert the letter to upper case. There is a remote
- // possibility of this rewrite causing a name collision, but it's so remote
- // we're prepared to pretend it's nonexistent - since the C++ generator
- // lowercases names, it's extremely unlikely to have two fields with different
- // capitalizations. In short, _my_field_name_2 becomes XMyFieldName_2.
- func CamelCase(s string) string {
- if s == "" {
- return ""
- }
- t := make([]byte, 0, 32)
- i := 0
- if s[0] == '_' {
- // Need a capital letter; drop the '_'.
- t = append(t, 'X')
- i++
- }
- // Invariant: if the next letter is lower case, it must be converted
- // to upper case.
- //
- // That is, we process a word at a time, where words are marked by _ or upper
- // case letter. Digits are treated as words.
- for ; i < len(s); i++ {
- c := s[i]
- if c == '_' && i+1 < len(s) && isASCIILower(s[i+1]) {
- continue // Skip the underscore in s.
- }
- if isASCIIDigit(c) {
- t = append(t, c)
- continue
- }
- // Assume we have a letter now - if not, it's a bogus identifier. The next
- // word is a sequence of characters that must start upper case.
- if isASCIILower(c) {
- c ^= ' ' // Make it a capital letter.
- }
- t = append(t, c) // Guaranteed not lower case.
- // Accept lower case sequence that follows.
- for i+1 < len(s) && isASCIILower(s[i+1]) {
- i++
- t = append(t, s[i])
- }
- }
- return string(t)
- }
- // CamelCaseSlice is like CamelCase, but the argument is a slice of strings to
- // be joined with "_" and then camelcased.
- func CamelCaseSlice(elem []string) string { return CamelCase(strings.Join(elem, "_")) }
- // DotJoin joins a slice of strings with '.'
- func DotJoin(elem []string) string { return strings.Join(elem, ".") }
- // AlphaDigitize replaces non-letter, non-digit, non-underscore characters with
- // underscore.
- func AlphaDigitize(r rune) rune {
- if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '_' {
- return r
- }
- return '_'
- }
- // CleanIdentifier makes sure s is a valid 'identifier' string: it contains only
- // letters, numbers, and underscore.
- func CleanIdentifier(s string) string {
- return strings.Map(AlphaDigitize, s)
- }
- // BaseName the last path element of a slash-delimited name, with the last
- // dotted suffix removed.
- func BaseName(name string) string {
- // First, find the last element
- if i := strings.LastIndex(name, "/"); i >= 0 {
- name = name[i+1:]
- }
- // Now drop the suffix
- if i := strings.LastIndex(name, "."); i >= 0 {
- name = name[0:i]
- }
- return name
- }
- // SnakeCase converts a string from CamelCase to snake_case.
- func SnakeCase(s string) string {
- var buf bytes.Buffer
- for i, r := range s {
- if unicode.IsUpper(r) && i > 0 {
- fmt.Fprintf(&buf, "_")
- }
- r = unicode.ToLower(r)
- fmt.Fprintf(&buf, "%c", r)
- }
- return buf.String()
- }
|