Skip to content

ValidationScheme implements pflag.Value and json.Marshaler/Unmarshaler interfaces #807

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 1 commit into
base: main
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
51 changes: 43 additions & 8 deletions model/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package model

import (
"encoding/json"
"errors"
"fmt"
"regexp"
Expand Down Expand Up @@ -77,10 +78,13 @@ const (
UTF8Validation
)

var (
_ yaml.Marshaler = UnsetValidation
_ fmt.Stringer = UnsetValidation
)
var _ interface {
yaml.Marshaler
yaml.Unmarshaler
json.Marshaler
json.Unmarshaler
fmt.Stringer
} = new(ValidationScheme)

// String returns the string representation of s.
func (s ValidationScheme) String() string {
Expand Down Expand Up @@ -114,15 +118,41 @@ func (s *ValidationScheme) UnmarshalYAML(unmarshal func(any) error) error {
if err := unmarshal(&scheme); err != nil {
return err
}
switch scheme {
return s.Set(scheme)
}

// MarshalJSON implements the json.Marshaler interface.
func (s ValidationScheme) MarshalJSON() ([]byte, error) {
switch s {
case UnsetValidation:
return json.Marshal("")
case UTF8Validation, LegacyValidation:
return json.Marshal(s.String())
default:
return nil, fmt.Errorf("unhandled ValidationScheme: %d", s)
}
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (s *ValidationScheme) UnmarshalJSON(bytes []byte) error {
var repr string
if err := json.Unmarshal(bytes, &repr); err != nil {
return err
}
return s.Set(repr)
}

// Set implements the pflag.Value interface.
func (s *ValidationScheme) Set(text string) error {
switch text {
case "":
// Don't change the value.
Copy link
Preview

Copilot AI Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment 'Don't change the value' is misleading for an empty string input. When Set() is called with an empty string, it should set the value to UnsetValidation, not leave it unchanged. Either update the logic to set *s = UnsetValidation or clarify the comment to explain why empty strings preserve the current value.

Suggested change
// Don't change the value.
// Set to UnsetValidation for empty string input.
*s = UnsetValidation

Copilot uses AI. Check for mistakes.

case "legacy":
case LegacyValidation.String():
*s = LegacyValidation
case "utf8":
case UTF8Validation.String():
*s = UTF8Validation
default:
return fmt.Errorf("unrecognized ValidationScheme: %q", scheme)
return fmt.Errorf("unrecognized ValidationScheme: %q", text)
}
return nil
}
Expand Down Expand Up @@ -174,6 +204,11 @@ func (s ValidationScheme) IsValidLabelName(labelName string) bool {
}
}

// Type implements the pflag.Value interface.
func (s ValidationScheme) Type() string {
return "validationScheme"
}

type EscapingScheme int

const (
Expand Down
89 changes: 89 additions & 0 deletions model/metric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
package model

import (
"encoding/json"
"errors"
"fmt"
"strings"
Expand Down Expand Up @@ -203,6 +204,94 @@ func TestValidationScheme_UnmarshalYAML(t *testing.T) {
}
}

func TestValidationScheme_UnmarshalJSON(t *testing.T) {
testCases := []struct {
name string
input string
want ValidationScheme
wantErr bool
}{
{
name: "invalid",
input: `invalid`,
wantErr: true,
},
{
name: "empty",
input: `""`,
want: UnsetValidation,
},
{
name: "legacy validation",
input: `"legacy"`,
want: LegacyValidation,
},
{
name: "utf8 validation",
input: `"utf8"`,
want: UTF8Validation,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var got ValidationScheme
err := json.Unmarshal([]byte(tc.input), &got)
if tc.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tc.want, ValidationScheme(got))

output, err := json.Marshal(got)
require.NoError(t, err)
require.Equal(t, tc.input, string(output))
})
}
}

func TestValidationScheme_Set(t *testing.T) {
testCases := []struct {
name string
input string
want ValidationScheme
wantErr bool
}{
{
name: "invalid",
input: `invalid`,
wantErr: true,
},
{
name: "empty",
input: ``,
want: UnsetValidation,
},
{
name: "legacy validation",
input: `legacy`,
want: LegacyValidation,
},
{
name: "utf8 validation",
input: `utf8`,
want: UTF8Validation,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
var got ValidationScheme
err := got.Set(tc.input)
if tc.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Equal(t, tc.want, ValidationScheme(got))
})
}
}

func TestValidationScheme_IsMetricNameValid(t *testing.T) {
scenarios := []struct {
mn string
Expand Down
Loading