Skip to content

Commit

Permalink
codegen: assembler for struct with map representation now validates a…
Browse files Browse the repository at this point in the history
…ll non-optional fields are present.

This continues what #111 did
and adds the same logic to the map representation.  The actual state
tracking works the same way (and was mostly already there).

Rearranged the tests slightly.

Made error messages include both field name and serial key when they
differ due to a rename directive.  (It's possible this error would get
nicer if it used a list of StructField instead of just strings, but it
would also get more complicated.  Maybe revisit later.)
  • Loading branch information
warpfork committed Dec 4, 2020
1 parent 2333d16 commit 076b78a
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 24 deletions.
16 changes: 15 additions & 1 deletion schema/gen/go/genStructReprMap.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,7 +543,21 @@ func (g structReprMapReprBuilderGenerator) emitMapAssemblerMethods(w io.Writer)
case maState_finished:
panic("invalid state: Finish cannot be called on an assembler that's already finished")
}
//FIXME check if all required fields are set
if ma.s & fieldBits__{{ $type | TypeSymbol }}_sufficient != fieldBits__{{ $type | TypeSymbol }}_sufficient {
err := ipld.ErrMissingRequiredField{Missing: make([]string, 0)}
{{- range $i, $field := .Type.Fields }}
{{- if not $field.IsMaybe}}
if ma.s & fieldBit__{{ $type | TypeSymbol }}_{{ $field | FieldSymbolUpper }} == 0 {
{{- if $field | $type.RepresentationStrategy.FieldHasRename }}
err.Missing = append(err.Missing, "{{ $field.Name }} (serial:\"{{ $field | $type.RepresentationStrategy.GetFieldKey }}\")")
{{- else}}
err.Missing = append(err.Missing, "{{ $field.Name }}")
{{- end}}
}
{{- end}}
{{- end}}
return err
}
ma.state = maState_finished
*ma.m = schema.Maybe_Value
return nil
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,68 @@ import (
"github.com/ipld/go-ipld-prime/schema"
)

func TestRequiredFields(t *testing.T) {
t.Parallel()

ts := schema.TypeSystem{}
ts.Init()
adjCfg := &AdjunctCfg{}
ts.Accumulate(schema.SpawnString("String"))
ts.Accumulate(schema.SpawnStruct("StructOne",
[]schema.StructField{
schema.SpawnStructField("a", "String", false, false),
schema.SpawnStructField("b", "String", false, false),
},
schema.SpawnStructRepresentationMap(map[string]string{
// no renames. we expect a simpler error message in this case.
}),
))
ts.Accumulate(schema.SpawnStruct("StructTwo",
[]schema.StructField{
schema.SpawnStructField("a", "String", false, false),
schema.SpawnStructField("b", "String", false, false),
},
schema.SpawnStructRepresentationMap(map[string]string{
"b": "z",
}),
))

prefix := "struct-required-fields"
pkgName := "main"
genAndCompileAndTest(t, prefix, pkgName, ts, adjCfg, func(t *testing.T, getPrototypeByName func(string) ipld.NodePrototype) {
t.Run("building-type-without-required-fields-errors", func(t *testing.T) {
np := getPrototypeByName("StructOne")

nb := np.NewBuilder()
ma, _ := nb.BeginMap(0)
err := ma.Finish()

Wish(t, err, ShouldBeSameTypeAs, ipld.ErrMissingRequiredField{})
Wish(t, err.Error(), ShouldEqual, `missing required fields: a,b`)
})
t.Run("building-representation-without-required-fields-errors", func(t *testing.T) {
nrp := getPrototypeByName("StructOne.Repr")

nb := nrp.NewBuilder()
ma, _ := nb.BeginMap(0)
err := ma.Finish()

Wish(t, err, ShouldBeSameTypeAs, ipld.ErrMissingRequiredField{})
Wish(t, err.Error(), ShouldEqual, `missing required fields: a,b`)
})
t.Run("building-representation-with-renames-without-required-fields-errors", func(t *testing.T) {
nrp := getPrototypeByName("StructTwo.Repr")

nb := nrp.NewBuilder()
ma, _ := nb.BeginMap(0)
err := ma.Finish()

Wish(t, err, ShouldBeSameTypeAs, ipld.ErrMissingRequiredField{})
Wish(t, err.Error(), ShouldEqual, `missing required fields: a,b (serial:"z")`)
})
})
}

func TestStructNesting(t *testing.T) {
t.Parallel()

Expand Down
23 changes: 0 additions & 23 deletions schema/gen/go/testStructsContainingMaybe_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,27 +171,4 @@ func TestStructsContainingMaybe(t *testing.T) {
}
})
})

genAndCompileAndTest(t, "stroct3", "main", ts, adjCfg, func(t *testing.T, getPrototypeByName func(string) ipld.NodePrototype) {
t.Run("insufficient", func(t *testing.T) {
nrp := getPrototypeByName("Stroct")
t.Run("typed-create", func(t *testing.T) {
b := nrp.NewBuilder()
mb, err := b.BeginMap(0)
if err != nil {
t.Fatal(err)
}
v, err := mb.AssembleEntry("f1")
if err != nil {
t.Fatal(err)
}
v.AssignString("v1")

err = mb.Finish()
if _, ok := err.(ipld.ErrMissingRequiredField); !ok {
t.Fatalf("Expected error for missing field, got %v", err)
}
})
})
})
}
5 changes: 5 additions & 0 deletions schema/typeMethods.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ func (r StructRepresentation_Map) GetFieldKey(field StructField) string {
return field.name
}

func (r StructRepresentation_Map) FieldHasRename(field StructField) bool {
_, ok := r.renames[field.name]
return ok
}

func (r StructRepresentation_Stringjoin) GetDelim() string {
return r.sep
}
Expand Down

0 comments on commit 076b78a

Please sign in to comment.