Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updating unimarshaller #289

Merged
merged 14 commits into from
Oct 5, 2023
71 changes: 49 additions & 22 deletions field/binary.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package field

import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"reflect"

"github.com/moov-io/iso8583/encoding"
"github.com/moov-io/iso8583/utils"
Expand All @@ -16,7 +17,6 @@ var _ json.Unmarshaler = (*Binary)(nil)
type Binary struct {
value []byte
spec *Spec
data *Binary
}

func NewBinary(spec *Spec) *Binary {
Expand All @@ -41,9 +41,6 @@ func (f *Binary) SetSpec(spec *Spec) {

func (f *Binary) SetBytes(b []byte) error {
f.value = b
if f.data != nil {
*(f.data) = *f
}
return nil
}

Expand Down Expand Up @@ -120,34 +117,64 @@ func (f *Binary) SetData(data interface{}) error {
}

func (f *Binary) Unmarshal(v interface{}) error {
if v == nil {
return nil
}

bin, ok := v.(*Binary)
if !ok {
return errors.New("data does not match required *Binary type")
switch val := v.(type) {
case reflect.Value:
if !val.CanSet() {
return fmt.Errorf("cannot set reflect.Value of type %s", val.Kind())
}

switch val.Kind() { //nolint:exhaustive
case reflect.String:
val.SetString(hex.EncodeToString(f.value))
case reflect.Slice:
val.SetBytes(f.value)
default:
return fmt.Errorf("unsupported reflect.Value type: %s", val.Kind())
}
case *string:
*val = hex.EncodeToString(f.value)
case *[]byte:
*val = f.value
case *Binary:
val.value = f.value
default:
return fmt.Errorf("unsupported type: expected *Binary, *string, *[]byte, or reflect.Value, got %T", v)
}

bin.value = f.value

return nil
}

func (f *Binary) Marshal(v interface{}) error {
if v == nil {
if v == nil || reflect.ValueOf(v).IsZero() {
f.value = nil
return nil
}

bin, ok := v.(*Binary)
if !ok {
return errors.New("data does not match required *Binary type")
}
switch v := v.(type) {
case *Binary:
f.value = v.value
case string:
buf, err := hex.DecodeString(v)
if err != nil {
return fmt.Errorf("failed to convert string to byte: %w", err)
}

f.data = bin
if bin.value != nil {
f.value = bin.value
f.value = buf
case *string:
buf, err := hex.DecodeString(*v)
if err != nil {
return fmt.Errorf("failed to convert string to byte: %w", err)
}

f.value = buf
case []byte:
f.SetBytes(v)
mfdeveloper508 marked this conversation as resolved.
Show resolved Hide resolved
case *[]byte:
f.SetBytes(*v)
default:
return fmt.Errorf("data does not match required *Binary or (string, *string, []byte, *[]byte) type")
mfdeveloper508 marked this conversation as resolved.
Show resolved Hide resolved
}

return nil
}

Expand Down
78 changes: 76 additions & 2 deletions field/binary_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package field

import (
"reflect"
"testing"

"github.com/moov-io/iso8583/encoding"
Expand Down Expand Up @@ -76,7 +77,7 @@ func TestBinaryField(t *testing.T) {
err := bin.SetBytes(in)
require.NoError(t, err)

require.Equal(t, in, data.value)
require.Equal(t, in, bin.value)
})

// SetValue sets data to the data field
Expand All @@ -96,7 +97,7 @@ func TestBinaryField(t *testing.T) {

require.NoError(t, err)
require.Equal(t, len(in), n)
require.Equal(t, in, data.value)
require.Equal(t, in, bin.value)
})

t.Run("UnmarshalJSON unquotes input before handling it", func(t *testing.T) {
Expand Down Expand Up @@ -140,3 +141,76 @@ func TestBinaryNil(t *testing.T) {
bs = str.Value()
require.Nil(t, bs)
}

func TestBinaryFieldUnmarshal(t *testing.T) {
testValue := []byte{0x12, 0x34, 0x56}
str := NewBinaryValue(testValue)

val1 := &Binary{}
err := str.Unmarshal(val1)
require.NoError(t, err)
require.Equal(t, testValue, val1.Value())

var val2 string
err = str.Unmarshal(&val2)
require.NoError(t, err)
require.Equal(t, "123456", val2)

var val3 []byte
err = str.Unmarshal(&val3)
require.NoError(t, err)
require.Equal(t, testValue, val3)

val4 := reflect.ValueOf(&val2).Elem()
err = str.Unmarshal(val4)
require.NoError(t, err)
require.Equal(t, "123456", val4.String())

val5 := reflect.ValueOf(&val3).Elem()
err = str.Unmarshal(val5)
require.NoError(t, err)
require.Equal(t, testValue, val5.Bytes())

val6 := reflect.ValueOf(val2)
err = str.Unmarshal(val6)
require.Error(t, err)
require.Equal(t, "cannot set reflect.Value of type string", err.Error())

val7 := reflect.ValueOf(&val2)
mfdeveloper508 marked this conversation as resolved.
Show resolved Hide resolved
err = str.Unmarshal(val7)
require.Error(t, err)
require.Equal(t, "cannot set reflect.Value of type ptr", err.Error())

err = str.Unmarshal(nil)
require.Error(t, err)
require.Equal(t, "unsupported type: expected *Binary, *string, *[]byte, or reflect.Value, got <nil>", err.Error())
}

func TestBinaryFieldMarshal(t *testing.T) {
testValue := []byte{0x12, 0x34, 0x56}
str := NewBinaryValue(nil)

vstring := "123456"
err := str.Marshal(vstring)
require.NoError(t, err)
require.Equal(t, testValue, str.Value())

err = str.Marshal(&vstring)
require.NoError(t, err)
require.Equal(t, testValue, str.Value())

err = str.Marshal(testValue)
require.NoError(t, err)
require.Equal(t, testValue, str.Value())

err = str.Marshal(&testValue)
require.NoError(t, err)
require.Equal(t, testValue, str.Value())

err = str.Marshal(nil)
require.NoError(t, err)

err = str.Marshal(123456)
require.Error(t, err)
require.Equal(t, "data does not match required *Binary or (string, *string, []byte, *[]byte) type", err.Error())
}
2 changes: 1 addition & 1 deletion field/bitmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func (f *Bitmap) Unmarshal(v interface{}) error {

bmap, ok := v.(*Bitmap)
if !ok {
return fmt.Errorf("data does not match required *Bitmap type")
return fmt.Errorf("unsupported type: expected *Bitmap, got %T", v)
}

bmap.data = f.data
Expand Down
4 changes: 2 additions & 2 deletions field/composite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ func TestCompositePacking(t *testing.T) {
})

require.Error(t, err)
require.EqualError(t, err, "failed to set data from field 1: data does not match required *String type")
require.EqualError(t, err, "failed to set data from field 1: data does not match required *String or (string, *string, int, *int) type")
})

t.Run("Pack returns error on failure of subfield packing", func(t *testing.T) {
Expand Down Expand Up @@ -747,7 +747,7 @@ func TestCompositePacking(t *testing.T) {
err = composite.Unmarshal(data)

require.Error(t, err)
require.EqualError(t, err, "failed to get data from field 1: data does not match required *String type")
require.EqualError(t, err, "failed to get data from field 1: unsupported type: expected *String, *string, or reflect.Value, got *field.Numeric")
})

t.Run("Unpack returns an error on failure of subfield to unpack bytes", func(t *testing.T) {
Expand Down
62 changes: 40 additions & 22 deletions field/hex.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package field
import (
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"

"github.com/moov-io/iso8583/utils"
Expand All @@ -22,7 +22,6 @@ var _ json.Unmarshaler = (*Hex)(nil)
type Hex struct {
value string
spec *Spec
data *Hex
}

func NewHex(spec *Spec) *Hex {
Expand Down Expand Up @@ -50,9 +49,6 @@ func (f *Hex) SetSpec(spec *Spec) {

func (f *Hex) SetBytes(b []byte) error {
f.value = strings.ToUpper(hex.EncodeToString(b))
if f.data != nil {
*(f.data) = *f
}
return nil
}

Expand Down Expand Up @@ -132,34 +128,56 @@ func (f *Hex) SetData(data interface{}) error {
}

func (f *Hex) Unmarshal(v interface{}) error {
if v == nil {
return nil
}

str, ok := v.(*Hex)
if !ok {
return errors.New("data does not match required *Hex type")
switch val := v.(type) {
case reflect.Value:
if !val.CanSet() {
return fmt.Errorf("cannot set reflect.Value of type %s", val.Kind())
}

switch val.Kind() { //nolint:exhaustive
case reflect.String:
str, _ := f.String()
val.SetString(str)
case reflect.Slice:
buf, _ := f.Bytes()
val.SetBytes(buf)
default:
return fmt.Errorf("unsupported reflect.Value type: %s", val.Kind())
}
case *string:
*val, _ = f.String()
case *[]byte:
*val, _ = f.Bytes()
case *Hex:
val.value = f.value
default:
return fmt.Errorf("unsupported type: expected *Hex, *string, *[]byte, or reflect.Value, got %T", v)
}

str.value = f.value

return nil
}

func (f *Hex) Marshal(v interface{}) error {
if v == nil {
if v == nil || reflect.ValueOf(v).IsZero() {
f.value = ""
return nil
}

str, ok := v.(*Hex)
if !ok {
return fmt.Errorf("data does not match required *Hex type")
switch v := v.(type) {
case *Hex:
f.value = v.value
case string:
f.value = v
case *string:
f.value = *v
case []byte:
f.SetBytes(v)
case *[]byte:
f.SetBytes(*v)
default:
return fmt.Errorf("data does not match required *Hex or (string, *string, []byte, *[]byte) type")
}

f.data = str
if str.value != "" {
f.value = str.value
}
return nil
}

Expand Down
Loading
Loading