[chore] [pdata] Migrate the generator to go templates (#7241)

This commit is contained in:
Dmitrii Anoshin 2023-02-27 09:05:50 -08:00 committed by GitHub
parent 6f800ca227
commit 1f5865df1c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 808 additions and 882 deletions

File diff suppressed because it is too large Load Diff

View File

@ -16,35 +16,35 @@ package internal // import "go.opentelemetry.io/collector/pdata/internal/cmd/pda
import ( import (
"bytes" "bytes"
"os" "text/template"
) )
const commonSliceTemplate = `// ${structName} logically represents a slice of ${elementName}. const sliceTemplate = `// {{ .structName }} logically represents a slice of {{ .elementName }}.
// //
// This is a reference type. If passed by value and callee modifies it, the // This is a reference type. If passed by value and callee modifies it, the
// caller will see the modification. // caller will see the modification.
// //
// Must use New${structName} function to create new instances. // Must use New{{ .structName }} function to create new instances.
// Important: zero-initialized instance is not valid for use. // Important: zero-initialized instance is not valid for use.
type ${structName} struct { type {{ .structName }} struct {
orig *[]${originElementType} orig *[]{{ .originElementType }}
} }
func new${structName}(orig *[]${originElementType}) ${structName} { func new{{ .structName }}(orig *[]{{ .originElementType }}) {{ .structName }} {
return ${structName}{orig} return {{ .structName }}{orig}
} }
// New${structName} creates a ${structName} with 0 elements. // New{{ .structName }} creates a {{ .structName }} with 0 elements.
// Can use "EnsureCapacity" to initialize with a given capacity. // Can use "EnsureCapacity" to initialize with a given capacity.
func New${structName}() ${structName} { func New{{ .structName }}() {{ .structName }} {
orig := []${originElementType}(nil) orig := []{{ .originElementType }}(nil)
return new${structName}(&orig) return new{{ .structName }}(&orig)
} }
// Len returns the number of elements in the slice. // Len returns the number of elements in the slice.
// //
// Returns "0" for a newly instance created with "New${structName}()". // Returns "0" for a newly instance created with "New{{ .structName }}()".
func (es ${structName}) Len() int { func (es {{ .structName }}) Len() int {
return len(*es.orig) return len(*es.orig)
} }
@ -55,42 +55,42 @@ func (es ${structName}) Len() int {
// e := es.At(i) // e := es.At(i)
// ... // Do something with the element // ... // Do something with the element
// } // }
func (es ${structName}) At(i int) ${elementName} { func (es {{ .structName }}) At(i int) {{ .elementName }} {
return ${newElement} return {{ .newElement }}
} }
// EnsureCapacity is an operation that ensures the slice has at least the specified capacity. // EnsureCapacity is an operation that ensures the slice has at least the specified capacity.
// 1. If the newCap <= cap then no change in capacity. // 1. If the newCap <= cap then no change in capacity.
// 2. If the newCap > cap then the slice capacity will be expanded to equal newCap. // 2. If the newCap > cap then the slice capacity will be expanded to equal newCap.
// //
// Here is how a new ${structName} can be initialized: // Here is how a new {{ .structName }} can be initialized:
// es := New${structName}() // es := New{{ .structName }}()
// es.EnsureCapacity(4) // es.EnsureCapacity(4)
// for i := 0; i < 4; i++ { // for i := 0; i < 4; i++ {
// e := es.AppendEmpty() // e := es.AppendEmpty()
// // Here should set all the values for e. // // Here should set all the values for e.
// } // }
func (es ${structName}) EnsureCapacity(newCap int) { func (es {{ .structName }}) EnsureCapacity(newCap int) {
oldCap := cap(*es.orig) oldCap := cap(*es.orig)
if newCap <= oldCap { if newCap <= oldCap {
return return
} }
newOrig := make([]${originElementType}, len(*es.orig), newCap) newOrig := make([]{{ .originElementType }}, len(*es.orig), newCap)
copy(newOrig, *es.orig) copy(newOrig, *es.orig)
*es.orig = newOrig *es.orig = newOrig
} }
// AppendEmpty will append to the end of the slice an empty ${elementName}. // AppendEmpty will append to the end of the slice an empty {{ .elementName }}.
// It returns the newly added ${elementName}. // It returns the newly added {{ .elementName }}.
func (es ${structName}) AppendEmpty() ${elementName} { func (es {{ .structName }}) AppendEmpty() {{ .elementName }} {
*es.orig = append(*es.orig, ${emptyOriginElement}) *es.orig = append(*es.orig, {{ .emptyOriginElement }})
return es.At(es.Len() - 1) return es.At(es.Len() - 1)
} }
// MoveAndAppendTo moves all elements from the current slice and appends them to the dest. // MoveAndAppendTo moves all elements from the current slice and appends them to the dest.
// The current slice will be cleared. // The current slice will be cleared.
func (es ${structName}) MoveAndAppendTo(dest ${structName}) { func (es {{ .structName }}) MoveAndAppendTo(dest {{ .structName }}) {
if *dest.orig == nil { if *dest.orig == nil {
// We can simply move the entire vector and avoid any allocations. // We can simply move the entire vector and avoid any allocations.
*dest.orig = *es.orig *dest.orig = *es.orig
@ -102,7 +102,7 @@ func (es ${structName}) MoveAndAppendTo(dest ${structName}) {
// RemoveIf calls f sequentially for each element present in the slice. // RemoveIf calls f sequentially for each element present in the slice.
// If f returns true, the element is removed from the slice. // If f returns true, the element is removed from the slice.
func (es ${structName}) RemoveIf(f func(${elementName}) bool) { func (es {{ .structName }}) RemoveIf(f func({{ .elementName }}) bool) {
newLen := 0 newLen := 0
for i := 0; i < len(*es.orig); i++ { for i := 0; i < len(*es.orig); i++ {
if f(es.At(i)) { if f(es.At(i)) {
@ -118,76 +118,117 @@ func (es ${structName}) RemoveIf(f func(${elementName}) bool) {
} }
// TODO: Prevent memory leak by erasing truncated values. // TODO: Prevent memory leak by erasing truncated values.
*es.orig = (*es.orig)[:newLen] *es.orig = (*es.orig)[:newLen]
}` }
const commonSliceTestTemplate = `func Test${structName}(t *testing.T) {
es := New${structName}() // CopyTo copies all elements from the current slice overriding the destination.
func (es {{ .structName }}) CopyTo(dest {{ .structName }}) {
srcLen := es.Len()
destCap := cap(*dest.orig)
if srcLen <= destCap {
(*dest.orig) = (*dest.orig)[:srcLen:destCap]
{{- if eq .type "sliceOfPtrs" }}
for i := range *es.orig {
new{{ .elementName }}((*es.orig)[i]).CopyTo(new{{ .elementName }}((*dest.orig)[i]))
}
return
}
origs := make([]{{ .originName }}, srcLen)
wrappers := make([]*{{ .originName }}, srcLen)
for i := range *es.orig {
wrappers[i] = &origs[i]
new{{ .elementName }}((*es.orig)[i]).CopyTo(new{{ .elementName }}(wrappers[i]))
}
*dest.orig = wrappers
{{- else }}
} else {
(*dest.orig) = make([]{{ .originElementType }}, srcLen)
}
for i := range *es.orig {
{{ .newElement }}.CopyTo(new{{ .elementName }}(&(*dest.orig)[i]))
}
{{- end }}
}
{{ if eq .type "sliceOfPtrs" -}}
// Sort sorts the {{ .elementName }} elements within {{ .structName }} given the
// provided less function so that two instances of {{ .structName }}
// can be compared.
func (es {{ .structName }}) Sort(less func(a, b {{ .elementName }}) bool) {
sort.SliceStable(*es.orig, func(i, j int) bool { return less(es.At(i), es.At(j)) })
}
{{- end }}`
const sliceTestTemplate = `func Test{{ .structName }}(t *testing.T) {
es := New{{ .structName }}()
assert.Equal(t, 0, es.Len()) assert.Equal(t, 0, es.Len())
es = new${structName}(&[]${originElementType}{}) es = new{{ .structName }}(&[]{{ .originElementType }}{})
assert.Equal(t, 0, es.Len()) assert.Equal(t, 0, es.Len())
emptyVal := New${elementName}() emptyVal := New{{ .elementName }}()
testVal := generateTest${elementName}() testVal := generateTest{{ .elementName }}()
for i := 0; i < 7; i++ { for i := 0; i < 7; i++ {
el := es.AppendEmpty() el := es.AppendEmpty()
assert.Equal(t, emptyVal, es.At(i)) assert.Equal(t, emptyVal, es.At(i))
fillTest${elementName}(el) fillTest{{ .elementName }}(el)
assert.Equal(t, testVal, es.At(i)) assert.Equal(t, testVal, es.At(i))
} }
assert.Equal(t, 7, es.Len()) assert.Equal(t, 7, es.Len())
} }
func Test${structName}_CopyTo(t *testing.T) { func Test{{ .structName }}_CopyTo(t *testing.T) {
dest := New${structName}() dest := New{{ .structName }}()
// Test CopyTo to empty // Test CopyTo to empty
New${structName}().CopyTo(dest) New{{ .structName }}().CopyTo(dest)
assert.Equal(t, New${structName}(), dest) assert.Equal(t, New{{ .structName }}(), dest)
// Test CopyTo larger slice // Test CopyTo larger slice
generateTest${structName}().CopyTo(dest) generateTest{{ .structName }}().CopyTo(dest)
assert.Equal(t, generateTest${structName}(), dest) assert.Equal(t, generateTest{{ .structName }}(), dest)
// Test CopyTo same size slice // Test CopyTo same size slice
generateTest${structName}().CopyTo(dest) generateTest{{ .structName }}().CopyTo(dest)
assert.Equal(t, generateTest${structName}(), dest) assert.Equal(t, generateTest{{ .structName }}(), dest)
} }
func Test${structName}_EnsureCapacity(t *testing.T) { func Test{{ .structName }}_EnsureCapacity(t *testing.T) {
es := generateTest${structName}() es := generateTest{{ .structName }}()
// Test ensure smaller capacity. // Test ensure smaller capacity.
const ensureSmallLen = 4 const ensureSmallLen = 4
es.EnsureCapacity(ensureSmallLen) es.EnsureCapacity(ensureSmallLen)
assert.Less(t, ensureSmallLen, es.Len()) assert.Less(t, ensureSmallLen, es.Len())
assert.Equal(t, es.Len(), cap(*es.orig)) assert.Equal(t, es.Len(), cap(*es.orig))
assert.Equal(t, generateTest${structName}(), es) assert.Equal(t, generateTest{{ .structName }}(), es)
// Test ensure larger capacity // Test ensure larger capacity
const ensureLargeLen = 9 const ensureLargeLen = 9
es.EnsureCapacity(ensureLargeLen) es.EnsureCapacity(ensureLargeLen)
assert.Less(t, generateTest${structName}().Len(), ensureLargeLen) assert.Less(t, generateTest{{ .structName }}().Len(), ensureLargeLen)
assert.Equal(t, ensureLargeLen, cap(*es.orig)) assert.Equal(t, ensureLargeLen, cap(*es.orig))
assert.Equal(t, generateTest${structName}(), es) assert.Equal(t, generateTest{{ .structName }}(), es)
} }
func Test${structName}_MoveAndAppendTo(t *testing.T) { func Test{{ .structName }}_MoveAndAppendTo(t *testing.T) {
// Test MoveAndAppendTo to empty // Test MoveAndAppendTo to empty
expectedSlice := generateTest${structName}() expectedSlice := generateTest{{ .structName }}()
dest := New${structName}() dest := New{{ .structName }}()
src := generateTest${structName}() src := generateTest{{ .structName }}()
src.MoveAndAppendTo(dest) src.MoveAndAppendTo(dest)
assert.Equal(t, generateTest${structName}(), dest) assert.Equal(t, generateTest{{ .structName }}(), dest)
assert.Equal(t, 0, src.Len()) assert.Equal(t, 0, src.Len())
assert.Equal(t, expectedSlice.Len(), dest.Len()) assert.Equal(t, expectedSlice.Len(), dest.Len())
// Test MoveAndAppendTo empty slice // Test MoveAndAppendTo empty slice
src.MoveAndAppendTo(dest) src.MoveAndAppendTo(dest)
assert.Equal(t, generateTest${structName}(), dest) assert.Equal(t, generateTest{{ .structName }}(), dest)
assert.Equal(t, 0, src.Len()) assert.Equal(t, 0, src.Len())
assert.Equal(t, expectedSlice.Len(), dest.Len()) assert.Equal(t, expectedSlice.Len(), dest.Len())
// Test MoveAndAppendTo not empty slice // Test MoveAndAppendTo not empty slice
generateTest${structName}().MoveAndAppendTo(dest) generateTest{{ .structName }}().MoveAndAppendTo(dest)
assert.Equal(t, 2*expectedSlice.Len(), dest.Len()) assert.Equal(t, 2*expectedSlice.Len(), dest.Len())
for i := 0; i < expectedSlice.Len(); i++ { for i := 0; i < expectedSlice.Len(); i++ {
assert.Equal(t, expectedSlice.At(i), dest.At(i)) assert.Equal(t, expectedSlice.At(i), dest.At(i))
@ -195,94 +236,53 @@ func Test${structName}_MoveAndAppendTo(t *testing.T) {
} }
} }
func Test${structName}_RemoveIf(t *testing.T) { func Test{{ .structName }}_RemoveIf(t *testing.T) {
// Test RemoveIf on empty slice // Test RemoveIf on empty slice
emptySlice := New${structName}() emptySlice := New{{ .structName }}()
emptySlice.RemoveIf(func(el ${elementName}) bool { emptySlice.RemoveIf(func(el {{ .elementName }}) bool {
t.Fail() t.Fail()
return false return false
}) })
// Test RemoveIf // Test RemoveIf
filtered := generateTest${structName}() filtered := generateTest{{ .structName }}()
pos := 0 pos := 0
filtered.RemoveIf(func(el ${elementName}) bool { filtered.RemoveIf(func(el {{ .elementName }}) bool {
pos++ pos++
return pos%3 == 0 return pos%3 == 0
}) })
assert.Equal(t, 5, filtered.Len()) assert.Equal(t, 5, filtered.Len())
}`
const slicePtrTemplate = `// CopyTo copies all elements from the current slice overriding the destination.
func (es ${structName}) CopyTo(dest ${structName}) {
srcLen := es.Len()
destCap := cap(*dest.orig)
if srcLen <= destCap {
(*dest.orig) = (*dest.orig)[:srcLen:destCap]
for i := range *es.orig {
new${elementName}((*es.orig)[i]).CopyTo(new${elementName}((*dest.orig)[i]))
}
return
}
origs := make([]${originName}, srcLen)
wrappers := make([]*${originName}, srcLen)
for i := range *es.orig {
wrappers[i] = &origs[i]
new${elementName}((*es.orig)[i]).CopyTo(new${elementName}(wrappers[i]))
}
*dest.orig = wrappers
} }
// Sort sorts the ${elementName} elements within ${structName} given the {{ if eq .type "sliceOfPtrs" -}}
// provided less function so that two instances of ${structName} func Test{{ .structName }}_Sort(t *testing.T) {
// can be compared. es := generateTest{{ .structName }}()
func (es ${structName}) Sort(less func(a, b ${elementName}) bool) { es.Sort(func(a, b {{ .elementName }}) bool {
sort.SliceStable(*es.orig, func(i, j int) bool { return less(es.At(i), es.At(j)) })
}`
// TODO: Use assert.Less once https://github.com/stretchr/testify/pull/1339 is merged.
const slicePtrTestTemplate = `func Test${structName}_Sort(t *testing.T) {
es := generateTest${structName}()
es.Sort(func(a, b ${elementName}) bool {
return uintptr(unsafe.Pointer(a.orig)) < uintptr(unsafe.Pointer(b.orig)) return uintptr(unsafe.Pointer(a.orig)) < uintptr(unsafe.Pointer(b.orig))
}) })
for i := 1; i < es.Len(); i++ { for i := 1; i < es.Len(); i++ {
assert.True(t, uintptr(unsafe.Pointer(es.At(i-1).orig)) < uintptr(unsafe.Pointer(es.At(i).orig))) assert.True(t, uintptr(unsafe.Pointer(es.At(i-1).orig)) < uintptr(unsafe.Pointer(es.At(i).orig)))
} }
es.Sort(func(a, b ${elementName}) bool { es.Sort(func(a, b {{ .elementName }}) bool {
return uintptr(unsafe.Pointer(a.orig)) > uintptr(unsafe.Pointer(b.orig)) return uintptr(unsafe.Pointer(a.orig)) > uintptr(unsafe.Pointer(b.orig))
}) })
for i := 1; i < es.Len(); i++ { for i := 1; i < es.Len(); i++ {
assert.True(t, uintptr(unsafe.Pointer(es.At(i-1).orig)) > uintptr(unsafe.Pointer(es.At(i).orig))) assert.True(t, uintptr(unsafe.Pointer(es.At(i-1).orig)) > uintptr(unsafe.Pointer(es.At(i).orig)))
} }
}` }
{{- end }}`
const sliceValueTemplate = `// CopyTo copies all elements from the current slice overriding the destination. const sliceGenerateTest = `func generateTest{{ .structName }}() {{ .structName }} {
func (es ${structName}) CopyTo(dest ${structName}) { es := New{{ .structName }}()
srcLen := es.Len() fillTest{{ .structName }}(es)
destCap := cap(*dest.orig)
if srcLen <= destCap {
(*dest.orig) = (*dest.orig)[:srcLen:destCap]
} else {
(*dest.orig) = make([]${originName}, srcLen)
}
for i := range *es.orig {
new${elementName}(&(*es.orig)[i]).CopyTo(new${elementName}(&(*dest.orig)[i]))
}
}`
const commonSliceGenerateTest = `func generateTest${structName}() ${structName} {
es := New${structName}()
fillTest${structName}(es)
return es return es
} }
func fillTest${structName}(es ${structName}) { func fillTest{{ .structName }}(es {{ .structName }}) {
*es.orig = make([]${originElementType}, 7) *es.orig = make([]{{ .originElementType }}, 7)
for i := 0; i < 7; i++ { for i := 0; i < 7; i++ {
(*es.orig)[i] = ${emptyOriginElement} (*es.orig)[i] = {{ .emptyOriginElement }}
fillTest${elementName}(${newElement}) fillTest{{ .elementName }}({{ .newElement }})
} }
}` }`
@ -307,37 +307,35 @@ func (ss *sliceOfPtrs) getPackageName() string {
} }
func (ss *sliceOfPtrs) generateStruct(sb *bytes.Buffer) { func (ss *sliceOfPtrs) generateStruct(sb *bytes.Buffer) {
sb.WriteString(os.Expand(commonSliceTemplate, ss.templateFields()) + newLine + newLine) t := template.Must(template.New("sliceTemplate").Parse(sliceTemplate))
sb.WriteString(os.Expand(slicePtrTemplate, ss.templateFields())) if err := t.Execute(sb, ss.templateFields()); err != nil {
panic(err)
}
} }
func (ss *sliceOfPtrs) generateTests(sb *bytes.Buffer) { func (ss *sliceOfPtrs) generateTests(sb *bytes.Buffer) {
sb.WriteString(os.Expand(commonSliceTestTemplate, ss.templateFields()) + newLine + newLine) t := template.Must(template.New("sliceTestTemplate").Parse(sliceTestTemplate))
sb.WriteString(os.Expand(slicePtrTestTemplate, ss.templateFields())) if err := t.Execute(sb, ss.templateFields()); err != nil {
panic(err)
}
} }
func (ss *sliceOfPtrs) generateTestValueHelpers(sb *bytes.Buffer) { func (ss *sliceOfPtrs) generateTestValueHelpers(sb *bytes.Buffer) {
sb.WriteString(os.Expand(commonSliceGenerateTest, ss.templateFields())) t := template.Must(template.New("sliceGenerateTest").Parse(sliceGenerateTest))
if err := t.Execute(sb, ss.templateFields()); err != nil {
panic(err)
}
} }
func (ss *sliceOfPtrs) templateFields() func(name string) string { func (ss *sliceOfPtrs) templateFields() map[string]any {
return func(name string) string { return map[string]any{
switch name { "type": "sliceOfPtrs",
case "structName": "structName": ss.structName,
return ss.structName "elementName": ss.element.structName,
case "elementName": "originName": ss.element.originFullName,
return ss.element.structName "originElementType": "*" + ss.element.originFullName,
case "originName": "emptyOriginElement": "&" + ss.element.originFullName + "{}",
return ss.element.originFullName "newElement": "new" + ss.element.structName + "((*es.orig)[i])",
case "originElementType":
return "*" + ss.element.originFullName
case "emptyOriginElement":
return "&" + ss.element.originFullName + "{}"
case "newElement":
return "new" + ss.element.structName + "((*es.orig)[i])"
default:
panic(name)
}
} }
} }
@ -361,36 +359,35 @@ func (ss *sliceOfValues) getPackageName() string {
} }
func (ss *sliceOfValues) generateStruct(sb *bytes.Buffer) { func (ss *sliceOfValues) generateStruct(sb *bytes.Buffer) {
sb.WriteString(os.Expand(commonSliceTemplate, ss.templateFields()) + newLine + newLine) t := template.Must(template.New("sliceTemplate").Parse(sliceTemplate))
sb.WriteString(os.Expand(sliceValueTemplate, ss.templateFields())) if err := t.Execute(sb, ss.templateFields()); err != nil {
panic(err)
}
} }
func (ss *sliceOfValues) generateTests(sb *bytes.Buffer) { func (ss *sliceOfValues) generateTests(sb *bytes.Buffer) {
sb.WriteString(os.Expand(commonSliceTestTemplate, ss.templateFields())) t := template.Must(template.New("sliceTestTemplate").Parse(sliceTestTemplate))
if err := t.Execute(sb, ss.templateFields()); err != nil {
panic(err)
}
} }
func (ss *sliceOfValues) generateTestValueHelpers(sb *bytes.Buffer) { func (ss *sliceOfValues) generateTestValueHelpers(sb *bytes.Buffer) {
sb.WriteString(os.Expand(commonSliceGenerateTest, ss.templateFields())) t := template.Must(template.New("sliceGenerateTest").Parse(sliceGenerateTest))
if err := t.Execute(sb, ss.templateFields()); err != nil {
panic(err)
}
} }
func (ss *sliceOfValues) templateFields() func(name string) string { func (ss *sliceOfValues) templateFields() map[string]any {
return func(name string) string { return map[string]any{
switch name { "type": "sliceOfValues",
case "structName": "structName": ss.structName,
return ss.structName "elementName": ss.element.structName,
case "elementName": "originName": ss.element.originFullName,
return ss.element.structName "originElementType": ss.element.originFullName,
case "originName": "emptyOriginElement": ss.element.originFullName + "{}",
return ss.element.originFullName "newElement": "new" + ss.element.structName + "(&(*es.orig)[i])",
case "originElementType":
return ss.element.originFullName
case "emptyOriginElement":
return ss.element.originFullName + "{}"
case "newElement":
return "new" + ss.element.structName + "(&(*es.orig)[i])"
default:
panic(name)
}
} }
} }

View File

@ -16,93 +16,116 @@ package internal // import "go.opentelemetry.io/collector/pdata/internal/cmd/pda
import ( import (
"bytes" "bytes"
"os" "strings"
"text/template"
) )
const messageValueTemplate = `${description} const messageValueTemplate = `{{ .description }}
// //
// This is a reference type, if passed by value and callee modifies it the // This is a reference type, if passed by value and callee modifies it the
// caller will see the modification. // caller will see the modification.
// //
// Must use New${structName} function to create new instances. // Must use New{{ .structName }} function to create new instances.
// Important: zero-initialized instance is not valid for use. // Important: zero-initialized instance is not valid for use.
${typeDeclaration} {{- if .isCommon }}
type {{ .structName }} internal.{{ .structName }}
{{- else }}
type {{ .structName }} struct {
orig *{{ .originName }}
}
{{- end }}
func new${structName}(orig *${originName}) ${structName} { func new{{ .structName }}(orig *{{ .originName }}) {{ .structName }} {
return ${newFuncValue} {{- if .isCommon }}
return {{ .structName }}(internal.New{{ .structName }}(orig))
{{- else }}
return {{ .structName }}{orig}
{{- end }}
} }
// New${structName} creates a new empty ${structName}. // New{{ .structName }} creates a new empty {{ .structName }}.
// //
// This must be used only in testing code. Users should use "AppendEmpty" when part of a Slice, // This must be used only in testing code. Users should use "AppendEmpty" when part of a Slice,
// OR directly access the member if this is embedded in another struct. // OR directly access the member if this is embedded in another struct.
func New${structName}() ${structName} { func New{{ .structName }}() {{ .structName }} {
return new${structName}(&${originName}{}) return new{{ .structName }}(&{{ .originName }}{})
} }
// MoveTo moves all properties from the current struct overriding the destination and // MoveTo moves all properties from the current struct overriding the destination and
// resetting the current instance to its zero value // resetting the current instance to its zero value
func (ms ${structName}) MoveTo(dest ${structName}) { func (ms {{ .structName }}) MoveTo(dest {{ .structName }}) {
*dest.${origAccessor} = *ms.${origAccessor} *dest.{{ .origAccessor }} = *ms.{{ .origAccessor }}
*ms.${origAccessor} = ${originName}{} *ms.{{ .origAccessor }} = {{ .originName }}{}
}`
const messageValueGetOrigTemplate = `func (ms ${structName}) getOrig() *${originName} {
return internal.GetOrig${structName}(internal.${structName}(ms))
}`
const messageValueCopyToHeaderTemplate = `// CopyTo copies all properties from the current struct overriding the destination.
func (ms ${structName}) CopyTo(dest ${structName}) {`
const messageValueCopyToFooterTemplate = `}`
const messageValueTestTemplate = `
func Test${structName}_MoveTo(t *testing.T) {
ms := ${generateTestData}
dest := New${structName}()
ms.MoveTo(dest)
assert.Equal(t, New${structName}(), ms)
assert.Equal(t, ${generateTestData}, dest)
} }
func Test${structName}_CopyTo(t *testing.T) { {{ if .isCommon -}}
ms := New${structName}() func (ms {{ .structName }}) getOrig() *{{ .originName }} {
orig := New${structName}() return internal.GetOrig{{ .structName }}(internal.{{ .structName }}(ms))
orig.CopyTo(ms) }
assert.Equal(t, orig, ms) {{- end }}
orig = ${generateTestData}
orig.CopyTo(ms) {{ range .fields -}}
assert.Equal(t, orig, ms) {{ .GenerateAccessors $.messageStruct }}
{{ end }}
// CopyTo copies all properties from the current struct overriding the destination.
func (ms {{ .structName }}) CopyTo(dest {{ .structName }}) {
{{- range .fields }}
{{ .GenerateCopyToValue $.messageStruct }}
{{- end }}
}` }`
const messageValueGenerateTestTemplate = `func generateTest${structName}() ${structName} { const messageValueTestTemplate = `
tv := New${structName}() func Test{{ .structName }}_MoveTo(t *testing.T) {
fillTest${structName}(tv) ms := {{ .generateTestData }}
return tv dest := New{{ .structName }}()
}` ms.MoveTo(dest)
assert.Equal(t, New{{ .structName }}(), ms)
assert.Equal(t, {{ .generateTestData }}, dest)
}
const messageValueGenerateTestTemplateCommon = `func GenerateTest${structName}() ${structName} { func Test{{ .structName }}_CopyTo(t *testing.T) {
orig := ${originName}{} ms := New{{ .structName }}()
tv := New${structName}(&orig) orig := New{{ .structName }}()
FillTest${structName}(tv) orig.CopyTo(ms)
assert.Equal(t, orig, ms)
orig = {{ .generateTestData }}
orig.CopyTo(ms)
assert.Equal(t, orig, ms)
}
{{ range .fields }}
{{ .GenerateAccessorsTest $.messageStruct }}
{{ end }}`
const messageValueGenerateTestTemplate = `func {{ upperIfInternal "g" }}enerateTest{{ .structName }}() {{ .structName }} {
{{- if .isCommon }}
orig := {{ .originName }}{}
{{- end }}
tv := New{{ .structName }}({{ if .isCommon }}&orig{{ end }})
{{ upperIfInternal "f" }}illTest{{ .structName }}(tv)
return tv return tv
}
func {{ upperIfInternal "f" }}illTest{{ .structName }}(tv {{ .structName }}) {
{{- range .fields }}
{{ .GenerateSetWithTestValue $.messageStruct }}
{{- end }}
}` }`
const messageValueAliasTemplate = ` const messageValueAliasTemplate = `
type ${structName} struct { type {{ .structName }} struct {
orig *${originName} orig *{{ .originName }}
} }
func GetOrig${structName}(ms ${structName}) *${originName} { func GetOrig{{ .structName }}(ms {{ .structName }}) *{{ .originName }} {
return ms.orig return ms.orig
} }
func New${structName}(orig *${originName}) ${structName} { func New{{ .structName }}(orig *{{ .originName }}) {{ .structName }} {
return ${structName}{orig: orig} return {{ .structName }}{orig: orig}
}` }`
const newLine = "\n"
type baseStruct interface { type baseStruct interface {
getName() string getName() string
getPackageName() string getPackageName() string
@ -131,96 +154,56 @@ func (ms *messageValueStruct) getPackageName() string {
} }
func (ms *messageValueStruct) generateStruct(sb *bytes.Buffer) { func (ms *messageValueStruct) generateStruct(sb *bytes.Buffer) {
sb.WriteString(os.Expand(messageValueTemplate, ms.templateFields())) t := template.Must(template.New("messageValueTemplate").Parse(messageValueTemplate))
if usedByOtherDataTypes(ms.packageName) { if err := t.Execute(sb, ms.templateFields()); err != nil {
sb.WriteString(newLine + newLine) panic(err)
sb.WriteString(os.Expand(messageValueGetOrigTemplate, ms.templateFields()))
} }
// Write accessors for the struct
for _, f := range ms.fields {
sb.WriteString(newLine + newLine)
f.generateAccessors(ms, sb)
}
sb.WriteString(newLine + newLine)
sb.WriteString(os.Expand(messageValueCopyToHeaderTemplate, ms.templateFields()))
// Write accessors CopyTo for the struct
for _, f := range ms.fields {
sb.WriteString(newLine)
f.generateCopyToValue(ms, sb)
}
sb.WriteString(newLine)
sb.WriteString(messageValueCopyToFooterTemplate)
} }
func (ms *messageValueStruct) generateTests(sb *bytes.Buffer) { func (ms *messageValueStruct) generateTests(sb *bytes.Buffer) {
sb.WriteString(os.Expand(messageValueTestTemplate, ms.templateFields())) t := template.Must(template.New("messageValueTestTemplate").Parse(messageValueTestTemplate))
if err := t.Execute(sb, ms.templateFields()); err != nil {
// Write accessors tests for the struct panic(err)
for _, f := range ms.fields {
sb.WriteString(newLine + newLine)
f.generateAccessorsTest(ms, sb)
} }
} }
func (ms *messageValueStruct) generateTestValueHelpers(sb *bytes.Buffer) { func (ms *messageValueStruct) generateTestValueHelpers(sb *bytes.Buffer) {
// Write generateTest func for the struct funcs := template.FuncMap{
template := messageValueGenerateTestTemplate "upperIfInternal": func(in string) string {
if usedByOtherDataTypes(ms.packageName) { if usedByOtherDataTypes(ms.packageName) {
template = messageValueGenerateTestTemplateCommon return strings.ToUpper(in)
}
return in
},
} }
sb.WriteString(os.Expand(template, ms.templateFields())) t := template.Must(template.New("messageValueGenerateTestTemplate").Funcs(funcs).Parse(messageValueGenerateTestTemplate))
if err := t.Execute(sb, ms.templateFields()); err != nil {
// Write fillTest func for the struct panic(err)
sb.WriteString(newLine + newLine + "func ")
if usedByOtherDataTypes(ms.packageName) {
sb.WriteString("FillTest")
} else {
sb.WriteString("fillTest")
} }
sb.WriteString(ms.structName + "(tv " + ms.structName + ") {")
for _, f := range ms.fields {
sb.WriteString(newLine)
f.generateSetWithTestValue(ms, sb)
}
sb.WriteString(newLine + "}" + newLine)
} }
func (ms *messageValueStruct) generateInternal(sb *bytes.Buffer) { func (ms *messageValueStruct) generateInternal(sb *bytes.Buffer) {
sb.WriteString(os.Expand(messageValueAliasTemplate, ms.templateFields())) t := template.Must(template.New("messageValueAliasTemplate").Parse(messageValueAliasTemplate))
sb.WriteString(newLine + newLine) if err := t.Execute(sb, ms.templateFields()); err != nil {
panic(err)
}
} }
func (ms *messageValueStruct) templateFields() func(name string) string { func (ms *messageValueStruct) templateFields() map[string]any {
return func(name string) string { return map[string]any{
switch name { "messageStruct": ms,
case "structName": "fields": ms.fields,
return ms.structName "structName": ms.structName,
case "originName": "originName": ms.originFullName,
return ms.originFullName "generateTestData": func() string {
case "generateTestData":
if usedByOtherDataTypes(ms.packageName) { if usedByOtherDataTypes(ms.packageName) {
return ms.structName + "(internal.GenerateTest" + ms.structName + "())" return ms.structName + "(internal.GenerateTest" + ms.structName + "())"
} }
return "generateTest" + ms.structName + "()" return "generateTest" + ms.structName + "()"
case "description": }(),
return ms.description "description": ms.description,
case "typeDeclaration": "isCommon": usedByOtherDataTypes(ms.packageName),
if usedByOtherDataTypes(ms.packageName) { "origAccessor": origAccessor(ms),
return "type " + ms.structName + " internal." + ms.structName
}
return "type " + ms.structName + " struct {\n\torig *" + ms.originFullName + "\n}"
case "newFuncValue":
if usedByOtherDataTypes(ms.packageName) {
return ms.structName + "(internal.New" + ms.structName + "(orig))"
}
return ms.structName + "{orig}"
case "origAccessor":
return origAccessor(ms)
default:
panic(name)
}
} }
} }

View File

@ -59,6 +59,8 @@ type Package struct {
structs []baseStruct structs []baseStruct
} }
const newLine = "\n"
// GenerateFiles generates files with the configured data structures for this Package. // GenerateFiles generates files with the configured data structures for this Package.
func (p *Package) GenerateFiles() error { func (p *Package) GenerateFiles() error {
for _, s := range p.structs { for _, s := range p.structs {

View File

@ -16,136 +16,136 @@ package internal // import "go.opentelemetry.io/collector/pdata/internal/cmd/pda
import ( import (
"bytes" "bytes"
"os"
"strings" "strings"
"text/template"
) )
const primitiveSliceTemplate = `// ${structName} represents a []${itemType} slice. const primitiveSliceTemplate = `// {{ .structName }} represents a []{{ .itemType }} slice.
// The instance of ${structName} can be assigned to multiple objects since it's immutable. // The instance of {{ .structName }} can be assigned to multiple objects since it's immutable.
// //
// Must use New${structName} function to create new instances. // Must use New{{ .structName }} function to create new instances.
// Important: zero-initialized instance is not valid for use. // Important: zero-initialized instance is not valid for use.
type ${structName} internal.${structName} type {{ .structName }} internal.{{ .structName }}
func (ms ${structName}) getOrig() *[]${itemType} { func (ms {{ .structName }}) getOrig() *[]{{ .itemType }} {
return internal.GetOrig${structName}(internal.${structName}(ms)) return internal.GetOrig{{ .structName }}(internal.{{ .structName }}(ms))
} }
// New${structName} creates a new empty ${structName}. // New{{ .structName }} creates a new empty {{ .structName }}.
func New${structName}() ${structName} { func New{{ .structName }}() {{ .structName }} {
orig := []${itemType}(nil) orig := []{{ .itemType }}(nil)
return ${structName}(internal.New${structName}(&orig)) return {{ .structName }}(internal.New{{ .structName }}(&orig))
} }
// AsRaw returns a copy of the []${itemType} slice. // AsRaw returns a copy of the []{{ .itemType }} slice.
func (ms ${structName}) AsRaw() []${itemType} { func (ms {{ .structName }}) AsRaw() []{{ .itemType }} {
return copy${structName}(nil, *ms.getOrig()) return copy{{ .structName }}(nil, *ms.getOrig())
} }
// FromRaw copies raw []${itemType} into the slice ${structName}. // FromRaw copies raw []{{ .itemType }} into the slice {{ .structName }}.
func (ms ${structName}) FromRaw(val []${itemType}) { func (ms {{ .structName }}) FromRaw(val []{{ .itemType }}) {
*ms.getOrig() = copy${structName}(*ms.getOrig(), val) *ms.getOrig() = copy{{ .structName }}(*ms.getOrig(), val)
} }
// Len returns length of the []${itemType} slice value. // Len returns length of the []{{ .itemType }} slice value.
// Equivalent of len(${lowerStructName}). // Equivalent of len({{ .lowerStructName }}).
func (ms ${structName}) Len() int { func (ms {{ .structName }}) Len() int {
return len(*ms.getOrig()) return len(*ms.getOrig())
} }
// At returns an item from particular index. // At returns an item from particular index.
// Equivalent of ${lowerStructName}[i]. // Equivalent of {{ .lowerStructName }}[i].
func (ms ${structName}) At(i int) ${itemType} { func (ms {{ .structName }}) At(i int) {{ .itemType }} {
return (*ms.getOrig())[i] return (*ms.getOrig())[i]
} }
// SetAt sets ${itemType} item at particular index. // SetAt sets {{ .itemType }} item at particular index.
// Equivalent of ${lowerStructName}[i] = val // Equivalent of {{ .lowerStructName }}[i] = val
func (ms ${structName}) SetAt(i int, val ${itemType}) { func (ms {{ .structName }}) SetAt(i int, val {{ .itemType }}) {
(*ms.getOrig())[i] = val (*ms.getOrig())[i] = val
} }
// EnsureCapacity ensures ${structName} has at least the specified capacity. // EnsureCapacity ensures {{ .structName }} has at least the specified capacity.
// 1. If the newCap <= cap, then is no change in capacity. // 1. If the newCap <= cap, then is no change in capacity.
// 2. If the newCap > cap, then the slice capacity will be expanded to the provided value which will be equivalent of: // 2. If the newCap > cap, then the slice capacity will be expanded to the provided value which will be equivalent of:
// buf := make([]${itemType}, len(${lowerStructName}), newCap) // buf := make([]{{ .itemType }}, len({{ .lowerStructName }}), newCap)
// copy(buf, ${lowerStructName}) // copy(buf, {{ .lowerStructName }})
// ${lowerStructName} = buf // {{ .lowerStructName }} = buf
func (ms ${structName}) EnsureCapacity(newCap int) { func (ms {{ .structName }}) EnsureCapacity(newCap int) {
oldCap := cap(*ms.getOrig()) oldCap := cap(*ms.getOrig())
if newCap <= oldCap { if newCap <= oldCap {
return return
} }
newOrig := make([]${itemType}, len(*ms.getOrig()), newCap) newOrig := make([]{{ .itemType }}, len(*ms.getOrig()), newCap)
copy(newOrig, *ms.getOrig()) copy(newOrig, *ms.getOrig())
*ms.getOrig() = newOrig *ms.getOrig() = newOrig
} }
// Append appends extra elements to ${structName}. // Append appends extra elements to {{ .structName }}.
// Equivalent of ${lowerStructName} = append(${lowerStructName}, elms...) // Equivalent of {{ .lowerStructName }} = append({{ .lowerStructName }}, elms...)
func (ms ${structName}) Append(elms ...${itemType}) { func (ms {{ .structName }}) Append(elms ...{{ .itemType }}) {
*ms.getOrig() = append(*ms.getOrig(), elms...) *ms.getOrig() = append(*ms.getOrig(), elms...)
} }
// MoveTo moves all elements from the current slice overriding the destination and // MoveTo moves all elements from the current slice overriding the destination and
// resetting the current instance to its zero value. // resetting the current instance to its zero value.
func (ms ${structName}) MoveTo(dest ${structName}) { func (ms {{ .structName }}) MoveTo(dest {{ .structName }}) {
*dest.getOrig() = *ms.getOrig() *dest.getOrig() = *ms.getOrig()
*ms.getOrig() = nil *ms.getOrig() = nil
} }
// CopyTo copies all elements from the current slice overriding the destination. // CopyTo copies all elements from the current slice overriding the destination.
func (ms ${structName}) CopyTo(dest ${structName}) { func (ms {{ .structName }}) CopyTo(dest {{ .structName }}) {
*dest.getOrig() = copy${structName}(*dest.getOrig(), *ms.getOrig()) *dest.getOrig() = copy{{ .structName }}(*dest.getOrig(), *ms.getOrig())
} }
func copy${structName}(dst, src []${itemType}) []${itemType} { func copy{{ .structName }}(dst, src []{{ .itemType }}) []{{ .itemType }} {
dst = dst[:0] dst = dst[:0]
return append(dst, src...) return append(dst, src...)
}` }`
const immutableSliceTestTemplate = `func TestNew${structName}(t *testing.T) { const immutableSliceTestTemplate = `func TestNew{{ .structName }}(t *testing.T) {
ms := New${structName}() ms := New{{ .structName }}()
assert.Equal(t, 0, ms.Len()) assert.Equal(t, 0, ms.Len())
ms.FromRaw([]${itemType}{1, 2, 3}) ms.FromRaw([]{{ .itemType }}{1, 2, 3})
assert.Equal(t, 3, ms.Len()) assert.Equal(t, 3, ms.Len())
assert.Equal(t, []${itemType}{1, 2, 3}, ms.AsRaw()) assert.Equal(t, []{{ .itemType }}{1, 2, 3}, ms.AsRaw())
ms.SetAt(1, ${itemType}(5)) ms.SetAt(1, {{ .itemType }}(5))
assert.Equal(t, []${itemType}{1, 5, 3}, ms.AsRaw()) assert.Equal(t, []{{ .itemType }}{1, 5, 3}, ms.AsRaw())
ms.FromRaw([]${itemType}{3}) ms.FromRaw([]{{ .itemType }}{3})
assert.Equal(t, 1, ms.Len()) assert.Equal(t, 1, ms.Len())
assert.Equal(t, ${itemType}(3), ms.At(0)) assert.Equal(t, {{ .itemType }}(3), ms.At(0))
cp := New${structName}() cp := New{{ .structName }}()
ms.CopyTo(cp) ms.CopyTo(cp)
ms.SetAt(0, ${itemType}(2)) ms.SetAt(0, {{ .itemType }}(2))
assert.Equal(t, ${itemType}(2), ms.At(0)) assert.Equal(t, {{ .itemType }}(2), ms.At(0))
assert.Equal(t, ${itemType}(3), cp.At(0)) assert.Equal(t, {{ .itemType }}(3), cp.At(0))
ms.CopyTo(cp) ms.CopyTo(cp)
assert.Equal(t, ${itemType}(2), cp.At(0)) assert.Equal(t, {{ .itemType }}(2), cp.At(0))
mv := New${structName}() mv := New{{ .structName }}()
ms.MoveTo(mv) ms.MoveTo(mv)
assert.Equal(t, 0, ms.Len()) assert.Equal(t, 0, ms.Len())
assert.Equal(t, 1, mv.Len()) assert.Equal(t, 1, mv.Len())
assert.Equal(t, ${itemType}(2), mv.At(0)) assert.Equal(t, {{ .itemType }}(2), mv.At(0))
ms.FromRaw([]${itemType}{1, 2, 3}) ms.FromRaw([]{{ .itemType }}{1, 2, 3})
ms.MoveTo(mv) ms.MoveTo(mv)
assert.Equal(t, 3, mv.Len()) assert.Equal(t, 3, mv.Len())
assert.Equal(t, ${itemType}(1), mv.At(0)) assert.Equal(t, {{ .itemType }}(1), mv.At(0))
} }
func Test${structName}Append(t *testing.T) { func Test{{ .structName }}Append(t *testing.T) {
ms := New${structName}() ms := New{{ .structName }}()
ms.FromRaw([]${itemType}{1, 2, 3}) ms.FromRaw([]{{ .itemType }}{1, 2, 3})
ms.Append(4, 5) ms.Append(4, 5)
assert.Equal(t, 5, ms.Len()) assert.Equal(t, 5, ms.Len())
assert.Equal(t, ${itemType}(5), ms.At(4)) assert.Equal(t, {{ .itemType }}(5), ms.At(4))
} }
func Test${structName}EnsureCapacity(t *testing.T) { func Test{{ .structName }}EnsureCapacity(t *testing.T) {
ms := New${structName}() ms := New{{ .structName }}()
ms.EnsureCapacity(4) ms.EnsureCapacity(4)
assert.Equal(t, 4, cap(*ms.getOrig())) assert.Equal(t, 4, cap(*ms.getOrig()))
ms.EnsureCapacity(2) ms.EnsureCapacity(2)
@ -153,16 +153,16 @@ func Test${structName}EnsureCapacity(t *testing.T) {
}` }`
const primitiveSliceInternalTemplate = ` const primitiveSliceInternalTemplate = `
type ${structName} struct { type {{ .structName }} struct {
orig *[]${itemType} orig *[]{{ .itemType }}
} }
func GetOrig${structName}(ms ${structName}) *[]${itemType} { func GetOrig{{ .structName }}(ms {{ .structName }}) *[]{{ .itemType }} {
return ms.orig return ms.orig
} }
func New${structName}(orig *[]${itemType}) ${structName} { func New{{ .structName }}(orig *[]{{ .itemType }}) {{ .structName }} {
return ${structName}{orig: orig} return {{ .structName }}{orig: orig}
}` }`
// primitiveSliceStruct generates a struct for a slice of primitive value elements. The structs are always generated // primitiveSliceStruct generates a struct for a slice of primitive value elements. The structs are always generated
@ -182,44 +182,32 @@ func (iss *primitiveSliceStruct) getPackageName() string {
} }
func (iss *primitiveSliceStruct) generateStruct(sb *bytes.Buffer) { func (iss *primitiveSliceStruct) generateStruct(sb *bytes.Buffer) {
sb.WriteString(os.Expand(primitiveSliceTemplate, func(name string) string { t := template.Must(template.New("primitiveSliceTemplate").Parse(primitiveSliceTemplate))
switch name { if err := t.Execute(sb, iss.templateFields()); err != nil {
case "structName": panic(err)
return iss.structName }
case "lowerStructName":
return strings.ToLower(iss.structName[:1]) + iss.structName[1:]
case "itemType":
return iss.itemType
default:
panic(name)
}
}))
} }
func (iss *primitiveSliceStruct) generateTests(sb *bytes.Buffer) { func (iss *primitiveSliceStruct) generateTests(sb *bytes.Buffer) {
sb.WriteString(os.Expand(immutableSliceTestTemplate, func(name string) string { t := template.Must(template.New("immutableSliceTestTemplate").Parse(immutableSliceTestTemplate))
switch name { if err := t.Execute(sb, iss.templateFields()); err != nil {
case "structName": panic(err)
return iss.structName }
case "itemType":
return iss.itemType
default:
panic(name)
}
}))
} }
func (iss *primitiveSliceStruct) generateTestValueHelpers(*bytes.Buffer) {} func (iss *primitiveSliceStruct) generateTestValueHelpers(*bytes.Buffer) {}
func (iss *primitiveSliceStruct) generateInternal(sb *bytes.Buffer) { func (iss *primitiveSliceStruct) generateInternal(sb *bytes.Buffer) {
sb.WriteString(os.Expand(primitiveSliceInternalTemplate, func(name string) string { t := template.Must(template.New("primitiveSliceInternalTemplate").Parse(primitiveSliceInternalTemplate))
switch name { if err := t.Execute(sb, iss.templateFields()); err != nil {
case "structName": panic(err)
return iss.structName }
case "itemType": }
return iss.itemType
default: func (iss *primitiveSliceStruct) templateFields() map[string]any {
panic(name) return map[string]any{
} "structName": iss.structName,
})) "itemType": iss.itemType,
"lowerStructName": strings.ToLower(iss.structName[:1]) + iss.structName[1:],
}
} }

View File

@ -133,7 +133,6 @@ func (es ExemplarSlice) CopyTo(dest ExemplarSlice) {
} else { } else {
(*dest.orig) = make([]otlpmetrics.Exemplar, srcLen) (*dest.orig) = make([]otlpmetrics.Exemplar, srcLen)
} }
for i := range *es.orig { for i := range *es.orig {
newExemplar(&(*es.orig)[i]).CopyTo(newExemplar(&(*dest.orig)[i])) newExemplar(&(*es.orig)[i]).CopyTo(newExemplar(&(*dest.orig)[i]))
} }