blob: 295bd296fcb3b90717bcede84230f812b864cc09 [file] [log] [blame]
Joe Tsai97a87392019-07-07 01:49:59 -07001// Copyright 2019 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package strs provides string manipulation functionality specific to protobuf.
6package strs
7
8import (
9 "strings"
10 "unicode"
11)
12
13// JSONCamelCase converts a snake_case identifier to a camelCase identifier,
14// according to the protobuf JSON specification.
15func JSONCamelCase(s string) string {
16 var b []byte
17 var wasUnderscore bool
18 for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
19 c := s[i]
20 if c != '_' {
21 isLower := 'a' <= c && c <= 'z'
22 if wasUnderscore && isLower {
23 c -= 'a' - 'A' // convert to uppercase
24 }
25 b = append(b, c)
26 }
27 wasUnderscore = c == '_'
28 }
29 return string(b)
30}
31
32// JSONSnakeCase converts a camelCase identifier to a snake_case identifier,
33// according to the protobuf JSON specification.
34func JSONSnakeCase(s string) string {
35 var b []byte
36 for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
37 c := s[i]
38 isUpper := 'A' <= c && c <= 'Z'
39 if isUpper {
40 b = append(b, '_')
41 c += 'a' - 'A' // convert to lowercase
42 }
43 b = append(b, c)
44 }
45 return string(b)
46}
47
48// MapEntryName derives the name of the map entry message given the field name.
49// See protoc v3.8.0: src/google/protobuf/descriptor.cc:254-276,6057
50func MapEntryName(s string) string {
51 var b []byte
52 upperNext := true
53 for _, c := range s {
54 switch {
55 case c == '_':
56 upperNext = true
57 case upperNext:
58 b = append(b, byte(unicode.ToUpper(c)))
59 upperNext = false
60 default:
61 b = append(b, byte(c))
62 }
63 }
64 b = append(b, "Entry"...)
65 return string(b)
66}
67
68// EnumValueName derives the camel-cased enum value name.
69// See protoc v3.8.0: src/google/protobuf/descriptor.cc:297-313
70func EnumValueName(s string) string {
71 var b []byte
72 upperNext := true
73 for _, c := range s {
74 switch {
75 case c == '_':
76 upperNext = true
77 case upperNext:
78 b = append(b, byte(unicode.ToUpper(c)))
79 upperNext = false
80 default:
81 b = append(b, byte(unicode.ToLower(c)))
82 upperNext = false
83 }
84 }
85 return string(b)
86}
87
88// TrimEnumPrefix trims the enum name prefix from an enum value name,
89// where the prefix is all lowercase without underscores.
90// See protoc v3.8.0: src/google/protobuf/descriptor.cc:330-375
91func TrimEnumPrefix(s, prefix string) string {
92 s0 := s // original input
93 for len(s) > 0 && len(prefix) > 0 {
94 if s[0] == '_' {
95 s = s[1:]
96 continue
97 }
98 if unicode.ToLower(rune(s[0])) != rune(prefix[0]) {
99 return s0 // no prefix match
100 }
101 s, prefix = s[1:], prefix[1:]
102 }
103 if len(prefix) > 0 {
104 return s0 // no prefix match
105 }
106 s = strings.TrimLeft(s, "_")
107 if len(s) == 0 {
108 return s0 // avoid returning empty string
109 }
110 return s
111}