Skip to content

union: support integration for oneof fields #201

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 69 additions & 40 deletions internal/confgen/document_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,51 +473,80 @@ func (sp *documentParser) parseUnionMessage(field *Field, msg protoreflect.Messa
if valueFD.Kind() != protoreflect.MessageKind {
return false, xerrors.Errorf("union value (oneof) as scalar type not supported: %s", valueFD.FullName())
}
// MUST be message type.
md := valueFD.Message()
fieldMsg := fieldValue.Message()
for i := 0; i < md.Fields().Len(); i++ {
fd := md.Fields().Get(i)
valNodeName := unionDesc.ValueFieldName() + strconv.Itoa(int(fd.Number()))
valNode := node.FindChild(valNodeName)
err := func() error {
subField := parseFieldDescriptor(fd, sp.GetSep(), sp.GetSubsep())
defer subField.release()
if valNode == nil && xproto.GetFieldDefaultValue(fd) != "" {
// if this field has a default value, use virtual node
valNode = &book.Node{
Name: node.Name,
Value: node.Value,
}
valueField := parseFieldDescriptor(valueFD, sp.GetSep(), sp.GetSubsep())
if valueField.opts.GetProp().GetIntegrated() {
valNode := node.FindChild(unionDesc.ValueFieldName() + "1")
if valNode == nil && xproto.GetFieldDefaultValue(field.fd) != "" {
// if this field has a default value, use virtual node
valNode = &book.Node{
Name: node.Name,
Value: node.Value,
}
if valNode == nil {
if sp.IsFieldOptional(subField) {
// field not found and is optional, just return nil.
return nil
}
kvs := node.DebugNameKV()
kvs = append(kvs,
xerrors.KeyPBFieldType, xproto.GetFieldTypeName(fd),
xerrors.KeyPBFieldName, fd.FullName(),
xerrors.KeyPBFieldOpts, subField.opts,
)
return xerrors.WrapKV(xerrors.E2014(subField.opts.Name), kvs...)
}
if valNode == nil {
if sp.IsFieldOptional(field) {
// field not found and is optional, just return nil.
return false, nil
}
crossNodeValues := []string{valNode.Value}
if fieldCount := fieldprop.GetUnionCrossFieldCount(subField.opts.Prop); fieldCount > 0 {
for j := 1; j < fieldCount; j++ {
nodeName := unionDesc.ValueFieldName() + strconv.Itoa(int(fd.Number())+j)
node := node.FindChild(nodeName)
if node == nil {
break
kvs := node.DebugNameKV()
kvs = append(kvs,
xerrors.KeyPBFieldType, xproto.GetFieldTypeName(field.fd),
xerrors.KeyPBFieldName, field.fd.FullName(),
xerrors.KeyPBFieldOpts, field.opts,
)
return false, xerrors.WrapKV(xerrors.E2014(field.opts.Name), kvs...)
}
present, err = sp.parseIncellStruct(fieldValue, valNode.Value, field.opts.GetProp().GetForm(), field.sep)
if err != nil {
return false, err
}
} else {
// MUST be message type.
md := valueFD.Message()
fieldMsg := fieldValue.Message()
for i := 0; i < md.Fields().Len(); i++ {
fd := md.Fields().Get(i)
valNodeName := unionDesc.ValueFieldName() + strconv.Itoa(int(fd.Number()))
valNode := node.FindChild(valNodeName)
err := func() error {
subField := parseFieldDescriptor(fd, sp.GetSep(), sp.GetSubsep())
defer subField.release()
if valNode == nil && xproto.GetFieldDefaultValue(fd) != "" {
// if this field has a default value, use virtual node
valNode = &book.Node{
Name: node.Name,
Value: node.Value,
}
crossNodeValues = append(crossNodeValues, node.Value)
}
if valNode == nil {
if sp.IsFieldOptional(subField) {
// field not found and is optional, just return nil.
return nil
}
kvs := node.DebugNameKV()
kvs = append(kvs,
xerrors.KeyPBFieldType, xproto.GetFieldTypeName(fd),
xerrors.KeyPBFieldName, fd.FullName(),
xerrors.KeyPBFieldOpts, subField.opts,
)
return xerrors.WrapKV(xerrors.E2014(subField.opts.Name), kvs...)
}
crossNodeValues := []string{valNode.Value}
if fieldCount := fieldprop.GetUnionCrossFieldCount(subField.opts.Prop); fieldCount > 0 {
for j := 1; j < fieldCount; j++ {
nodeName := unionDesc.ValueFieldName() + strconv.Itoa(int(fd.Number())+j)
node := node.FindChild(nodeName)
if node == nil {
break
}
crossNodeValues = append(crossNodeValues, node.Value)
}
}
return sp.parseUnionMessageField(subField, fieldMsg, crossNodeValues)
}()
if err != nil {
return false, xerrors.WrapKV(err, valNode.DebugNameKV()...)
}
return sp.parseUnionMessageField(subField, fieldMsg, crossNodeValues)
}()
if err != nil {
return false, xerrors.WrapKV(err, valNode.DebugNameKV()...)
}
}
msg.Set(valueFD, fieldValue)
Expand Down
64 changes: 38 additions & 26 deletions internal/confgen/table_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -632,35 +632,47 @@ func (sp *tableParser) parseUnionMessage(msg protoreflect.Message, field *Field,
if valueFD.Kind() != protoreflect.MessageKind {
return false, xerrors.Errorf("union value (oneof) as scalar type not supported: %s", valueFD.FullName())
}
// MUST be message type.
md := valueFD.Message()
fieldMsg := fieldValue.Message()
for i := 0; i < md.Fields().Len(); i++ {
fd := md.Fields().Get(i)
valColName := prefix + unionDesc.ValueFieldName() + strconv.Itoa(int(fd.Number()))
err := func() error {
subField := parseFieldDescriptor(fd, sp.GetSep(), sp.GetSubsep())
defer subField.release()
// incell scalar
cell, err := rc.Cell(valColName, sp.IsFieldOptional(subField))
if err != nil {
return err
}
crossCellDataList := []string{cell.Data}
if fieldCount := fieldprop.GetUnionCrossFieldCount(subField.opts.Prop); fieldCount > 0 {
for j := 1; j < fieldCount; j++ {
colName := prefix + unionDesc.ValueFieldName() + strconv.Itoa(int(fd.Number())+j)
c, err := rc.Cell(colName, sp.IsFieldOptional(subField))
if err != nil {
break
valueField := parseFieldDescriptor(valueFD, sp.GetSep(), sp.GetSubsep())
if valueField.opts.GetProp().GetIntegrated() {
cell, err := rc.Cell(prefix+unionDesc.ValueFieldName()+"1", false)
if err != nil {
return false, err
}
present, err = sp.parseIncellStruct(fieldValue, cell.Data, valueField.opts.GetProp().GetForm(), valueField.sep)
if err != nil {
return false, err
}
} else {
// MUST be message type.
md := valueFD.Message()
fieldMsg := fieldValue.Message()
for i := 0; i < md.Fields().Len(); i++ {
fd := md.Fields().Get(i)
valColName := prefix + unionDesc.ValueFieldName() + strconv.Itoa(int(fd.Number()))
err := func() error {
subField := parseFieldDescriptor(fd, sp.GetSep(), sp.GetSubsep())
defer subField.release()
// incell scalar
cell, err := rc.Cell(valColName, sp.IsFieldOptional(subField))
if err != nil {
return err
}
crossCellDataList := []string{cell.Data}
if fieldCount := fieldprop.GetUnionCrossFieldCount(subField.opts.Prop); fieldCount > 0 {
for j := 1; j < fieldCount; j++ {
colName := prefix + unionDesc.ValueFieldName() + strconv.Itoa(int(fd.Number())+j)
c, err := rc.Cell(colName, sp.IsFieldOptional(subField))
if err != nil {
break
}
crossCellDataList = append(crossCellDataList, c.Data)
}
crossCellDataList = append(crossCellDataList, c.Data)
}
return sp.parseUnionMessageField(subField, fieldMsg, crossCellDataList)
}()
if err != nil {
return false, xerrors.WrapKV(err, rc.CellDebugKV(valColName)...)
}
return sp.parseUnionMessageField(subField, fieldMsg, crossCellDataList)
}()
if err != nil {
return false, xerrors.WrapKV(err, rc.CellDebugKV(valColName)...)
}
}
msg.Set(valueFD, fieldValue)
Expand Down
9 changes: 6 additions & 3 deletions internal/protogen/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ func (x *sheetExporter) exportUnion() error {

for _, field := range x.ws.Fields {
ename := "TYPE_" + strcase.ToScreamingSnake(field.Name)
x.g.P(" ", strings.TrimSpace(field.Name), " ", strcase.ToSnake(field.Name), " = ", field.Number, `; // Bound to enum value: `, ename, ".")
x.g.P(" ", strings.TrimSpace(field.Name), " ", strcase.ToSnake(field.Name), " = ", field.Number, genFieldOptionsString(field.Options), `; // Bound to enum value: `, ename, ".")
}
x.g.P(` }`)
x.g.P()
Expand Down Expand Up @@ -279,7 +279,7 @@ func (x *sheetExporter) exportField(depth int, tagid int, field *internalpb.Fiel
label = "optional "
}

x.g.P(printer.Indent(depth), label, field.FullType, " ", field.Name, " = ", tagid, " ", genFieldOptionsString(field.Options), ";")
x.g.P(printer.Indent(depth), label, field.FullType, " ", field.Name, " = ", tagid, genFieldOptionsString(field.Options), ";")

typeName := field.Type
fullTypeName := field.FullType
Expand Down Expand Up @@ -340,6 +340,9 @@ func (x *sheetExporter) exportField(depth int, tagid int, field *internalpb.Fiel
}

func genFieldOptionsString(opts *tableaupb.FieldOptions) string {
if opts == nil {
return ""
}
jsonName := ""
// remember and then clear protobuf built-in options
if opts.Prop != nil {
Expand All @@ -353,7 +356,7 @@ func genFieldOptionsString(opts *tableaupb.FieldOptions) string {
}

// compose this field options
fieldOpts := "[(tableau.field) = {" + marshalToText(opts) + "}"
fieldOpts := " [(tableau.field) = {" + marshalToText(opts) + "}"
if jsonName != "" {
fieldOpts += `, json_name="` + jsonName + `"`
}
Expand Down
15 changes: 11 additions & 4 deletions internal/protogen/exporter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func Test_genFieldOptionsString(t *testing.T) {
Name: "ItemID",
},
},
want: `[(tableau.field) = {name:"ItemID"}]`,
want: ` [(tableau.field) = {name:"ItemID"}]`,
},
{
name: "name-and-prop",
Expand All @@ -39,7 +39,7 @@ func Test_genFieldOptionsString(t *testing.T) {
},
},
},
want: `[(tableau.field) = {name:"ItemID" prop:{unique:true}}]`,
want: ` [(tableau.field) = {name:"ItemID" prop:{unique:true}}]`,
},
{
name: "name-prop-and-json-name",
Expand All @@ -52,7 +52,7 @@ func Test_genFieldOptionsString(t *testing.T) {
},
},
},
want: `[(tableau.field) = {name:"ItemID" prop:{unique:true}}, json_name="item_id_1"]`,
want: ` [(tableau.field) = {name:"ItemID" prop:{unique:true}}, json_name="item_id_1"]`,
},
{
name: "name-and-prop-json_name",
Expand All @@ -64,7 +64,14 @@ func Test_genFieldOptionsString(t *testing.T) {
},
},
},
want: `[(tableau.field) = {name:"ItemID"}, json_name="item_id_1"]`,
want: ` [(tableau.field) = {name:"ItemID"}, json_name="item_id_1"]`,
},
{
name: "nil",
args: args{
opts: nil,
},
want: ``,
},
}
for _, tt := range tests {
Expand Down
13 changes: 12 additions & 1 deletion internal/protogen/sheet_mode.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,11 +186,22 @@ func parseUnionType(ws *internalpb.Worksheet, sheet *book.Sheet, parser book.She
if value.Number != nil {
number = *value.Number
}
name := value.Name
var prop *tableaupb.FieldProp
if desc := types.MatchScalar(name); desc != nil {
name = desc.ScalarType
prop, _ = desc.Prop.FieldProp()
}
field := &internalpb.Field{
Number: number,
Name: value.Name,
Name: name,
Alias: value.Alias,
}
if prop != nil {
field.Options = &tableaupb.FieldOptions{
Prop: prop,
}
}
// create a book parser
bp := newTableParser("union", "", "", gen)

Expand Down
31 changes: 23 additions & 8 deletions proto/tableau/protobuf/internal/metabook.proto
Original file line number Diff line number Diff line change
Expand Up @@ -84,23 +84,38 @@ message Metasheet {
// Generate ordered map accessers
bool ordered_map = 50 [(tableau.field) = {name:"OrderedMap" prop:{optional:true}}];
// Generate index accessers, and multiple index columns are comma-separated.
// Format: <ColumnName>[@IndexName], if IndexName is not set, it will be this
// Format: ColumnName<KeyName>[@IndexName]. If IndexName is not set, it will be this
// column’s parent struct type name.
//
// Composite indexes (or multicolumn indexes) are in the form: ([column1, column2, column3,...])[@IndexName]
// Examples:
// - ID
// - ID@Item
// - ID<Key>@Item
// - ID<Key, Key2>@Item
//
// Composite indexes (or multicolumn indexes) are in the form:
// ([column1, column2, column3,...])<[key1, key2, key3,...]>[@IndexName]
//
// Examples:
// - ID
// - ID@Item
// - (ID,Type)
// - (ID,Type)@Item
// - ID, (ID,Type)@Item
// - (ID, Name)
// - (ID, Name)@Item
// - (ID, Name)<Key>@Item
// - (ID, Name)<Key1, Key2>@Item
//
// Generated APIs are:
//
// C++:
// - const std::vector<const STRUCT_TYPE*>& Find<IndexName>(INDEX_TYPE index) const;
// - using Index_<IndexName>Vector = std::vector<const STRUCT_TYPE*>;
// - using Index_<IndexName>Map = std::unordered_map<INDEX_TYPE, Index_<IndexName>Vector>;
// - const Index_<IndexName>Map& Find<IndexName>() const;
// - const Index_<IndexName>Vector* Find<IndexName>(INDEX_TYPE index) const;
// - const STRUCT_TYPE* FindFirst<IndexName>(INDEX_TYPE index);
//
// Go:
// - type <MessageName>_Index_<IndexName>Map = map[INDEX_TYPE][]*STRUCT_TYPE
// - func (x *<MessageName>) Find<IndexName>Map() <MessageName>_Index_<IndexName>Map
// - func (x *<MessageName>) Find<IndexName>(index INDEX_TYPE) []*STRUCT_TYPE
// - func (x *<MessageName>) FindFirst<IndexName>(index INDEX_TYPE) *STRUCT_TYPE
string index = 51 [(tableau.field) = {name:"Index" prop:{optional:true}}];

// Specify loader language options.
Expand Down
3 changes: 3 additions & 0 deletions proto/tableau/protobuf/tableau.proto
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,9 @@ message FieldProp {
//
// TODO: use cases for more composite types.
int32 cross = 15;
// Whether this union field data is integrated in its Field1
// as an incell struct.
bool integrated = 16;
}

// Layout of list and map.
Expand Down
Loading
Loading