generate.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. package yu_proto_old
  2. import (
  3. "errors"
  4. "fmt"
  5. "io"
  6. "reflect"
  7. "regexp"
  8. "sort"
  9. "strings"
  10. "text/template"
  11. )
  12. const protoTemplate = `[[range $name, $values := .Enums]]
  13. enum [[$name|$.Renamer.TypeName]] {[[range $values]]
  14. [[.Name|$.Renamer.ConstName]] = [[.Value]];[[end]]
  15. }
  16. [[end]][[range .Types]]
  17. message [[.Name|$.Renamer.TypeName]] {[[range .|Fields]]
  18. [[.|TypeName]] [[.|$.Renamer.FieldName]] = [[.ID]][[.|Options]];[[end]]
  19. }
  20. [[end]]
  21. `
  22. var splitName = regexp.MustCompile(`((?:ID)|(?:[A-Z][a-z_0-9]+)|([\w\d]+))`)
  23. func typeIndirect(t reflect.Type) reflect.Type {
  24. for t.Kind() == reflect.Ptr {
  25. t = t.Elem()
  26. }
  27. return t
  28. }
  29. func typeName(f ProtoField, enums enumTypeMap, renamer GeneratorNamer) (s string) {
  30. defer func() {
  31. if e := recover(); e != nil {
  32. s = ""
  33. panic(e.(string))
  34. }
  35. }()
  36. t := f.Field.Type
  37. if t.Kind() == reflect.Slice {
  38. if t.Elem().Kind() == reflect.Uint8 {
  39. return fieldPrefix(f, TagNone) + "bytes"
  40. }
  41. return "repeated " + innerTypeName(typeIndirect(t.Elem()), enums, renamer)
  42. }
  43. if t.Kind() == reflect.Ptr {
  44. return fieldPrefix(f, TagOptional) + innerTypeName(t.Elem(), enums, renamer)
  45. }
  46. return fieldPrefix(f, TagNone) + innerTypeName(t, enums, renamer)
  47. }
  48. func fieldPrefix(f ProtoField, def TagPrefix) string {
  49. opt := def
  50. if def == TagNone {
  51. opt = f.Prefix
  52. }
  53. switch opt {
  54. case TagOptional:
  55. return "optional "
  56. case TagRequired:
  57. return "required "
  58. default:
  59. if f.Field.Type.Kind() == reflect.Ptr {
  60. return "optional "
  61. }
  62. return "required "
  63. }
  64. }
  65. func innerTypeName(t reflect.Type, enums enumTypeMap, renamer GeneratorNamer) string {
  66. if (t.Kind() == reflect.Slice || t.Kind() == reflect.Array) && t.Elem().Kind() == reflect.Uint8 {
  67. return "bytes"
  68. }
  69. if t.PkgPath() == "time" {
  70. if t.Name() == "Time" {
  71. return "sfixed64"
  72. }
  73. if t.Name() == "Duration" {
  74. return "sint64"
  75. }
  76. }
  77. switch t.Name() {
  78. case "Ufixed32":
  79. return "fixed32"
  80. case "Ufixed64":
  81. return "ufixed64"
  82. case "Sfixed32":
  83. return "sfixed32"
  84. case "Sfixed64":
  85. return "sfixed64"
  86. }
  87. if _, ok := enums[t.Name()]; ok {
  88. return renamer.TypeName(t.Name())
  89. }
  90. switch t.Kind() {
  91. case reflect.Float64:
  92. return "double"
  93. case reflect.Float32:
  94. return "float"
  95. case reflect.Int32:
  96. return "sint32"
  97. case reflect.Int, reflect.Int64:
  98. return "sint64"
  99. case reflect.Bool:
  100. return "bool"
  101. case reflect.Uint32:
  102. return "uint32"
  103. case reflect.Uint, reflect.Uint64:
  104. return "uint64"
  105. case reflect.String:
  106. return "string"
  107. case reflect.Struct:
  108. return t.Name()
  109. case reflect.Map:
  110. // we have to do this again (otherwise we'll end up with an empty name for the value):
  111. var valTypeName string
  112. valType := t.Elem()
  113. if valType.Kind() == reflect.Slice {
  114. if valType.Elem().Kind() == reflect.Uint8 {
  115. valTypeName = "bytes"
  116. } else {
  117. valTypeName = innerTypeName(typeIndirect(valType.Elem()), enums, renamer)
  118. }
  119. } else if valType.Kind() == reflect.Ptr {
  120. valTypeName = innerTypeName(valType.Elem(), enums, renamer)
  121. } else {
  122. // here we can just use the value's type:
  123. valTypeName = innerTypeName(valType, enums, renamer)
  124. }
  125. return fmt.Sprintf("map<%s, %s>", innerTypeName(t.Key(), enums, renamer), valTypeName)
  126. default:
  127. panic("unsupported type " + t.Name())
  128. }
  129. }
  130. func options(f ProtoField) string {
  131. if f.Field.Type.Kind() == reflect.Slice {
  132. switch f.Field.Type.Elem().Kind() {
  133. case reflect.Bool,
  134. reflect.Int32, reflect.Int64,
  135. reflect.Uint32, reflect.Uint64,
  136. reflect.Float32, reflect.Float64:
  137. return " [packed=true]"
  138. }
  139. }
  140. return ""
  141. }
  142. type GeneratorNamer interface {
  143. FieldName(ProtoField) string
  144. TypeName(name string) string
  145. ConstName(name string) string
  146. }
  147. // DefaultGeneratorNamer renames symbols when mapping from Go to .proto files.
  148. //
  149. // The rules are:
  150. // - Field names are mapped from SomeFieldName to some_field_name.
  151. // - Type names are not modified.
  152. // - Constants are mapped form SomeConstantName to SOME_CONSTANT_NAME.
  153. type DefaultGeneratorNamer struct{}
  154. func (d *DefaultGeneratorNamer) FieldName(f ProtoField) string {
  155. if f.Name != "" {
  156. return f.Name
  157. }
  158. parts := splitName.FindAllString(f.Field.Name, -1)
  159. for i := range parts {
  160. parts[i] = strings.ToLower(parts[i])
  161. }
  162. return strings.Join(parts, "_")
  163. }
  164. func (d *DefaultGeneratorNamer) TypeName(name string) string {
  165. return name
  166. }
  167. func (d *DefaultGeneratorNamer) ConstName(name string) string {
  168. parts := splitName.FindAllString(name, -1)
  169. for i := range parts {
  170. parts[i] = strings.ToUpper(parts[i])
  171. }
  172. return strings.Join(parts, "_")
  173. }
  174. type reflectedTypes []reflect.Type
  175. func (r reflectedTypes) Len() int { return len(r) }
  176. func (r reflectedTypes) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
  177. func (r reflectedTypes) Less(i, j int) bool { return r[i].Name() < r[j].Name() }
  178. type EnumMap map[string]interface{}
  179. type enumValue struct {
  180. Name string
  181. Value Enum
  182. }
  183. type enumValues []enumValue
  184. func (e enumValues) Len() int { return len(e) }
  185. func (e enumValues) Swap(i, j int) { e[i], e[j] = e[j], e[i] }
  186. func (e enumValues) Less(i, j int) bool { return e[i].Value < e[j].Value }
  187. type enumTypeMap map[string]enumValues
  188. // GenerateProtobufDefinition generates a .proto file from a list of structs via reflection.
  189. // fieldNamer is a function that maps ProtoField types to generated protobuf field names.
  190. func GenerateProtobufDefinition(w io.Writer, types []interface{}, enumMap EnumMap, renamer GeneratorNamer) (err error) {
  191. defer func() {
  192. if e := recover(); e != nil {
  193. err = errors.New(e.(string))
  194. }
  195. }()
  196. enums := enumTypeMap{}
  197. for name, value := range enumMap {
  198. v := reflect.ValueOf(value)
  199. t := v.Type()
  200. if t.Kind() != reflect.Uint32 {
  201. return fmt.Errorf("enum type aliases must be uint32")
  202. }
  203. if t.Name() == "uint32" {
  204. return fmt.Errorf("enum value must be a type alias, but got uint32")
  205. }
  206. enums[t.Name()] = append(enums[t.Name()], enumValue{name, Enum(v.Uint())})
  207. }
  208. for _, values := range enums {
  209. sort.Sort(values)
  210. }
  211. rt := reflectedTypes{}
  212. for _, t := range types {
  213. typ := reflect.Indirect(reflect.ValueOf(t)).Type()
  214. if typ.Kind() != reflect.Struct {
  215. continue
  216. }
  217. rt = append(rt, typ)
  218. }
  219. sort.Sort(rt)
  220. if renamer == nil {
  221. renamer = &DefaultGeneratorNamer{}
  222. }
  223. t := template.Must(template.New("protobuf").Funcs(template.FuncMap{
  224. "Fields": ProtoFields,
  225. "TypeName": func(f ProtoField) string { return typeName(f, enums, renamer) },
  226. "Options": options,
  227. }).Delims("[[", "]]").Parse(protoTemplate))
  228. return t.Execute(w, map[string]interface{}{
  229. "Renamer": renamer,
  230. "Enums": enums,
  231. "Types": rt,
  232. "Ptr": reflect.Ptr,
  233. "Slice": reflect.Slice,
  234. "Map": reflect.Map,
  235. })
  236. }