Skip to content

Commit

Permalink
do not skip fields in composite if keepzero is set
Browse files Browse the repository at this point in the history
  • Loading branch information
alovak committed Oct 25, 2024
1 parent 12a215c commit 938552f
Show file tree
Hide file tree
Showing 3 changed files with 113 additions and 8 deletions.
20 changes: 13 additions & 7 deletions field/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,17 +234,23 @@ func (f *Composite) Marshal(v interface{}) error {
defer f.mu.Unlock()

rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr || rv.IsNil() {
return errors.New("data is not a pointer or nil")
if rv.Kind() != reflect.Ptr {
return errors.New("data is not a pointer")
}

// get the struct from the pointer
dataStruct := rv.Elem()
elemType := rv.Type().Elem()
if elemType.Kind() != reflect.Struct {
return errors.New("data must be a pointer to struct")
}

if dataStruct.Kind() != reflect.Struct {
return errors.New("data is not a struct")
// If nil, create a new instance of the struct
if rv.IsNil() {
rv = reflect.New(elemType)
}

// get the struct from the pointer
dataStruct := rv.Elem()

// iterate over struct fields
for i := 0; i < dataStruct.NumField(); i++ {
indexTag := NewIndexTag(dataStruct.Type().Field(i))
Expand All @@ -258,7 +264,7 @@ func (f *Composite) Marshal(v interface{}) error {
}

dataField := dataStruct.Field(i)
if dataField.IsZero() {
if dataField.IsZero() && !indexTag.KeepZero {
continue
}

Expand Down
15 changes: 14 additions & 1 deletion field/composite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,23 @@ type SubConstructedTLVTestData struct {
}

func TestCompositeField_Marshal(t *testing.T) {
t.Run("Marshal returns error on nil value", func(t *testing.T) {
composite := NewComposite(compositeTestSpec)
err := composite.Marshal(nil)
require.EqualError(t, err, "data is not a pointer")
})

t.Run("Marshal doesn't return an error when nil pointer is of struct type", func(t *testing.T) {
composite := NewComposite(compositeTestSpec)
var data *CompositeTestData
err := composite.Marshal(data)
require.NoError(t, err)
})

t.Run("Marshal returns an error on provision of primitive type data", func(t *testing.T) {
composite := NewComposite(compositeTestSpec)
err := composite.Marshal("primitive str")
require.EqualError(t, err, "data is not a pointer or nil")
require.EqualError(t, err, "data is not a pointer")
})

t.Run("Marshal skips fields without index tag", func(t *testing.T) {
Expand Down
86 changes: 86 additions & 0 deletions message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,73 @@ func TestMessage(t *testing.T) {
wantMsg := []byte("01007000000000000000164242424242424242123456000000000100")
require.Equal(t, wantMsg, rawMsg)
})

t.Run("Clone and reset fields", func(t *testing.T) {
type TestISOF3Data struct {
F1 *field.String
F2 *field.String
F3 *field.String
}

type ISO87Data struct {
F0 *field.String
F2 *field.String
F3 *TestISOF3Data
F4 *field.String
}

message := NewMessage(spec)
err := message.Marshal(&ISO87Data{
F0: field.NewStringValue("0100"),
F2: field.NewStringValue("4242424242424242"),
F3: &TestISOF3Data{
F1: field.NewStringValue("12"),
F2: field.NewStringValue("34"),
F3: field.NewStringValue("56"),
},
F4: field.NewStringValue("100"),
})
require.NoError(t, err)

// clone the message and reset some fields
clone, err := message.Clone()
require.NoError(t, err)

// reset the fields
// first, check that the fields are set
data := &ISO87Data{}
require.NoError(t, clone.Unmarshal(data))

require.Equal(t, "0100", data.F0.Value())
require.Equal(t, "4242424242424242", data.F2.Value())
require.Equal(t, "12", data.F3.F1.Value())
require.Equal(t, "34", data.F3.F2.Value())
require.Equal(t, "56", data.F3.F3.Value())
require.Equal(t, "100", data.F4.Value())

// reset the fields
err = clone.Marshal(&struct {
F2 *string `iso8583:"2,keepzero"`
F3 *struct {
F2 *string `iso8583:"2,keepzero"`
} `iso8583:"3,keepzero"`
}{})
require.NoError(t, err)

// check that the fields are reset
data = &ISO87Data{}
require.NoError(t, clone.Unmarshal(data))

require.Equal(t, "", data.F2.Value())
require.Equal(t, "", data.F3.F2.Value())

// check the reset fields in the message
require.Equal(t, "0100", data.F0.Value())
require.Equal(t, "12", data.F3.F1.Value())
require.Equal(t, "56", data.F3.F3.Value())
require.Equal(t, "100", data.F4.Value())
})

}

func TestPackUnpack(t *testing.T) {
Expand Down Expand Up @@ -1680,6 +1747,25 @@ func TestMessageClone(t *testing.T) {
require.Equal(t, message2Data.F55.F9A.Value(), message3Data.F55.F9A.Value())
require.Equal(t, message2Data.F55.F9F02.Value(), message3Data.F55.F9F02.Value())
require.Equal(t, message2Data.F120.Value(), message3Data.F120.Value())

// here is the way how to reset fields
response, err := message3.Clone()
// handle error

err = response.Marshal(&struct {
PAN *string `iso8583:"2,keepzero"`
F55 *struct {
F9A *string `iso8583:",keepzero"`
} `iso8583:",keepzero"`
}{})

responseData := &TestISOData{}
require.NoError(t, response.Unmarshal(responseData))

// check if the PAN is reset
require.Equal(t, "", responseData.F2.Value())
// check if the F55.F9A is reset
require.Equal(t, "210720", responseData.F55.F9A.Value())
}

func TestMessageMarshaling(t *testing.T) {
Expand Down

0 comments on commit 938552f

Please sign in to comment.