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

Experimental: simplify data structs #273

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 206 additions & 0 deletions exp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
package iso8583_test

import (
"testing"

"github.com/moov-io/iso8583"
"github.com/moov-io/iso8583/field"
"github.com/moov-io/iso8583/specs"
"github.com/stretchr/testify/require"
)

func TestStructWithTypes(t *testing.T) {
t.Run("pack", func(t *testing.T) {
panInt := 4242424242424242
panStr := "4242424242424242"

tests := []struct {
name string
input interface{}
expectedPackedString string
}{
// Tests for string type
{
name: "struct with string type and value set",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber string `index:"2"`
}{
MTI: "0110",
PrimaryAccountNumber: panStr,
},
expectedPackedString: "011040000000000000000000000000000000164242424242424242",
},
{
name: "struct with string type and no value",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber string `index:"2"`
}{
MTI: "0110",
},
expectedPackedString: "011000000000000000000000000000000000",
},
{
name: "struct with string type, no value and keepzero tag - length prefix is set to 0 and no value is following",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber string `index:"2,keepzero"`
}{
MTI: "0110",
},
expectedPackedString: "01104000000000000000000000000000000000",
},

// Tests for *string type
{
name: "struct with *string type and value set",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber *string `index:"2"`
}{
MTI: "0110",
PrimaryAccountNumber: &panStr,
},
expectedPackedString: "011040000000000000000000000000000000164242424242424242",
},
{
name: "struct with *string type and no value",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber *string `index:"2"`
}{
MTI: "0110",
},
expectedPackedString: "011000000000000000000000000000000000",
},
{
name: "struct with *string type, no value and keepzero tag",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber *string `index:"2,keepzero"`
}{
MTI: "0110",
},
expectedPackedString: "011000000000000000000000000000000000",
},

// Tests for int type
{
name: "struct with int type and value set",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber int `index:"2"`
}{
MTI: "0110",
PrimaryAccountNumber: panInt,
},
expectedPackedString: "011040000000000000000000000000000000164242424242424242",
},
{
name: "struct with int type and no value",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber int `index:"2"`
}{
MTI: "0110",
},
expectedPackedString: "011000000000000000000000000000000000",
},
{
name: "struct with int type, no value and keepzero tag",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber int `index:"2,keepzero"`
}{
MTI: "0110",
},
expectedPackedString: "011040000000000000000000000000000000010",
},

// Tests for *int type
{
name: "struct with *int type and value set",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber *int `index:"2"`
}{
MTI: "0110",
PrimaryAccountNumber: &panInt,
},
expectedPackedString: "011040000000000000000000000000000000164242424242424242",
},
{
name: "struct with *int type and no value",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber *int `index:"2"`
}{
MTI: "0110",
},
expectedPackedString: "011000000000000000000000000000000000",
},
{
name: "struct with *int type, no value and keepzero tag",
input: struct {
MTI string `index:"0"`
PrimaryAccountNumber *int `index:"2,keepzero"`
}{
MTI: "0110",
},
expectedPackedString: "011000000000000000000000000000000000",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
message := iso8583.NewMessage(specs.Spec87ASCII)
err := message.Marshal(tt.input)
require.NoError(t, err)

packed, err := message.Pack()
require.NoError(t, err)

require.Equal(t, tt.expectedPackedString, string(packed))
})
}
})

t.Run("unpack", func(t *testing.T) {
type authRequest struct {
MTI string `index:"0"`
PrimaryAccountNumber string `index:"2"`
}

packed := []byte("011040000000000000000000000000000000164242424242424242")

message := iso8583.NewMessage(specs.Spec87ASCII)
err := message.Unpack(packed)
require.NoError(t, err)

data := authRequest{}
err = message.Unmarshal(&data)
require.NoError(t, err)
require.Equal(t, "0110", data.MTI)
require.Equal(t, "4242424242424242", data.PrimaryAccountNumber)
})

