Skip to content

Commit

Permalink
Replace comment tags with custom decorations, support new @decoration
Browse files Browse the repository at this point in the history
…format
  • Loading branch information
200sc committed Mar 24, 2024
1 parent a7281a0 commit 72cd773
Show file tree
Hide file tree
Showing 17 changed files with 504 additions and 1,672 deletions.
49 changes: 28 additions & 21 deletions bebop.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type Struct struct {
// If ReadOnly is true, generated code for the struct will
// provide field getters instead of exporting fields.
ReadOnly bool
Decorations
}

// A Field is an individual, typed data component making up
Expand All @@ -55,11 +56,7 @@ type Field struct {
FieldType
Name string
Comment string
// Tags are not written by default, and must be enabled via a compiler flag.
Tags []Tag
// DeprecatedMessage is only provided if Deprecated is true.
DeprecatedMessage string
Deprecated bool
Decorations
}

// A Message is a record type where all fields are optional and keyed to indices.
Expand All @@ -71,6 +68,7 @@ type Message struct {
// Namespace is only provided for imported types, and only
// used in code generation.
Namespace string
Decorations
}

// A Union is like a message where explicitly one field will be provided.
Expand All @@ -82,17 +80,14 @@ type Union struct {
// Namespace is only provided for imported types, and only
// used in code generation.
Namespace string
Decorations
}

// A UnionField is either a Message, Struct, or Union, defined inline.
type UnionField struct {
Message *Message
Struct *Struct
// Tags are not written by default, ard must be enabled via a compiler flag.
Tags []Tag
// DeprecatedMessage is only provided if Deprecated is true.
DeprecatedMessage string
Deprecated bool
Decorations
}

// An Enum is a definition that will generate typed enumerable options.
Expand All @@ -107,19 +102,18 @@ type Enum struct {
// otherwise specified, it defaults to uint32
SimpleType string
Unsigned bool
Decorations
}

// An EnumOption is one possible value for a field typed as a specific Enum.
type EnumOption struct {
Name string
Comment string
// DeprecatedMessage is only provided if Deprecated is true.
DeprecatedMessage string
// Only one of Value or UintValue should e populated, dependant on
// if the enum this option applies to is unsigned.
Value int64
UintValue uint64
Deprecated bool
Value int64
UintValue uint64
Decorations
}

// A FieldType is a union of three choices: Simple types, array types, and map types.
Expand All @@ -146,12 +140,25 @@ type Const struct {
Comment string
Name string
Value string
Decorations
}

// A Tag is a Go struct field tag, e.g. `json:"userId,omitempty"`
type Tag struct {
Key string
Value string
// Boolean is set if Value is empty, in the form `key`, not `key:""`.
Boolean bool
type Decorations struct {
// A DeprecatedMessage is only provided if Deprecated is true.
DeprecatedMessage string
// The Deprecated boolean implies the field or top level structure
// this is associated with should not be used. Deprecated message
// fields will not be written to the wire format even if they are
// provided.
Deprecated bool
// Custom annotations are any not recognized by the parser.
// Recognized annotations are deprecated, opcode, flags;
// @decorator will result in an empty value for the key
// 'decorator', and is equivalent to @decorator("").
// In Go, Custom decorations on fields will result in struct
// tags; empty values for custom decorations in this case
// correspond to boolean struct fields i.e.
// @boolean + @json("abc") =
// 'field Type `boolean json:"abc"`'
Custom map[string]string
}
82 changes: 47 additions & 35 deletions equality_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ func (f File) equals(f2 File) error {
}
for i, cons := range f.Consts {
if err := cons.equals(f2.Consts[i]); err != nil {
fmt.Println(cons, f2.Consts[i])
return fmt.Errorf("const %d mismatched: %w", i, err)
}
}
Expand All @@ -67,6 +66,34 @@ func (s Struct) equals(s2 Struct) (err error) {
return fmt.Errorf("field %d mismatched: %v", i, err)
}
}
if err := s.Decorations.equals(s2.Decorations); err != nil {
return fmt.Errorf("decorations mismatched: %v", err)
}
return nil
}

func (d Decorations) equals(d2 Decorations) (err error) {
if d.Deprecated != d2.Deprecated {
return fmt.Errorf("deprecation mismatch: %v vs %v", d.Deprecated, d2.Deprecated)
}
if d.DeprecatedMessage != d2.DeprecatedMessage {
return fmt.Errorf("deprecated message mismatch: %v vs %v", d.DeprecatedMessage, d2.DeprecatedMessage)
}
if len(d.Custom) != len(d2.Custom) {
return fmt.Errorf("custom decorations mismatch: %v vs %v", len(d.Custom), len(d2.Custom))
}
for k, v := range d.Custom {
v2 := d2.Custom[k]
if v != v2 {
return fmt.Errorf("decoration %v mismatch: %v vs %v", k, v, v2)
}
}
for k, v := range d2.Custom {
v2 := d.Custom[k]
if v != v2 {
return fmt.Errorf("decoration %v mismatch: %v vs %v", k, v, v2)
}
}
return nil
}

