diff --git a/field/composite.go b/field/composite.go index 28df3ce..2adf7f7 100644 --- a/field/composite.go +++ b/field/composite.go @@ -6,14 +6,12 @@ import ( "fmt" "math" "reflect" - "regexp" "strconv" "sync" "github.com/moov-io/iso8583/encoding" "github.com/moov-io/iso8583/prefix" "github.com/moov-io/iso8583/sort" - "github.com/moov-io/iso8583/utils" ) @@ -676,21 +674,9 @@ func orderedKeys(kvs map[string]Field, sorter sort.StringSlice) []string { return keys } -var fieldNameTagRe = regexp.MustCompile(`^F.+$`) - // getFieldIndexOrTag returns index or tag of the field. First, it checks the // 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 - } - - if len(dataFieldName) > 0 && fieldNameTagRe.MatchString(dataFieldName) { - return dataFieldName[1:], nil - } - - return "", nil + return NewIndexTag(field).Tag, nil } diff --git a/index_tag.go b/field/index_tag.go similarity index 96% rename from index_tag.go rename to field/index_tag.go index 11ddcf8..16f7827 100644 --- a/index_tag.go +++ b/field/index_tag.go @@ -1,11 +1,14 @@ -package iso8583 +package field import ( "reflect" + "regexp" "strconv" "strings" ) +var fieldNameIndexRe = regexp.MustCompile(`^F.+$`) + type IndexTag struct { Id int // is -1 if index is not a number diff --git a/message.go b/message.go index c90da51..218ce1d 100644 --- a/message.go +++ b/message.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "reflect" - "regexp" "sort" "strconv" "sync" @@ -427,7 +426,7 @@ func (m *Message) Marshal(v interface{}) error { // iterate over struct fields for i := 0; i < dataStruct.NumField(); i++ { - indexTag := NewIndexTag(dataStruct.Type().Field(i)) + indexTag := field.NewIndexTag(dataStruct.Type().Field(i)) // skip field without index or if index in tag is not defined if indexTag.Id < 0 { @@ -486,23 +485,19 @@ func (m *Message) Unmarshal(v interface{}) error { // iterate over struct fields for i := 0; i < dataStruct.NumField(); i++ { - fieldIndex, err := getFieldIndex(dataStruct.Type().Field(i)) - if err != nil { - return fmt.Errorf("getting field %d index: %w", i, err) - } - - // skip field without index - if fieldIndex < 0 { - continue + indexTag := field.NewIndexTag(dataStruct.Type().Field(i)) + // skip field without index or if index in tag is not defined + if indexTag.Id < 0 { + return fmt.Errorf("getting field %d index error", i) } // we can get data only if field value is set - messageField := m.GetField(fieldIndex) + messageField := m.GetField(indexTag.Id) if messageField == nil { continue } - if _, set := m.fieldsMap[fieldIndex]; !set { + if _, set := m.fieldsMap[indexTag.Id]; !set { continue } @@ -512,47 +507,17 @@ func (m *Message) Unmarshal(v interface{}) error { if dataField.IsNil() || dataField.IsZero() { dataField.Set(reflect.New(dataField.Type().Elem())) } - err = messageField.Unmarshal(dataField.Interface()) + err := messageField.Unmarshal(dataField.Interface()) if err != nil { - return fmt.Errorf("failed to get value from field %d: %w", fieldIndex, err) + return fmt.Errorf("failed to get value from field %d: %w", indexTag.Id, err) } default: // Native types - err = messageField.Unmarshal(dataField) + err := messageField.Unmarshal(dataField) if err != nil { - return fmt.Errorf("failed to get value from field %d: %w", fieldIndex, err) + return fmt.Errorf("failed to get value from field %d: %w", indexTag.Id, err) } } } return nil } - -var fieldNameIndexRe = regexp.MustCompile(`^F\d+$`) - -// fieldIndex returns index of the field. First, it checks field name. If it -// does not match FNN (when NN is digits), it checks value of `index` tag. If -// negative value returned (-1) then index was not found for the field. -func getFieldIndex(field reflect.StructField) (int, error) { - dataFieldName := field.Name - - if indexStr := field.Tag.Get("index"); indexStr != "" { - fieldIndex, err := strconv.Atoi(indexStr) - if err != nil { - return -1, fmt.Errorf("converting field index into int: %w", err) - } - - return fieldIndex, nil - } - - if len(dataFieldName) > 0 && fieldNameIndexRe.MatchString(dataFieldName) { - indexStr := dataFieldName[1:] - fieldIndex, err := strconv.Atoi(indexStr) - if err != nil { - return -1, fmt.Errorf("converting field index into int: %w", err) - } - - return fieldIndex, nil - } - - return -1, nil -} diff --git a/message_test.go b/message_test.go index 0680d9c..a4d23e5 100644 --- a/message_test.go +++ b/message_test.go @@ -1415,10 +1415,8 @@ func Test_getFieldIndex(t *testing.T) { F1 string }{}).Elem() - index, err := getFieldIndex(st.Type().Field(0)) - - require.NoError(t, err) - require.Equal(t, 1, index) + indexTag := field.NewIndexTag(st.Type().Field(0)) + require.Equal(t, 1, indexTag.Id) }) t.Run("returns index from field tag instead of field name when both match", func(t *testing.T) { @@ -1426,10 +1424,8 @@ func Test_getFieldIndex(t *testing.T) { F1 string `index:"2"` }{}).Elem() - index, err := getFieldIndex(st.Type().Field(0)) - - require.NoError(t, err) - require.Equal(t, 2, index) + indexTag := field.NewIndexTag(st.Type().Field(0)) + require.Equal(t, 2, indexTag.Id) }) t.Run("returns index from field tag", func(t *testing.T) { @@ -1440,22 +1436,16 @@ func Test_getFieldIndex(t *testing.T) { }{}).Elem() // get index from field Name - _, err := getFieldIndex(st.Type().Field(0)) - - require.Error(t, err) - require.EqualError(t, err, "converting field index into int: strconv.Atoi: parsing \"abcd\": invalid syntax") + indexTag := field.NewIndexTag(st.Type().Field(0)) + require.Equal(t, -1, indexTag.Id) // get index from field F - index, err := getFieldIndex(st.Type().Field(1)) - - require.NoError(t, err) - require.Equal(t, 2, index) + indexTag = field.NewIndexTag(st.Type().Field(1)) + require.Equal(t, 2, indexTag.Id) // get index from field Amount - index, err = getFieldIndex(st.Type().Field(2)) - - require.NoError(t, err) - require.Equal(t, 3, index) + indexTag = field.NewIndexTag(st.Type().Field(2)) + require.Equal(t, 3, indexTag.Id) }) t.Run("returns empty string when no tag and field name does not match the pattern", func(t *testing.T) { @@ -1463,20 +1453,16 @@ func Test_getFieldIndex(t *testing.T) { Name string }{}).Elem() - index, err := getFieldIndex(st.Type().Field(0)) - - require.NoError(t, err) - require.Equal(t, -1, index) + indexTag := field.NewIndexTag(st.Type().Field(0)) + require.Equal(t, -1, indexTag.Id) // single letter field without tag is ignored st = reflect.ValueOf(&struct { F string }{}).Elem() - index, err = getFieldIndex(st.Type().Field(0)) - - require.NoError(t, err) - require.Equal(t, -1, index) + indexTag = field.NewIndexTag(st.Type().Field(0)) + require.Equal(t, -1, indexTag.Id) }) }