Repository: prometheus/common
Pull request #688 (2024-09-04 13:14:06)

expfmt: Add WithEscapingScheme to help construct Formats
should be helpful for https://github.com/open-telemetry/opentelemetry-go/pull/5755


Patch:
diff --git a/expfmt/expfmt.go b/expfmt/expfmt.go
index 14034a6..d942af8 100644
--- a/expfmt/expfmt.go
+++ b/expfmt/expfmt.go
@@ -112,6 +112,29 @@ func NewOpenMetricsFormat(version string) (Format, error) {
 	return FmtUnknown, fmt.Errorf("unknown open metrics version string")
 }
 
+// WithEscapingScheme returns a copy of Format with the specified escaping
+// scheme appended to the end. If an escaping scheme already exists it is
+// removed.
+func (f Format) WithEscapingScheme(s model.EscapingScheme) Format {
+	var terms []string
+	for _, p := range strings.Split(string(f), ";") {
+		toks := strings.Split(p, "=")
+		if len(toks) != 2 {
+			trimmed := strings.TrimSpace(p)
+			if len(trimmed) > 0 {
+				terms = append(terms, trimmed)
+			}
+			continue
+		}
+		key := strings.TrimSpace(toks[0])
+		if key != model.EscapingKey {
+			terms = append(terms, strings.TrimSpace(p))
+		}
+	}
+	terms = append(terms, model.EscapingKey+"="+s.String())
+	return Format(strings.Join(terms, "; "))
+}
+
 // FormatType deduces an overall FormatType for the given format.
 func (f Format) FormatType() FormatType {
 	toks := strings.Split(string(f), ";")


Test patch:
diff --git a/expfmt/expfmt_test.go b/expfmt/expfmt_test.go
index 3a16f3b..bc946ec 100644
--- a/expfmt/expfmt_test.go
+++ b/expfmt/expfmt_test.go
@@ -17,6 +17,8 @@ import (
 	"testing"
 
 	"github.com/prometheus/common/model"
+
+	"github.com/stretchr/testify/require"
 )
 
 // Test Format to Escapting Scheme conversion
@@ -92,9 +94,7 @@ func TestToFormatType(t *testing.T) {
 		},
 	}
 	for _, test := range tests {
-		if test.format.FormatType() != test.expected {
-			t.Errorf("expected %v got %v", test.expected, test.format.FormatType())
-		}
+		require.Equal(t, test.expected, test.format.FormatType())
 	}
 }
 
@@ -122,8 +122,43 @@ func TestToEscapingScheme(t *testing.T) {
 		},
 	}
 	for _, test := range tests {
-		if test.format.ToEscapingScheme() != test.expected {
-			t.Errorf("expected %v got %v", test.expected, test.format.ToEscapingScheme())
-		}
+		require.Equal(t, test.expected, test.format.ToEscapingScheme())
+	}
+}
+
+func TestWithEscapingScheme(t *testing.T) {
+	tests := []struct {
+		name     string
+		format   Format
+		scheme   model.EscapingScheme
+		expected string
+	}{
+		{
+			name:     "no existing term, append one",
+			format:   FmtProtoCompact,
+			scheme:   model.DotsEscaping,
+			expected: "application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=compact-text; escaping=dots",
+		},
+		{
+			name:     "existing term at end, replace",
+			format:   "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=underscores",
+			scheme:   model.ValueEncodingEscaping,
+			expected: "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=values",
+		},
+		{
+			name:     "existing term in middle, replace",
+			format:   "application/openmetrics-text; escaping=dots; version=1.0.0; charset=utf-8; ",
+			scheme:   model.NoEscaping,
+			expected: "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=allow-utf-8",
+		},
+		{
+			name:     "multiple existing terms removed",
+			format:   "application/openmetrics-text; escaping=dots; version=1.0.0; charset=utf-8; escaping=allow-utf-8",
+			scheme:   model.ValueEncodingEscaping,
+			expected: "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=values",
+		},
+	}
+	for _, test := range tests {
+		require.Equal(t, test.expected, string(test.format.WithEscapingScheme(test.scheme)))
 	}
 }