Expand All @@ -77,20 +104,8 @@ func (f Field) equals(f2 Field) error {
if f.Comment != f2.Comment {
return fmt.Errorf("comment mismatch: %q vs %q", f.Comment, f2.Comment)
}
if f.DeprecatedMessage != f2.DeprecatedMessage {
return fmt.Errorf("deprecated message mismatch: %v vs %v", f.DeprecatedMessage, f2.DeprecatedMessage)
}
if f.Deprecated != f2.Deprecated {
return fmt.Errorf("deprecation mismatch: %v vs %v", f.Deprecated, f2.Deprecated)
}
if len(f.Tags) != len(f2.Tags) {
return fmt.Errorf("tag length mismatch: %v vs %v", len(f.Tags), len(f2.Tags))
}
for i, tag := range f.Tags {
tag2 := f2.Tags[i]
if tag != tag2 {
return fmt.Errorf("tag %d mismatch: %v vs %v", i, tag, tag2)
}
if err := f.Decorations.equals(f2.Decorations); err != nil {
return fmt.Errorf("decorations mismatched: %v", err)
}
return f.FieldType.equals(f2.FieldType)
}
Expand Down Expand Up @@ -118,6 +133,9 @@ func (m Message) equals(m2 Message) error {
return fmt.Errorf("field %d mismatched: %v", key, err)
}
}
if err := m.Decorations.equals(m2.Decorations); err != nil {
return fmt.Errorf("decorations mismatched: %v", err)
}
return nil
}

Expand All @@ -142,6 +160,9 @@ func (e Enum) equals(e2 Enum) error {
if e.Unsigned != e2.Unsigned {
return fmt.Errorf("unsigned mismatch: %v vs %v", e.Unsigned, e2.Unsigned)
}
if err := e.Decorations.equals(e2.Decorations); err != nil {
return fmt.Errorf("decorations mismatched: %v", err)
}
return nil
}

Expand All @@ -152,15 +173,12 @@ func (eo EnumOption) equals(eo2 EnumOption) error {
if eo.Comment != eo2.Comment {
return fmt.Errorf("comment mismatch: %q vs %q", eo.Comment, eo2.Comment)
}
if eo.DeprecatedMessage != eo2.DeprecatedMessage {
return fmt.Errorf("deprecated message mismatch: %v vs %v", eo.DeprecatedMessage, eo2.DeprecatedMessage)
}
if eo.Deprecated != eo2.Deprecated {
return fmt.Errorf("deprecated mismatch: %v vs %v", eo.Deprecated, eo2.Deprecated)
}
if eo.Value != eo2.Value {
return fmt.Errorf("value mismatch: %v vs %v", eo.Value, eo2.Value)
}
if err := eo.Decorations.equals(eo2.Decorations); err != nil {
return fmt.Errorf("decorations mismatched: %v", err)
}
return nil
}

Expand Down Expand Up @@ -220,15 +238,15 @@ func (u Union) equals(u2 Union) error {
return fmt.Errorf("field %d mismatch: %w", key, err)
}
}
if err := u.Decorations.equals(u2.Decorations); err != nil {
return fmt.Errorf("decorations mismatched: %v", err)
}
return nil
}

