-
Notifications
You must be signed in to change notification settings - Fork 192
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add "clearomitted" directive Adds `//msgp:clearomitted` directive. This will cause all `omitempty` and `omitzero` fields to be set to the zero value on Unmarshal and Decode, and the field wasn't present in the marshalled data. This can be useful when de-serializing into reused objects, that can have values set for these, and we want to avoid clearing all fields, but also don't want existing fields to leak through. Fields are tracked through a bit mask, and zeroed after the unmarshal loop, if no value has been written. This does not affect marshaling.
- Loading branch information
Showing
8 changed files
with
313 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package _generated | ||
|
||
import ( | ||
"encoding/json" | ||
"time" | ||
) | ||
|
||
//go:generate msgp | ||
|
||
//msgp:clearomitted | ||
|
||
// check some specific cases for omitzero | ||
|
||
type ClearOmitted0 struct { | ||
AStruct ClearOmittedA `msg:"astruct,omitempty"` // leave this one omitempty | ||
BStruct ClearOmittedA `msg:"bstruct,omitzero"` // and compare to this | ||
AStructPtr *ClearOmittedA `msg:"astructptr,omitempty"` // a pointer case omitempty | ||
BStructPtr *ClearOmittedA `msg:"bstructptr,omitzero"` // a pointer case omitzero | ||
AExt OmitZeroExt `msg:"aext,omitzero"` // external type case | ||
|
||
// more | ||
APtrNamedStr *NamedStringCO `msg:"aptrnamedstr,omitzero"` | ||
ANamedStruct NamedStructCO `msg:"anamedstruct,omitzero"` | ||
APtrNamedStruct *NamedStructCO `msg:"aptrnamedstruct,omitzero"` | ||
EmbeddableStructCO `msg:",flatten,omitzero"` // embed flat | ||
EmbeddableStructCO2 `msg:"embeddablestruct2,omitzero"` // embed non-flat | ||
ATime time.Time `msg:"atime,omitzero"` | ||
ASlice []int `msg:"aslice,omitempty"` | ||
AMap map[string]int `msg:"amap,omitempty"` | ||
ABin []byte `msg:"abin,omitempty"` | ||
AInt int `msg:"aint,omitempty"` | ||
AString string `msg:"atring,omitempty"` | ||
Adur time.Duration `msg:"adur,omitempty"` | ||
AJSON json.Number `msg:"ajson,omitempty"` | ||
|
||
ClearOmittedTuple ClearOmittedTuple `msg:"ozt"` // the inside of a tuple should ignore both omitempty and omitzero | ||
} | ||
|
||
type ClearOmittedA struct { | ||
A string `msg:"a,omitempty"` | ||
B NamedStringCO `msg:"b,omitzero"` | ||
C NamedStringCO `msg:"c,omitzero"` | ||
} | ||
|
||
func (o *ClearOmittedA) IsZero() bool { | ||
if o == nil { | ||
return true | ||
} | ||
return *o == (ClearOmittedA{}) | ||
} | ||
|
||
type NamedStructCO struct { | ||
A string `msg:"a,omitempty"` | ||
B string `msg:"b,omitempty"` | ||
} | ||
|
||
func (ns *NamedStructCO) IsZero() bool { | ||
if ns == nil { | ||
return true | ||
} | ||
return *ns == (NamedStructCO{}) | ||
} | ||
|
||
type NamedStringCO string | ||
|
||
func (ns *NamedStringCO) IsZero() bool { | ||
if ns == nil { | ||
return true | ||
} | ||
return *ns == "" | ||
} | ||
|
||
type EmbeddableStructCO struct { | ||
SomeEmbed string `msg:"someembed2,omitempty"` | ||
} | ||
|
||
func (es EmbeddableStructCO) IsZero() bool { return es == (EmbeddableStructCO{}) } | ||
|
||
type EmbeddableStructCO2 struct { | ||
SomeEmbed2 string `msg:"someembed2,omitempty"` | ||
} | ||
|
||
func (es EmbeddableStructCO2) IsZero() bool { return es == (EmbeddableStructCO2{}) } | ||
|
||
//msgp:tuple ClearOmittedTuple | ||
|
||
// ClearOmittedTuple is flagged for tuple output, it should ignore all omitempty and omitzero functionality | ||
// since it's fundamentally incompatible. | ||
type ClearOmittedTuple struct { | ||
FieldA string `msg:"fielda,omitempty"` | ||
FieldB NamedStringCO `msg:"fieldb,omitzero"` | ||
FieldC NamedStringCO `msg:"fieldc,omitzero"` | ||
} | ||
|
||
type ClearOmitted1 struct { | ||
T1 ClearOmittedTuple `msg:"t1"` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package _generated | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"reflect" | ||
"testing" | ||
"time" | ||
|
||
"github.com/tinylib/msgp/msgp" | ||
) | ||
|
||
func TestClearOmitted(t *testing.T) { | ||
cleared := ClearOmitted0{} | ||
encoded, err := cleared.MarshalMsg(nil) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
vPtr := NamedStringCO("value") | ||
filled := ClearOmitted0{ | ||
AStruct: ClearOmittedA{A: "something"}, | ||
BStruct: ClearOmittedA{A: "somthing"}, | ||
AStructPtr: &ClearOmittedA{A: "something"}, | ||
AExt: OmitZeroExt{25}, | ||
APtrNamedStr: &vPtr, | ||
ANamedStruct: NamedStructCO{A: "value"}, | ||
APtrNamedStruct: &NamedStructCO{A: "sdf"}, | ||
EmbeddableStructCO: EmbeddableStructCO{"value"}, | ||
EmbeddableStructCO2: EmbeddableStructCO2{"value"}, | ||
ATime: time.Now(), | ||
ASlice: []int{1, 2, 3}, | ||
AMap: map[string]int{"1": 1}, | ||
ABin: []byte{1, 2, 3}, | ||
ClearOmittedTuple: ClearOmittedTuple{FieldA: "value"}, | ||
AInt: 42, | ||
AString: "value", | ||
Adur: time.Second, | ||
AJSON: json.Number(`43.0000000000002`), | ||
} | ||
dst := filled | ||
_, err = dst.UnmarshalMsg(encoded) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if !reflect.DeepEqual(dst, cleared) { | ||
t.Errorf("\n got=%#v\nwant=%#v", dst, cleared) | ||
} | ||
// Reset | ||
dst = filled | ||
err = dst.DecodeMsg(msgp.NewReader(bytes.NewReader(encoded))) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if !reflect.DeepEqual(dst, cleared) { | ||
t.Errorf("\n got=%#v\nwant=%#v", dst, cleared) | ||
} | ||
|
||
// Check that fields aren't accidentally zeroing fields. | ||
wantJson, err := json.Marshal(filled) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
encoded, err = filled.MarshalMsg(nil) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
dst = ClearOmitted0{} | ||
_, err = dst.UnmarshalMsg(encoded) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
got, err := json.Marshal(dst) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if !bytes.Equal(got, wantJson) { | ||
t.Errorf("\n got=%#v\nwant=%#v", string(got), string(wantJson)) | ||
} | ||
// Reset | ||
dst = ClearOmitted0{} | ||
err = dst.DecodeMsg(msgp.NewReader(bytes.NewReader(encoded))) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
got, err = json.Marshal(dst) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
if !bytes.Equal(got, wantJson) { | ||
t.Errorf("\n got=%#v\nwant=%#v", string(got), string(wantJson)) | ||
} | ||
t.Log("OK - got", string(got)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.