blob: 29c209c7708525ca8ca23a9cabfd28fdc81df158 [file] [log] [blame]
// 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()
}