| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package starlarkgen |
| |
| import ( |
| "reflect" |
| "strings" |
| |
| "github.com/golang/protobuf/descriptor" |
| protoc "github.com/golang/protobuf/protoc-gen-go/descriptor" |
| ) |
| |
| // Generate produces the starlark code needed to generate the input protobuf message. |
| func Generate(msg descriptor.Message) string { |
| ast := genMessage(reflect.ValueOf(msg)) |
| return ast.String() |
| } |
| |
| func genMessage(val reflect.Value) Node { |
| // Don't output anything for nil or zero values. |
| if isNothing(val) { |
| return nil |
| } |
| |
| // Must read these values before calling val.Elem(). Otherwise the value it reflects |
| // is no longer a descriptor.Message. |
| msg := val.Interface().(descriptor.Message) |
| filedesc, desc := descriptor.ForMessage(msg) |
| node := &ConstructorCall{ |
| Package: filedesc.GetPackage(), |
| Typename: desc.GetName(), |
| } |
| val = val.Elem() |
| for _, field := range desc.GetField() { |
| fieldValue := val.FieldByName(strings.Title(field.GetJsonName())) |
| fieldAst := genField(field, fieldValue) |
| // Skip fields that have nil pointer values. |
| if fieldAst == nil { |
| continue |
| } |
| // Skip fields that are set to zero-values. |
| if primitive, ok := fieldAst.(Primitive); ok && isZero(primitive.Value) { |
| continue |
| } |
| node.Kwargs = append(node.Kwargs, Kwarg{ |
| Keyword: field.GetName(), |
| Value: fieldAst, |
| }) |
| } |
| return node |
| } |
| |
| func genField(desc *protoc.FieldDescriptorProto, val reflect.Value) Node { |
| if desc.GetTypeName() == "" { |
| // This field is a not a proto message (and the instance is not a Go struct). Just |
| // write the raw value. |
| return Primitive{val} |
| } |
| |
| // This field is a proto.Message, which has a corresponding Go struct type. Generate |
| // it and its children recursively. |
| return genMessage(val) |
| } |
| |
| func isZero(val reflect.Value) bool { |
| zero := reflect.Zero(val.Type()).Interface() |
| return reflect.DeepEqual(zero, val.Interface()) |
| } |
| |
| func isNothing(val reflect.Value) bool { |
| return isZero(val) || val.IsNil() |
| } |