diff --git a/modules/mesh-build-go/buildstep/seatgen/gogen.go b/modules/mesh-build-go/buildstep/seatgen/gogen.go index 7f4f0d23..f7715b64 100644 --- a/modules/mesh-build-go/buildstep/seatgen/gogen.go +++ b/modules/mesh-build-go/buildstep/seatgen/gogen.go @@ -496,6 +496,7 @@ func goTypes(b *bytes.Buffer, tp *typer, also map[string]*typ) error { fmt.Fprintf(b, "// %s %s\n", t.name, oneLine(desc)) if t.t.kind != "struct" { fmt.Fprintf(b, "type %s = %s\n\n", t.name, goType(t.t, kinds, true)) + goEnum(b, t.name, t.name, t.t) continue } fmt.Fprintf(b, "type %s struct {\n", t.name) @@ -510,10 +511,26 @@ func goTypes(b *bytes.Buffer, tp *typer, also map[string]*typ) error { fmt.Fprintf(b, "\t%s %s `json:%q`\n", ident(f.json), goType(f.t, kinds, f.required), tag) } b.WriteString("}\n\n") + for _, f := range t.t.fields { + goEnum(b, t.name+"'s "+f.json, t.name+ident(f.json), f.t) + } } return nil } +// goEnum writes a string's enum as one constant per value, named prefix and the value (RemoveInputReasonRetention), so a +// caller names a value the definition gives and never writes it by hand. +func goEnum(b *bytes.Buffer, what, prefix string, t *typ) { + if t.kind != "string" || len(t.enum) == 0 { + return + } + fmt.Fprintf(b, "// The values of %s.\nconst (\n", what) + for _, v := range t.enum { + fmt.Fprintf(b, "\t%s%s = %q\n", prefix, ident(v), v) + } + b.WriteString(")\n\n") +} + func goType(t *typ, kinds map[string]*typ, required bool) string { switch t.kind { case "string": diff --git a/modules/mesh-build-go/buildstep/seatgen/layout_test.go b/modules/mesh-build-go/buildstep/seatgen/layout_test.go index b7c5b642..c7848ab6 100644 --- a/modules/mesh-build-go/buildstep/seatgen/layout_test.go +++ b/modules/mesh-build-go/buildstep/seatgen/layout_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "go/parser" "go/token" + re "regexp" "strings" "testing" @@ -71,6 +72,10 @@ func TestEachSeatDefinedWholeIsAGoModuleOfItsOwn(t *testing.T) { if !strings.Contains(src, "type Ref struct") || !strings.Contains(src, "type PublishedEvent") { t.Errorf("%s does not hold the part's types whole", seat) } + // A string's enum is a constant per value, so a caller names the definition's value (A3t: the removal reason). + if !re.MustCompile(`RefReasonRetention\s+= "retention"`).MatchString(src) || !re.MustCompile(`RefReasonTagGone\s+= "tag-gone"`).MatchString(src) { + t.Errorf("%s does not name the part's enum values:\n%s", seat, src) + } } if !strings.Contains(string(files["go/seats/seats.go"]), "OneStore") { t.Error("the seat set does not name the seat at the seat list's grain") diff --git a/modules/mesh-build-go/buildstep/seatgen/testdata/contracts/parts/registry.json b/modules/mesh-build-go/buildstep/seatgen/testdata/contracts/parts/registry.json index e06ac049..01a76b78 100644 --- a/modules/mesh-build-go/buildstep/seatgen/testdata/contracts/parts/registry.json +++ b/modules/mesh-build-go/buildstep/seatgen/testdata/contracts/parts/registry.json @@ -1,3 +1,3 @@ {"format":1,"part":"registry","verbs":[{"name":"publish","input":{"$ref":"#/defs/Ref"},"answer":{"type":"object"}}], "events":[{"name":"published","body":{"$ref":"#/defs/Ref"}}], - "defs":{"Ref":{"type":"object","properties":{"digest":{"type":"string"}},"required":["digest"]}}} + "defs":{"Ref":{"type":"object","properties":{"digest":{"type":"string"},"reason":{"type":"string","enum":["tag-gone","retention"]}},"required":["digest"]}}}