t.Run("unpack2", func(t *testing.T) {
type authRequest struct {
MTI *string `index:"0"`
PrimaryAccountNumber *field.String `index:"2"`
}

packed := []byte("011040000000000000000000000000000000164242424242424242")

message := iso8583.NewMessage(specs.Spec87ASCII)
err := message.Unpack(packed)
require.NoError(t, err)

data := authRequest{}
err = message.Unmarshal(&data)
require.NoError(t, err)
require.Equal(t, "0110", *data.MTI)
require.Equal(t, "4242424242424242", data.PrimaryAccountNumber.Value())
})
}
13 changes: 9 additions & 4 deletions field/composite.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,12 +623,17 @@ var fieldNameTagRe = regexp.MustCompile(`^F.+$`)
// field name. If it does not match F.+ pattern, it checks value of `index`
// tag. If empty string, then index/tag was not found for the field.
func getFieldIndexOrTag(field reflect.StructField) (string, error) {
dataFieldName := field.Name

if fieldIndex := field.Tag.Get("index"); fieldIndex != "" {
return fieldIndex, nil
var fieldIndex string
// keep the order of tags for now, when index tag is deprecated we can
// change the order
for _, tag := range []string{"index", "iso8583"} {
if fieldIndex = field.Tag.Get(tag); fieldIndex != "" {
return fieldIndex, nil
}
}

dataFieldName := field.Name

if len(dataFieldName) > 0 && fieldNameTagRe.MatchString(dataFieldName) {
return dataFieldName[1:], nil
}
Expand Down
4 changes: 2 additions & 2 deletions field/composite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,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.Contains(t, err.Error(), "failed to set data from field 1: data does not match required *String")
})

t.Run("Pack returns error on failure of subfield packing", func(t *testing.T) {
Expand Down Expand Up @@ -745,7 +745,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.Contains(t, err.Error(), "failed to get data from field 1: data does not match required *String")
})

t.Run("Unpack returns an error on failure of subfield to unpack bytes", func(t *testing.T) {
Expand Down
50 changes: 36 additions & 14 deletions field/numeric.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ var _ json.Unmarshaler = (*Numeric)(nil)
type Numeric struct {
value int
spec *Spec
data *Numeric
}

func NewNumeric(spec *Spec) *Numeric {
Expand Down Expand Up @@ -54,9 +53,6 @@ func (f *Numeric) SetBytes(b []byte) error {
f.value = val
}

if f.data != nil {
*(f.data) = *f
}
return nil
}

Expand Down Expand Up @@ -143,19 +139,45 @@ func (f *Numeric) Unmarshal(v interface{}) error {
}

func (f *Numeric) SetData(data interface{}) error {
if data == nil {
return nil
}
switch v := data.(type) {
case *Numeric:
if v == nil {
return nil
}
f.value = v.value
case int:
f.value = v
case *int:
if v == nil {
f.value = 0
return nil
}
f.value = *v
case string:
if v == "" {
f.value = 0
return nil
}
val, err := strconv.Atoi(v)
if err != nil {
return utils.NewSafeError(err, "failed to convert sting value into number")
}
f.value = val
case *string:
if v == nil {
f.value = 0
return nil
}

num, ok := data.(*Numeric)
if !ok {
return fmt.Errorf("data does not match required *Numeric type")
val, err := strconv.Atoi(*v)
if err != nil {
return utils.NewSafeError(err, "failed to convert sting value into number")
}
f.value = val
default:
return fmt.Errorf("data does not match require *Numeric or supported numeric types (int, *int, string, *string)")
}

f.data = num
if num.value != 0 {
f.value = num.value
}
return nil
}

Expand Down
20 changes: 1 addition & 19 deletions field/numeric_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,8 @@ func TestNumericField(t *testing.T) {
require.NoError(t, err)
require.Equal(t, " 9876", string(packed))

numeric = NewNumeric(spec)
data := NewNumericValue(0)
numeric.SetData(data)
length, err = numeric.Unpack([]byte(" 9876"))
require.NoError(t, err)
require.Equal(t, 10, length)
require.Equal(t, 9876, data.Value())

numeric = NewNumeric(spec)
numeric.SetValue(9876)

require.Equal(t, 9876, numeric.Value())
}

Expand Down Expand Up @@ -151,18 +142,9 @@ func TestNumericFieldZeroLeftPaddedZero(t *testing.T) {
}

func TestNumericSetBytesSetsDataOntoDataStruct(t *testing.T) {
numeric := NewNumeric(&Spec{
Length: 1,
Description: "Field",
Enc: encoding.ASCII,
Pref: prefix.ASCII.Fixed,
})

data := &Numeric{}
err := numeric.SetData(data)
require.NoError(t, err)

err = numeric.SetBytes([]byte("9"))
err := data.SetBytes([]byte("9"))
require.NoError(t, err)

require.Equal(t, 9, data.Value())
Expand Down
Loading
Loading