Skip to content

Commit

Permalink
add field tag and support different types for String, Numeric
Browse files Browse the repository at this point in the history
  • Loading branch information
alovak committed Sep 7, 2023
1 parent cb6628d commit b65ea63
Show file tree
Hide file tree
Showing 8 changed files with 166 additions and 113 deletions.
3 changes: 2 additions & 1 deletion exp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func TestStructWithTypes(t *testing.T) {
MTI string `index:"0"`
PrimaryAccountNumber string `index:"2"`
ProcessingCode int `index:"3"`
TransactionAmount int `index:"4,keepzero"` // we will set message field value to 0
}

t.Run("pack", func(t *testing.T) {
Expand All @@ -29,6 +30,6 @@ func TestStructWithTypes(t *testing.T) {
packed, err := message.Pack()
require.NoError(t, err)

require.Equal(t, "011060000000000000000000000000000000164242424242424242200000", string(packed))
require.Equal(t, "011070000000000000000000000000000000164242424242424242200000000000000000", string(packed))
})
}
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
64 changes: 24 additions & 40 deletions field/numeric.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"reflect"
"math"
"strconv"

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

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

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

Expand Down Expand Up @@ -144,44 +140,32 @@ func (f *Numeric) Unmarshal(v interface{}) error {
}

func (f *Numeric) SetData(data interface{}) error {
if v, ok := data.(reflect.Value); ok {
switch v.Kind() {
case reflect.Int:
f.value = int(v.Int())
case reflect.String:
val, err := strconv.Atoi(v.String())
if err != nil {
return utils.NewSafeError(err, "failed to convert into number")
}
f.value = val
default:
return fmt.Errorf("data does not match required *Numeric type")
switch v := data.(type) {
case *Numeric:
if v == nil {
return nil
}
return nil
}

// if v.Kind() == reflect.Int {
// }
// if v, ok := data.(reflect.Value); ok {
// if v.CanSet() {
// v.Set(reflect.ValueOf(f.value))
// return nil
// }
// }

if data == nil {
return nil
}

num, ok := data.(*Numeric)
if !ok {
return fmt.Errorf("data does not match required *Numeric type")
f.value = v.value
case int:
f.value = v
case int32:
if v >= math.MinInt32 && v <= math.MaxInt32 {
f.value = int(v)
} else {
return fmt.Errorf("int32 value out of range for int")
}
case int64:
if v >= math.MinInt64 && v <= math.MaxInt64 {
f.value = int(v)
} else {
return fmt.Errorf("int64 value out of range for int")
}
case []byte:
return f.SetBytes(v)
default:
return fmt.Errorf("data does not match require *Numeric or supported numeric types (int, int32, int64)")
}

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

Expand Down
9 changes: 0 additions & 9 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
48 changes: 19 additions & 29 deletions field/string.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strconv"

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

func NewString(spec *Spec) *String {
Expand All @@ -41,9 +40,6 @@ func (f *String) SetSpec(spec *Spec) {

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

Expand Down Expand Up @@ -130,33 +126,27 @@ func (f *String) Unmarshal(v interface{}) error {
}

func (f *String) SetData(data interface{}) error {
if v, ok := data.(reflect.Value); ok {
switch v.Kind() {
case reflect.String:
f.value = v.String()
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
f.value = fmt.Sprintf("%d", v.Int())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
f.value = fmt.Sprintf("%d", v.Uint())
default:
return fmt.Errorf("unsupported type %v", v.Kind())
switch v := data.(type) {
case *String:
if v == nil {
return nil
}
return nil
}

if data == nil {
return nil
}

str, ok := data.(*String)
if !ok {
return fmt.Errorf("data does not match required *String type")
f.value = v.value
case string:
if v == "" {
return nil
}
f.value = v
case int:
f.value = strconv.FormatInt(int64(v), 10)
case int32:
f.value = strconv.FormatInt(int64(v), 10)
case int64:
f.value = strconv.FormatInt(v, 10)
default:
return fmt.Errorf("data does not match required *String or string type")
}

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

Expand Down
18 changes: 1 addition & 17 deletions field/string_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,24 +43,8 @@ func TestStringField(t *testing.T) {
require.Equal(t, " hello", string(packed))

str = NewString(spec)
data := NewStringValue("")
str.SetData(data)
length, err = str.Unpack([]byte(" olleh"))
require.NoError(t, err)
require.Equal(t, 10, length)
require.Equal(t, "olleh", data.Value())

str = NewString(spec)
data = &String{}
str.SetData(data)
err = str.SetBytes([]byte("hello"))
require.NoError(t, err)
require.Equal(t, "hello", data.Value())

str = NewString(spec)

str.SetValue("hello")
require.Equal(t, "hello", data.Value())
require.Equal(t, "hello", str.Value())
}

func TestStringNil(t *testing.T) {
Expand Down
93 changes: 93 additions & 0 deletions field_tag.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
package iso8583

import (
"reflect"
"strconv"
"strings"
)

type FieldTag struct {
Id int // is -1 if index is not a number
Index string

// KeepZero tells the marshaler to use zero value and set bitmap bit to
// 1 for this field. Default behavior is to omit the field from the
// message if it's zero value.
KeepZero bool
}

func NewFieldTag(field reflect.StructField) FieldTag {
// value of the key "index" in the tag
var value 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 value = field.Tag.Get(tag); value != "" {
break
}
}

// format of the value is "id[,keep_zero_value]"
// id is the id of the field
// let's parse it
if value != "" {
index, keepZero := parseValue(value)

id, err := strconv.Atoi(index)
if err != nil {
id = -1
}

return FieldTag{
Id: id,
Index: index,
KeepZero: keepZero,
}
}

dataFieldName := field.Name
if len(dataFieldName) > 0 && fieldNameIndexRe.MatchString(dataFieldName) {
indexStr := dataFieldName[1:]
fieldIndex, err := strconv.Atoi(indexStr)
if err != nil {
return FieldTag{
Id: -1,
Index: indexStr,
}
}

return FieldTag{
Id: fieldIndex,
Index: indexStr,
}
}

return FieldTag{
Id: -1,
}
}

func parseValue(value string) (index string, keepZero bool) {
if value == "" {
return
}

// split the value by comma
values := strings.Split(value, ",")

// the first value is the index
index = values[0]

// if there is only one value, return
if len(values) == 1 {
return
}

// if the second value is "keep_zero_value", set the flag
if values[1] == "keepzero" {
keepZero = true
}

return
}
Loading

0 comments on commit b65ea63

Please sign in to comment.