Skip to content

Commit c4a24a4

Browse files
refactor:
- replace DefaultValue type with any for default values in config metadata - add go_struct.ignore_default flag to clear default for given field
1 parent 0b7f0bb commit c4a24a4

8 files changed

Lines changed: 121 additions & 146 deletions

File tree

cmd/mdatagen/internal/cfggen/generation.go

Lines changed: 61 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,14 @@ func NewCfgFns(rootPackage, componentPackage string) map[string]any {
7171
return name
7272
},
7373
"camelVar": CamelVar,
74-
"formatDefaultValue": func(md *ConfigMetadata, name string, defaultValue DefaultValue) string {
74+
"formatDefaultValue": func(md *ConfigMetadata, name string, defaultValue any) string {
7575
return FormatDefaultValue(md, name, defaultValue, rootPackage, componentPackage)
7676
},
77-
"formatBaseValue": func(md *ConfigMetadata, name string, defaultValue DefaultValue) string {
77+
"formatBaseValue": func(md *ConfigMetadata, name string, defaultValue any) string {
7878
return FormatBaseValue(md, name, defaultValue, rootPackage, componentPackage)
7979
},
8080
"wrapDefaultValue": WrapDefaultValue,
81-
"mapCustomDefaults": func(schema *ConfigMetadata, defaultValue DefaultValue) []string {
81+
"mapCustomDefaults": func(schema *ConfigMetadata, defaultValue any) []string {
8282
return MapCustomDefaults(schema, defaultValue, rootPackage, componentPackage)
8383
},
8484
"hasDefaultValue": hasDefaultValue,
@@ -222,6 +222,9 @@ func collectImports(md *ConfigMetadata, imports map[string]bool, rootPackage, co
222222
}
223223
refDesc := NewRef(md.ResolvedFrom)
224224
if !refDesc.isInternal() {
225+
if err := collectCustomDefaultImports(md, md.Default, imports, rootPackage, componentPackage); err != nil {
226+
return err
227+
}
225228
return nil
226229
}
227230
}
@@ -267,6 +270,42 @@ func collectImports(md *ConfigMetadata, imports map[string]bool, rootPackage, co
267270
return nil
268271
}
269272

273+
func collectCustomDefaultImports(md *ConfigMetadata, defaultValue any, imports map[string]bool, rootPackage, componentPackage string) error {
274+
if md == nil || md.GoStruct.IgnoreDefault {
275+
return nil
276+
}
277+
278+
switch typedValue := defaultValue.(type) {
279+
case map[string]any:
280+
if md.AdditionalProperties != nil {
281+
return nil
282+
}
283+
for key, value := range typedValue {
284+
prop := md.Properties[key]
285+
if prop == nil {
286+
continue
287+
}
288+
if err := collectImports(prop, imports, rootPackage, componentPackage); err != nil {
289+
return err
290+
}
291+
if err := collectCustomDefaultImports(prop, value, imports, rootPackage, componentPackage); err != nil {
292+
return err
293+
}
294+
}
295+
case []any:
296+
if md.Items == nil || md.Items.Type != "object" {
297+
return nil
298+
}
299+
for _, item := range typedValue {
300+
if err := collectCustomDefaultImports(md.Items, item, imports, rootPackage, componentPackage); err != nil {
301+
return err
302+
}
303+
}
304+
}
305+
306+
return nil
307+
}
308+
270309
// FormatTypeName resolves a reference string to a Go type expression using GoTypeRef.
271310
func FormatTypeName(ref, rootPackage, componentPackage string) (string, error) {
272311
tr, err := ResolveGoTypeRef(ref, rootPackage, componentPackage)
@@ -447,12 +486,12 @@ func generateValidatorName(propName string, desc *CustomValidatorConfig) string
447486
return "validate" + id
448487
}
449488

450-
func MapCustomDefaults(schema *ConfigMetadata, defaultValue DefaultValue, rootPackage, componentPackage string) []string {
451-
if !defaultValue.IsSet() {
489+
func MapCustomDefaults(schema *ConfigMetadata, defaultValue any, rootPackage, componentPackage string) []string {
490+
if schema.GoStruct.IgnoreDefault {
452491
return nil
453492
}
454493
exps := make([]string, 0)
455-
switch typedValue := defaultValue.Get().(type) {
494+
switch typedValue := defaultValue.(type) {
456495
case map[string]any:
457496
// is nested struct
458497
if schema.AdditionalProperties == nil {
@@ -462,7 +501,7 @@ func MapCustomDefaults(schema *ConfigMetadata, defaultValue DefaultValue, rootPa
462501
panic("schema does not contain required property: " + key)
463502
}
464503
varName, _ := helpers.FormatIdentifier(key, true)
465-
exp := fmt.Sprintf(".%s = %s", varName, FormatDefaultValue(propSchema, key, NewDefaultValue(value), rootPackage, componentPackage))
504+
exp := fmt.Sprintf(".%s = %s", varName, FormatDefaultValue(propSchema, key, value, rootPackage, componentPackage))
466505
exps = append(exps, exp)
467506
}
468507
} else if schema.AdditionalProperties.Type == "object" { // is a map of object
@@ -474,7 +513,7 @@ func MapCustomDefaults(schema *ConfigMetadata, defaultValue DefaultValue, rootPa
474513
panic("unsupported default value type for custom mapping")
475514
}
476515
for i, item := range typedValue {
477-
nestedExps := MapCustomDefaults(schema.Items, NewDefaultValue(item), rootPackage, componentPackage)
516+
nestedExps := MapCustomDefaults(schema.Items, item, rootPackage, componentPackage)
478517
for _, exp := range nestedExps {
479518
exps = append(exps, fmt.Sprintf("[%d]%s", i, exp))
480519
}
@@ -484,8 +523,8 @@ func MapCustomDefaults(schema *ConfigMetadata, defaultValue DefaultValue, rootPa
484523
return exps
485524
}
486525

487-
func FormatDefaultValue(md *ConfigMetadata, name string, defaultValue DefaultValue, rootPackage, componentPackage string) string {
488-
if defaultValue.IsSet() && defaultValue.Get() == nil {
526+
func FormatDefaultValue(md *ConfigMetadata, name string, defaultValue any, rootPackage, componentPackage string) string {
527+
if md.GoStruct.IgnoreDefault || (defaultValue == nil && !hasDefaultValue(md)) {
489528
if md.IsPointer {
490529
return "nil"
491530
}
@@ -510,7 +549,7 @@ func FormatDefaultValue(md *ConfigMetadata, name string, defaultValue DefaultVal
510549

511550
// FormatBaseValue returns the default value expression without IsPointer/IsOptional wrappers.
512551
// Use this when initializing a local variable that will be mutated before wrapping.
513-
func FormatBaseValue(md *ConfigMetadata, name string, defaultValue DefaultValue, rootPackage, componentPackage string) string {
552+
func FormatBaseValue(md *ConfigMetadata, name string, defaultValue any, rootPackage, componentPackage string) string {
514553
return formatSimpleValue(md, name, defaultValue, rootPackage, componentPackage)
515554
}
516555

@@ -531,7 +570,7 @@ func WrapDefaultValue(md *ConfigMetadata, varName string) string {
531570
}
532571

533572
func hasDefaultValue(md *ConfigMetadata) bool {
534-
if md.Default.IsSet() {
573+
if !md.GoStruct.IgnoreDefault && md.Default != nil {
535574
return true
536575
}
537576
for _, prop := range md.Properties {
@@ -552,7 +591,7 @@ func CamelVar(ref string) string {
552591
return name
553592
}
554593

555-
func formatSimpleValue(md *ConfigMetadata, name string, defaultValue DefaultValue, rootPackage, componentPackage string) string {
594+
func formatSimpleValue(md *ConfigMetadata, name string, defaultValue any, rootPackage, componentPackage string) string {
556595
// handle references
557596
isReference := md.ResolvedFrom != ""
558597
isSubStruct := md.Type == "object" && md.AdditionalProperties == nil
@@ -577,19 +616,19 @@ func formatSimpleValue(md *ConfigMetadata, name string, defaultValue DefaultValu
577616
return ""
578617
}
579618

580-
// do not process further if "default" attribute not defined
581-
if !defaultValue.IsSet() {
619+
// do not process further if "ignore_default" attribute set
620+
if md.GoStruct.IgnoreDefault {
582621
return ""
583622
}
584623

585624
switch md.Type {
586625
case "array":
587626
typeExpr, err := resolveGoType(md.Items, name+"_item", "", "")
588627
if err == nil {
589-
if defaultValues, ok := defaultValue.Get().([]any); ok {
628+
if defaultValues, ok := defaultValue.([]any); ok {
590629
exps := make([]string, 0, len(defaultValues))
591630
for _, defaultValue := range defaultValues {
592-
exps = append(exps, FormatDefaultValue(md.Items, name+"_item", NewDefaultValue(defaultValue), rootPackage, componentPackage))
631+
exps = append(exps, FormatDefaultValue(md.Items, name+"_item", defaultValue, rootPackage, componentPackage))
593632
}
594633
return fmt.Sprintf("[]%s{%s}", typeExpr, strings.Join(exps, ", "))
595634
}
@@ -599,13 +638,13 @@ func formatSimpleValue(md *ConfigMetadata, name string, defaultValue DefaultValu
599638
case "object":
600639
typeExpr, err := resolveGoType(md.AdditionalProperties, name, "", "")
601640
if err == nil {
602-
if defaultValues, ok := defaultValue.Get().(map[string]any); ok {
641+
if defaultValues, ok := defaultValue.(map[string]any); ok {
603642
exps := make([]string, 0, len(defaultValues))
604643
for _, keyName := range slices.Sorted(maps.Keys(defaultValues)) {
605644
value := defaultValues[keyName]
606645
exps = append(
607646
exps,
608-
fmt.Sprintf("%q: %v", keyName, FormatDefaultValue(md.AdditionalProperties, name, NewDefaultValue(value), rootPackage, componentPackage)))
647+
fmt.Sprintf("%q: %v", keyName, FormatDefaultValue(md.AdditionalProperties, name, value, rootPackage, componentPackage)))
609648
}
610649
return fmt.Sprintf("map[string]%s{%s}", typeExpr, strings.Join(exps, ", "))
611650
}
@@ -615,14 +654,14 @@ func formatSimpleValue(md *ConfigMetadata, name string, defaultValue DefaultValu
615654
case "string":
616655
switch md.GoType {
617656
case "time.Duration":
618-
if durationExpr, ok := renderDurationExpr(defaultValue.Get()); ok {
657+
if durationExpr, ok := renderDurationExpr(defaultValue); ok {
619658
return durationExpr
620659
}
621660
default:
622-
return fmt.Sprintf("%q", defaultValue.Get())
661+
return fmt.Sprintf("%q", defaultValue)
623662
}
624663
default:
625-
return fmt.Sprintf("%v", defaultValue.Get())
664+
return fmt.Sprintf("%v", defaultValue)
626665
}
627666

628667
panic("unreachable")

cmd/mdatagen/internal/cfggen/generation_test.go

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ func TestExtractImports_Optional(t *testing.T) {
500500
require.Contains(t, result, "go.opentelemetry.io/collector/config/configoptional")
501501
}
502502

503-
func TestExtractImports_ResolvedReferenceUsesResolvedTypeOnly(t *testing.T) {
503+
func TestExtractImports_ResolvedReferenceIncludesDefaultOverrideImports(t *testing.T) {
504504
md := &ConfigMetadata{
505505
Type: "object",
506506
ResolvedFrom: "go.opentelemetry.io/collector/scraper/scraperhelper.ControllerConfig",
@@ -515,7 +515,7 @@ func TestExtractImports_ResolvedReferenceUsesResolvedTypeOnly(t *testing.T) {
515515

516516
result, err := ExtractImports(md, "", "")
517517
require.NoError(t, err)
518-
require.Equal(t, []string{"go.opentelemetry.io/collector/scraper/scraperhelper"}, result)
518+
require.ElementsMatch(t, []string{"go.opentelemetry.io/collector/scraper/scraperhelper", "time"}, result)
519519
}
520520

521521
func TestExtractImports_InternalResolvedReferenceIncludesNestedImports(t *testing.T) {
@@ -1738,7 +1738,7 @@ func TestFormatDefaultValue_ScalarDefaults(t *testing.T) {
17381738
name string
17391739
schema *ConfigMetadata
17401740
propName string
1741-
defaultValue DefaultValue
1741+
defaultValue any
17421742
expected string
17431743
}{
17441744
{
@@ -1886,14 +1886,14 @@ func TestFormatBaseValue(t *testing.T) {
18861886
}
18871887

18881888
func TestFormatDefaultValue_UnsetDefault(t *testing.T) {
1889-
require.Empty(t, FormatDefaultValue(&ConfigMetadata{Type: "integer"}, "port", DefaultValue{}, "", ""))
1889+
require.Empty(t, FormatDefaultValue(&ConfigMetadata{Type: "integer"}, "port", nil, "", ""))
18901890
}
18911891

18921892
func TestFormatDefaultValue_Panics(t *testing.T) {
18931893
tests := []struct {
18941894
name string
18951895
metadata *ConfigMetadata
1896-
defaultValue DefaultValue
1896+
defaultValue any
18971897
}{
18981898
{
18991899
name: "invalid reference",
@@ -2039,6 +2039,11 @@ func TestHasDefaultValue(t *testing.T) {
20392039
{Type: "object", Default: defaultValue(map[string]any{"enabled": true})},
20402040
},
20412041
}))
2042+
require.False(t, hasDefaultValue(&ConfigMetadata{
2043+
Type: "string",
2044+
Default: defaultValue("value"),
2045+
GoStruct: GoStructConfig{IgnoreDefault: true},
2046+
}))
20422047
// External ref without any property defaults must not be treated as having defaults.
20432048
require.False(t, hasDefaultValue(&ConfigMetadata{
20442049
Type: "object",
@@ -2088,7 +2093,7 @@ func TestMapCustomDefaults_Panics(t *testing.T) {
20882093
tests := []struct {
20892094
name string
20902095
metadata *ConfigMetadata
2091-
defaultValue DefaultValue
2096+
defaultValue any
20922097
}{
20932098
{
20942099
name: "missing property",
@@ -2126,15 +2131,27 @@ func TestMapCustomDefaults_Panics(t *testing.T) {
21262131
}
21272132

21282133
func TestMapCustomDefaults_EmptyInput(t *testing.T) {
2129-
require.Empty(t, MapCustomDefaults(&ConfigMetadata{Type: "string"}, DefaultValue{}, "", ""))
2134+
require.Empty(t, MapCustomDefaults(&ConfigMetadata{Type: "string"}, nil, "", ""))
2135+
}
2136+
2137+
func TestMapCustomDefaults_IgnoreDefault(t *testing.T) {
2138+
md := &ConfigMetadata{
2139+
Type: "object",
2140+
GoStruct: GoStructConfig{IgnoreDefault: true},
2141+
Properties: map[string]*ConfigMetadata{
2142+
"host": {Type: "string"},
2143+
},
2144+
}
2145+
2146+
require.Empty(t, MapCustomDefaults(md, defaultValue(map[string]any{"host": "localhost"}), "", ""))
21302147
}
21312148

21322149
func TestNewCfgFns_DefaultHelpers(t *testing.T) {
21332150
fns := NewCfgFns("", "")
21342151

2135-
formatDefaultValue := fns["formatDefaultValue"].(func(*ConfigMetadata, string, DefaultValue) string)
2136-
formatBaseValue := fns["formatBaseValue"].(func(*ConfigMetadata, string, DefaultValue) string)
2137-
mapCustomDefaults := fns["mapCustomDefaults"].(func(*ConfigMetadata, DefaultValue) []string)
2152+
formatDefaultValue := fns["formatDefaultValue"].(func(*ConfigMetadata, string, any) string)
2153+
formatBaseValue := fns["formatBaseValue"].(func(*ConfigMetadata, string, any) string)
2154+
mapCustomDefaults := fns["mapCustomDefaults"].(func(*ConfigMetadata, any) []string)
21382155
hasDefaultValue := fns["hasDefaultValue"].(func(*ConfigMetadata) bool)
21392156

21402157
require.Equal(t, `"localhost"`, formatDefaultValue(&ConfigMetadata{Type: "string"}, "endpoint", defaultValue("localhost")))

cmd/mdatagen/internal/cfggen/model.go

Lines changed: 2 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,6 @@ import (
88
"errors"
99
"fmt"
1010

11-
"go.yaml.in/yaml/v3"
12-
1311
"go.opentelemetry.io/collector/confmap"
1412
)
1513

@@ -22,7 +20,7 @@ type ConfigMetadata struct {
2220
Comment string `mapstructure:"$comment,omitempty" json:"$comment,omitempty" yaml:"$comment,omitempty"`
2321
Type string `mapstructure:"type,omitempty" json:"type,omitempty" yaml:"type,omitempty"`
2422
Ref string `mapstructure:"$ref,omitempty" json:"-" yaml:"$ref,omitempty"`
25-
Default DefaultValue `mapstructure:"-" json:"default,omitzero" yaml:"default,omitempty"`
23+
Default any `mapstructure:"default,omitempty" json:"default,omitempty" yaml:"default,omitempty"`
2624
Examples []any `mapstructure:"examples,omitempty" json:"examples,omitempty" yaml:"examples,omitempty"`
2725
Deprecated bool `mapstructure:"deprecated,omitempty" json:"deprecated,omitempty" yaml:"deprecated,omitempty"`
2826
Enum []any `mapstructure:"enum,omitempty" json:"enum,omitempty" yaml:"enum,omitempty"`
@@ -61,54 +59,10 @@ type ConfigMetadata struct {
6159
EmbeddedName string `mapstructure:"-" json:"-" yaml:"-"`
6260
}
6361

64-
// DefaultValue distinguishes between an absent default attribute and an explicitly configured default value.
65-
type DefaultValue struct {
66-
value any
67-
set bool
68-
}
69-
70-
// NewDefaultValue returns a DefaultValue wrapping the provided value.
71-
func NewDefaultValue(value any) DefaultValue {
72-
return DefaultValue{
73-
value: value,
74-
set: true,
75-
}
76-
}
77-
78-
// IsZero implements the interface used by encoding/json's omitzero tag.
79-
func (dv DefaultValue) IsZero() bool {
80-
return !dv.set
81-
}
82-
83-
// IsSet reports whether the default attribute was present in metadata.
84-
func (dv *DefaultValue) IsSet() bool {
85-
return dv.set
86-
}
87-
88-
// Get returns the configured default value.
89-
func (dv *DefaultValue) Get() any {
90-
return dv.value
91-
}
92-
93-
func (dv *DefaultValue) UnmarshalYAML(node *yaml.Node) error {
94-
var value any
95-
if err := node.Decode(&value); err != nil {
96-
return err
97-
}
98-
*dv = NewDefaultValue(value)
99-
return nil
100-
}
101-
102-
func (dv *DefaultValue) MarshalJSON() ([]byte, error) {
103-
if !dv.set {
104-
return []byte("null"), nil
105-
}
106-
return json.Marshal(dv.value)
107-
}
108-
10962
type GoStructConfig struct {
11063
CustomValidator *CustomValidatorConfig `mapstructure:"custom_validator" json:"-" yaml:"custom_validator,omitempty"`
11164
Anonymous bool `mapstructure:"anonymous" json:"-" yaml:"anonymous,omitempty"`
65+
IgnoreDefault bool `mapstructure:"ignore_default" json:"-" yaml:"ignore_default,omitempty"`
11266
}
11367

11468
type CustomValidatorConfig struct {
@@ -131,24 +85,6 @@ func (g *GoStructConfig) Unmarshal(parser *confmap.Conf) error {
13185
return sub.Unmarshal(g.CustomValidator)
13286
}
13387

134-
func (md *ConfigMetadata) Unmarshal(parser *confmap.Conf) error {
135-
raw := parser.ToStringMap()
136-
defaultValue, hasDefault := raw["default"]
137-
delete(raw, "default")
138-
139-
type configMetadata ConfigMetadata
140-
var decoded configMetadata
141-
if err := confmap.NewFromStringMap(raw).Unmarshal(&decoded); err != nil {
142-
return err
143-
}
144-
145-
*md = ConfigMetadata(decoded)
146-
if hasDefault {
147-
md.Default = NewDefaultValue(defaultValue)
148-
}
149-
return nil
150-
}
151-
15288
func (md *ConfigMetadata) ToJSON() ([]byte, error) {
15389
return json.MarshalIndent(md, "", " ")
15490
}

0 commit comments

Comments
 (0)