strings.go 2.7 KB

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