internal/unify: fix round-tripping strings with regexp metacharacters Change-Id: I92956b13c7532b9a96386947ee19aa61142337c8 Reviewed-on: https://go-review.googlesource.com/c/arch/+/689478 Auto-Submit: Austin Clements <austin@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: David Chase <drchase@google.com>
diff --git a/internal/unify/domain.go b/internal/unify/domain.go index c59bd62..00c8090 100644 --- a/internal/unify/domain.go +++ b/internal/unify/domain.go
@@ -270,8 +270,8 @@ continue } - if _, complete := re.LiteralPrefix(); complete { - v = String{kind: stringExact, exact: expr} + if exact, complete := re.LiteralPrefix(); complete { + v = String{kind: stringExact, exact: exact} } else { v.kind = stringRegex v.re = append(v.re, re)
diff --git a/internal/unify/yaml.go b/internal/unify/yaml.go index 4731140..6782b31 100644 --- a/internal/unify/yaml.go +++ b/internal/unify/yaml.go
@@ -430,7 +430,14 @@ n.Tag = "tag:yaml.org,2002:int" return &n } - n.SetString(regexp.QuoteMeta(d.exact)) + // If this doesn't require escaping, leave it as a str node to avoid + // the annoying YAML tags. Otherwise, mark it as an exact string. + // Alternatively, we could always emit a str node with regexp + // quoting. + n.SetString(d.exact) + if d.exact != regexp.QuoteMeta(d.exact) { + n.Tag = "!string" + } return &n case stringRegex: o := make([]string, 0, 1)
diff --git a/internal/unify/yaml_test.go b/internal/unify/yaml_test.go index af73001..05a26be 100644 --- a/internal/unify/yaml_test.go +++ b/internal/unify/yaml_test.go
@@ -8,6 +8,9 @@ "bytes" "fmt" "iter" + "log" + "strings" + "testing" "gopkg.in/yaml.v3" ) @@ -20,6 +23,19 @@ return c } +func oneValue(t *testing.T, c Closure) *Value { + t.Helper() + var v *Value + var i int + for v = range c.All() { + i++ + } + if i != 1 { + t.Fatalf("expected 1 value, got %d", i) + } + return v +} + func printYaml(val any) { b, err := yaml.Marshal(val) if err != nil { @@ -89,3 +105,31 @@ } } } + +func TestRoundTripString(t *testing.T) { + // Check that we can round-trip a string with regexp meta-characters in it. + const y = `!string test*` + t.Logf("input:\n%s", y) + + v1 := oneValue(t, mustParse(y)) + var buf1 strings.Builder + enc := yaml.NewEncoder(&buf1) + if err := enc.Encode(v1); err != nil { + log.Fatal(err) + } + enc.Close() + t.Logf("after parse 1:\n%s", buf1.String()) + + v2 := oneValue(t, mustParse(buf1.String())) + var buf2 strings.Builder + enc = yaml.NewEncoder(&buf2) + if err := enc.Encode(v2); err != nil { + log.Fatal(err) + } + enc.Close() + t.Logf("after parse 2:\n%s", buf2.String()) + + if buf1.String() != buf2.String() { + t.Fatal("parse 1 and parse 2 differ") + } +}