func (uf UnionField) equals(uf2 UnionField) error {
if uf.Deprecated != uf2.Deprecated {
return fmt.Errorf("deprecated mismatch: %v vs %v", uf.Deprecated, uf2.Deprecated)
}
if uf.DeprecatedMessage != uf2.DeprecatedMessage {
return fmt.Errorf("deprecated message mismatch: %v vs %v", uf.DeprecatedMessage, uf2.DeprecatedMessage)
if err := uf.Decorations.equals(uf2.Decorations); err != nil {
return fmt.Errorf("decorations mismatched: %v", err)
}
if (uf.Struct == nil) != (uf2.Struct == nil) {
return fmt.Errorf("field is struct type mismatch: %v vs %v", uf.Struct != nil, uf2.Struct != nil)
Expand All @@ -242,15 +260,6 @@ func (uf UnionField) equals(uf2 UnionField) error {
if uf.Message != nil && uf2.Message != nil {
return uf.Message.equals(*uf2.Message)
}
if len(uf.Tags) != len(uf2.Tags) {
return fmt.Errorf("tag length mismatch: %v vs %v", len(uf.Tags), len(uf2.Tags))
}
for i, tag := range uf.Tags {
tag2 := uf2.Tags[i]
if tag != tag2 {
return fmt.Errorf("tag %d mismatch: %v vs %v", i, tag, tag2)
}
}
return nil
}

Expand All @@ -264,5 +273,8 @@ func (c Const) equals(c2 Const) error {
if c.Value != c2.Value {
return fmt.Errorf("value mismatch: %v vs %v", c.Value, c2.Value)
}
if err := c.Decorations.equals(c2.Decorations); err != nil {
return fmt.Errorf("decorations mismatched: %v", err)
}
return nil
}
28 changes: 15 additions & 13 deletions gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path"
"path/filepath"
"sort"
"strconv"
"strings"

Expand Down Expand Up @@ -36,7 +37,6 @@ type GenerateSettings struct {

GenerateUnsafeMethods bool
SharedMemoryStrings bool
GenerateFieldTags bool
PrivateDefinitions bool
AlwaysUsePointerReceivers bool
}
Expand Down Expand Up @@ -566,13 +566,22 @@ func writeFieldDefinition(fd Field, w *iohelp.ErrorWriter, readOnly bool, messag
if message {
typ = "*" + typ
}
if settings.GenerateFieldTags && len(fd.Tags) != 0 {
if len(fd.Decorations.Custom) != 0 {
formattedTags := []string{}
for _, tag := range fd.Tags {
if tag.Boolean {
formattedTags = append(formattedTags, tag.Key)
orderedTags := make([][2]string, len(fd.Decorations.Custom))
i := 0
for key, value := range fd.Decorations.Custom {
orderedTags[i] = [2]string{key, value}
i++
}
sort.Slice(orderedTags, func(i, j int) bool {
return orderedTags[i][0] < orderedTags[j][0]
})
for _, tag := range orderedTags {
if tag[1] == "" {
formattedTags = append(formattedTags, tag[0])
} else {
formattedTags = append(formattedTags, fmt.Sprintf("%s:%q", tag.Key, tag.Value))
formattedTags = append(formattedTags, fmt.Sprintf("%s:%q", tag[0], tag[1]))
}
}
writeLine(w, "\t%s %s `%s`", name, typ, strings.Join(formattedTags, " "))
Expand Down Expand Up @@ -859,13 +868,6 @@ func writeComment(w *iohelp.ErrorWriter, depth int, comment string, settings Gen

commentLines := strings.Split(comment, "\n")
for _, cm := range commentLines {
// If you have tag comments and are generating them as tags,
// you probably don't want them showing up in your code as comments too.
if settings.GenerateFieldTags {
if _, ok := parseCommentTag(cm); ok {
continue
}
}
writeLine(w, tbs+"//%s", cm)
}
}
Expand Down
4 changes: 0 additions & 4 deletions gen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,6 @@ var genTestFiles = []string{
"union_field",
"date",
"message_1",
"tags",
"typed_enums",
}

Expand Down Expand Up @@ -445,7 +444,6 @@ func TestGenerateToFile(t *testing.T) {
PackageName: "generated",
GenerateUnsafeMethods: true,
SharedMemoryStrings: false,
GenerateFieldTags: true,
})
if err != nil {
t.Fatalf("generation failed: %v", err)
Expand Down Expand Up @@ -483,7 +481,6 @@ func TestGenerateToFile_Private(t *testing.T) {
PackageName: "generated",
GenerateUnsafeMethods: true,
SharedMemoryStrings: false,
GenerateFieldTags: true,
PrivateDefinitions: true,
})
if err != nil {
Expand Down Expand Up @@ -522,7 +519,6 @@ func TestGenerateToFile_AlwaysPointers(t *testing.T) {
PackageName: "generated",
GenerateUnsafeMethods: true,
SharedMemoryStrings: false,
GenerateFieldTags: true,
AlwaysUsePointerReceivers: true,
})
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion gen_union.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func (u Union) Generate(w io.Writer, settings GenerateSettings) {
fd.FieldType.Simple = ufd.Message.Name
}
fd.Name = fd.FieldType.Simple
fd.Tags = ufd.Tags
fd.Decorations = ufd.Decorations
fields = append(fields, fieldWithNumber{
UnionField: ufd,
Field: fd,
Expand Down
2 changes: 0 additions & 2 deletions main/bebopc-go/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ var (
generateUnsafeMethods = flag.Bool("generate-unsafe", false, "whether unchecked additional methods should be generated")
shareStringMemory = flag.Bool("share-string-memory", false, "whether strings read in unmarshalling should share memory with the original byte slice")
combinedImports = flag.Bool("combined-imports", false, "whether imported files should be combined and generated as one, or to separate files")
generateTags = flag.Bool("generate-tags", false, "whether field tags found in comments should be parsed and generated")
privateDefinitions = flag.Bool("private-definitions", false, "whether generated code should be private to the generated package")
pointerReceivers = flag.Bool("force-pointer-receivers", false, "whether generated method receivers must be pointers")
)
Expand Down Expand Up @@ -80,7 +79,6 @@ func run() error {
GenerateUnsafeMethods: *generateUnsafeMethods,
SharedMemoryStrings: *shareStringMemory,
ImportGenerationMode: importMode,
GenerateFieldTags: *generateTags,
PrivateDefinitions: *privateDefinitions,
AlwaysUsePointerReceivers: *pointerReceivers,
}
Expand Down
Loading

0 comments on commit 72cd773

Please sign in to comment.