blob: 2208ff2104cc6c80a68794734c2f940666b25791 [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"
Joe Tsaic51e2e02019-07-13 00:44:41 -070011
12 "google.golang.org/protobuf/internal/flags"
13 "google.golang.org/protobuf/reflect/protoreflect"
Joe Tsai97a87392019-07-07 01:49:59 -070014)
15
Joe Tsaic51e2e02019-07-13 00:44:41 -070016// EnforceUTF8 reports whether to enforce strict UTF-8 validation.
17func EnforceUTF8(fd protoreflect.FieldDescriptor) bool {
Joe Tsai1799d112019-08-08 13:31:59 -070018 if flags.ProtoLegacy {
Joe Tsaic51e2e02019-07-13 00:44:41 -070019 if fd, ok := fd.(interface{ EnforceUTF8() bool }); ok {
20 return fd.EnforceUTF8()
21 }
22 }
23 return fd.Syntax() == protoreflect.Proto3
24}
25
Joe Tsai97a87392019-07-07 01:49:59 -070026// JSONCamelCase converts a snake_case identifier to a camelCase identifier,
27// according to the protobuf JSON specification.
28func JSONCamelCase(s string) string {
29 var b []byte
30 var wasUnderscore bool
31 for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
32 c := s[i]
33 if c != '_' {
34 isLower := 'a' <= c && c <= 'z'
35 if wasUnderscore && isLower {
36 c -= 'a' - 'A' // convert to uppercase
37 }
38 b = append(b, c)
39 }
40 wasUnderscore = c == '_'
41 }
42 return string(b)
43}
44
45// JSONSnakeCase converts a camelCase identifier to a snake_case identifier,
46// according to the protobuf JSON specification.
47func JSONSnakeCase(s string) string {
48 var b []byte
49 for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
50 c := s[i]
51 isUpper := 'A' <= c && c <= 'Z'
52 if isUpper {
53 b = append(b, '_')
54 c += 'a' - 'A' // convert to lowercase
55 }
56 b = append(b, c)
57 }
58 return string(b)
59}
60
61// MapEntryName derives the name of the map entry message given the field name.
62// See protoc v3.8.0: src/google/protobuf/descriptor.cc:254-276,6057
63func MapEntryName(s string) string {
64 var b []byte
65 upperNext := true
66 for _, c := range s {
67 switch {
68 case c == '_':
69 upperNext = true
70 case upperNext:
71 b = append(b, byte(unicode.ToUpper(c)))
72 upperNext = false
73 default:
74 b = append(b, byte(c))
75 }
76 }
77 b = append(b, "Entry"...)
78 return string(b)
79}
80
81// EnumValueName derives the camel-cased enum value name.
82// See protoc v3.8.0: src/google/protobuf/descriptor.cc:297-313
83func EnumValueName(s string) string {
84 var b []byte
85 upperNext := true
86 for _, c := range s {
87 switch {
88 case c == '_':
89 upperNext = true
90 case upperNext:
91 b = append(b, byte(unicode.ToUpper(c)))
92 upperNext = false
93 default:
94 b = append(b, byte(unicode.ToLower(c)))
95 upperNext = false
96 }
97 }
98 return string(b)
99}
100
101// TrimEnumPrefix trims the enum name prefix from an enum value name,
102// where the prefix is all lowercase without underscores.
103// See protoc v3.8.0: src/google/protobuf/descriptor.cc:330-375
104func TrimEnumPrefix(s, prefix string) string {
105 s0 := s // original input
106 for len(s) > 0 && len(prefix) > 0 {
107 if s[0] == '_' {
108 s = s[1:]
109 continue
110 }
111 if unicode.ToLower(rune(s[0])) != rune(prefix[0]) {
112 return s0 // no prefix match
113 }
114 s, prefix = s[1:], prefix[1:]
115 }
116 if len(prefix) > 0 {
117 return s0 // no prefix match
118 }
119 s = strings.TrimLeft(s, "_")
120 if len(s) == 0 {
121 return s0 // avoid returning empty string
122 }
123 return s
124}