Skip to content

Commit 7ac888e

Browse files
authored
Merge pull request #19 from jattento/feature/improve-code-quality
improve code quality
2 parents 92acb5a + 5f83680 commit 7ac888e

File tree

6 files changed

+378
-337
lines changed

6 files changed

+378
-337
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[![codecov](https://codecov.io/gh/jattento/go-iso8583/branch/master/graph/badge.svg)](https://codecov.io/gh/jattento/go-iso8583)
2+
[![Maintainability](https://api.codeclimate.com/v1/badges/94a2058a2b0823cf31be/maintainability)](https://codeclimate.com/github/jattento/go-iso8583/maintainability)
23

34
| Version | Build |
45
|----------|:-------------:|
@@ -35,7 +36,7 @@ import "github.com/jattento/go-iso8583/pkg/iso8583"
3536
## Quick start
3637

3738
```go
38-
import "github.com/go-iso8583/pkg/iso8583"
39+
import "github.com/jattento/go-iso8583/pkg/iso8583"
3940

4041
type PurchaseRequest struct {
4142
MTI iso8583.MTI `iso8583:"mti"`
@@ -77,7 +78,7 @@ func GenerateStaticReqBytes() ([]byte, error) {
7778
```
7879

7980
```go
80-
import "github.com/go-iso8583/pkg/iso8583"
81+
import "github.com/jattento/go-iso8583/pkg/iso8583"
8182

8283
type PurchaseResponse struct {
8384
MTI iso8583.MTI `iso8583:"mti,length:4"`

pkg/iso8583/decode.go

Lines changed: 136 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ type UnmarshalerBitmap interface {
4747
Bits() (m map[int]bool, err error)
4848
}
4949

50+
var errStructFieldNonExistent = errors.New("non existent struct field")
51+
5052
// Unmarshal parses the ISO-8583 data and stores the result
5153
// in the struct pointed to by v. If v is nil or not a pointer to a struct,
5254
// Unmarshal returns an error.
@@ -59,8 +61,8 @@ type UnmarshalerBitmap interface {
5961
// returns the amount of bytes consumed from original message. If unused bytes remain from input
6062
// its not considerate an error.
6163
// If an error is encountered a counter with consumed bytes up to the moment is returned.
62-
func Unmarshal(data []byte, v interface{}) (int, error) { // TODO: Check duplicated fieds
63-
input := reflect.ValueOf(v)
64+
func Unmarshal(data []byte, v interface{}) (int, error) {
65+
strctInput := reflect.ValueOf(v)
6466
// bitnapN works like an index that allows to know which fields are already
6567
// mapped to a bitmap. For example: If bitmap = 10 means that all fields from 1 to 10
6668
// are contemplated by already unmarshaled bitmaps and the next bitmap should consider
@@ -69,77 +71,36 @@ func Unmarshal(data []byte, v interface{}) (int, error) { // TODO: Check duplica
6971

7072
// Because only each implementation of iso8583.Unmarshaler know how many bytes it needs to consume
7173
// and can potentially only know it while reading the field raw data; all currently un-read bytes
72-
// are given to the fields which return how many bytes from the message it consumed, value that is
73-
// subtracted from unconsumedBytes.
74-
unconsumedBytes := make([]byte, len(data))
75-
copy(unconsumedBytes, data)
76-
77-
// consumed returns the amount of consumed bytes
78-
consumed := func() int {
79-
return len(data) - len(unconsumedBytes)
80-
}
81-
82-
// consume reduces unconsumedBytes by the indicated amount with pointer safety.
83-
consume := func(m int, f string) error {
84-
if m > len(unconsumedBytes) {
85-
// This error can only be caused by bad unmarshal implementations
86-
return fmt.Errorf("iso8583.unmarshal: Unmarshaler from field %s returned a n higher than unconsumed "+
87-
"bytes", f)
88-
}
89-
unconsumedBytes = unconsumedBytes[m:]
90-
return nil
91-
}
92-
93-
// mustLen returns the capacity of a field ignoring all possible errors.
94-
mustLen := func(f string) int {
95-
_, tag, _ := getUnmarshaler(input, f)
96-
l, _ := tag.lenINT()
97-
return l
98-
}
74+
// are given to the fields which return how many bytes from the message it consumed.
75+
buffer := newUnmarshalBuffer(data)
9976

10077
// Only pointer to structs can be unmarshaled.
101-
if !isPointerToStruct(input) {
102-
return consumed(), errors.New("iso8583.unmarshal: interface input is not a pointer to a structure")
103-
}
104-
105-
// Unmarshal MTI.
106-
m, err := unmarshalField(input, _tagMTI, unconsumedBytes)
107-
if err != nil {
108-
return consumed(), err
78+
if !isPointerToStruct(strctInput) {
79+
return buffer.UntilNowConsumed(), errors.New("iso8583.unmarshal: interface input is not a pointer to a structure")
10980
}
11081

111-
if err := consume(m, _tagMTI); err != nil {
112-
return consumed(), err
113-
}
114-
115-
// Unmarshal first bitmap.
116-
m, err = unmarshalField(input, _tagBITMAP, unconsumedBytes)
82+
representativeBits, err := readMessageHeader(buffer, strctInput)
11783
if err != nil {
118-
return consumed(), err
84+
return buffer.UntilNowConsumed(), err
11985
}
12086

121-
if err := consume(m, _tagBITMAP); err != nil {
122-
return consumed(), err
123-
}
87+
// Obtain the length tag from first bitmap to know how many
88+
// fields presences are indicated by him.
89+
bitmapN += representativeBits
12490

12591
// Get Bits method from first bitmap type to know which fields
12692
// to expect.
127-
firstFieldsMethod, err := getBitsMethod(input, _tagBITMAP)
93+
firstFieldsMethod, err := getBitsMethod(strctInput, _tagBITMAP)
12894
if err != nil {
129-
return consumed(), err
95+
return buffer.UntilNowConsumed(), err
13096
}
13197

13298
// Execute Bits method.
13399
firstFieldsList, err := firstFieldsMethod()
134100
if err != nil {
135-
return consumed(), fmt.Errorf("iso8583.unmarshal: failed reading first bitmap: %w", err)
101+
return buffer.UntilNowConsumed(), fmt.Errorf("iso8583.unmarshal: failed reading first bitmap: %w", err)
136102
}
137103

138-
// Obtain the length tag from first bitmap to know how many
139-
// fields presences are indicated by him.
140-
// These operation are safe because there were executed during unmarshaling.
141-
bitmapN += mustLen(_tagBITMAP)
142-
143104
// This block is needed because if we use the original "firstFieldList" variable, we would copy only the reference
144105
// to the map and modify the value of the "bitmap" field.
145106
fieldList := make(map[int]bool)
@@ -157,64 +118,130 @@ func Unmarshal(data []byte, v interface{}) (int, error) { // TODO: Check duplica
157118
}
158119

159120
// Unmarshal current field.
160-
m, err := unmarshalField(input, strconv.Itoa(n), unconsumedBytes)
121+
m, tagValues, err := unmarshalField(strctInput, strconv.Itoa(n), buffer.Bytes())
161122
if err != nil {
162-
return consumed(), err
123+
return buffer.UntilNowConsumed(), err
163124
}
164125

165-
if err := consume(m, "struct "+strconv.Itoa(n)); err != nil {
166-
return consumed(), err
126+
if err := buffer.IncrementConsumedCounter(m, "struct "+strconv.Itoa(n)); err != nil {
127+
return buffer.UntilNowConsumed(), err
167128
}
168129

169130
// Check if current field is a bitmap.
170-
if bitsMethod, err := getBitsMethod(input, strconv.Itoa(n)); bitsMethod != nil && err == nil {
131+
if bitsMethod, err := getBitsMethod(strctInput, strconv.Itoa(n)); bitsMethod != nil && err == nil {
171132
// If bits method is present current field is a bitmap and the method is executed.
172133
fieldsListExpansion, err := bitsMethod()
173134
if err != nil {
174-
return consumed(), fmt.Errorf("iso8583.unmarshal: failed reading field %v bitmap: %w", n, err)
135+
return buffer.UntilNowConsumed(), fmt.Errorf("iso8583.unmarshal: failed reading field %v bitmap: %w", n, err)
175136
}
176137

177138
// Expand current field list with obtained information from current field.
178139
expandFieldList(fieldList, fieldsListExpansion, bitmapN)
179140

180141
// Obtain the length tag from first bitmap to know how many
181142
// fields presences are indicated by him.
182-
// These operation are safe because there were executed during unmarshaling.
183143
// The value is added to index of mapped fields.
184-
bitmapN += mustLen(strconv.Itoa(n))
144+
bitmapN += tagValues.Length
185145
}
186146
}
187147

188148
// Return the amount of consumed fields.
189-
return consumed(), nil
149+
return buffer.UntilNowConsumed(), nil
190150
}
191151

192-
// unmarshalField and save the value. Returns the amount of consumed bytes.
193-
func unmarshalField(strct reflect.Value, fieldName string, bytes []byte) (int, error) {
194-
fieldInterface, tag, err := getUnmarshaler(strct, fieldName)
152+
type unmarshalBuffer struct {
153+
data []byte
154+
placeholder int
155+
}
156+
157+
func newUnmarshalBuffer(data []byte) *unmarshalBuffer {
158+
buffer := unmarshalBuffer{data: make([]byte, len(data))}
159+
copy(buffer.data, data)
160+
return &buffer
161+
}
162+
163+
// incrementConsumed increments the placeholder considering the data length.
164+
func (buffer *unmarshalBuffer) IncrementConsumedCounter(n int, fieldName string) error {
165+
if len(buffer.data[buffer.placeholder:]) < n {
166+
// This error can only be caused by bad unmarshal implementations
167+
return fmt.Errorf("iso8583.unmarshal: Unmarshaler from field %s returned a n higher than unconsumed "+
168+
"bytes", fieldName)
169+
}
170+
171+
buffer.placeholder += n
172+
return nil
173+
}
174+
175+
// UntilNowConsumed returns the amount of until now consumed bytes
176+
func (buffer *unmarshalBuffer) UntilNowConsumed() int { return buffer.placeholder }
177+
178+
// Bytes returns a copy from the input data bytes remainder.
179+
func (buffer *unmarshalBuffer) Bytes() []byte {
180+
data := make([]byte, len(buffer.data[buffer.placeholder:]))
181+
copy(data, buffer.data[buffer.placeholder:])
182+
return data
183+
}
184+
185+
// readMessageHeader reads the message MTI and the first bitmap.
186+
// Returns the amount of representative bits of the bitmap.
187+
func readMessageHeader(buffer *unmarshalBuffer, strct reflect.Value) (int, error) {
188+
// Unmarshal MTI.
189+
consumed, _, err := unmarshalField(strct, _tagMTI, buffer.Bytes())
195190
if err != nil {
196191
return 0, err
197192
}
198193

199-
if fieldInterface == nil {
200-
return 0, fmt.Errorf("iso8583.unmarshal: unknown field in message '%v', cant resolve upcomming fields",
201-
fieldName)
194+
if err := buffer.IncrementConsumedCounter(consumed, _tagMTI); err != nil {
195+
return 0, err
202196
}
203197

204-
return executeUnmarshal(fieldInterface, bytes, *tag)
198+
return readFirstBitmap(buffer, strct)
205199
}
206200

207-
// executeUnmarshal calls unmarshal method of objective, obtaining parameters from tags.
208-
// Returns consumed bytes from implementation.
209-
func executeUnmarshal(field Unmarshaler, b []byte, tag tags) (int, error) {
210-
// Transform length from string to int type.
211-
length, err := tag.lenINT()
201+
// readFirstBitmap reads the first bitmap and returns the amount of representative bits.
202+
func readFirstBitmap(buffer *unmarshalBuffer, strct reflect.Value) (int, error) {
203+
// Unmarshal first bitmap.
204+
consumed, tagValues, err := unmarshalField(strct, _tagBITMAP, buffer.Bytes())
212205
if err != nil {
213206
return 0, err
214207
}
215208

209+
if err := buffer.IncrementConsumedCounter(consumed, _tagBITMAP); err != nil {
210+
return 0, err
211+
}
212+
213+
return tagValues.Length, nil
214+
}
215+
216+
// unmarshalField and save the value. Returns the amount of consumed bytes.
217+
func unmarshalField(strct reflect.Value, fieldName string, bytes []byte) (int, tags, error) {
218+
fieldValue, tag, err := searchStructField(strct, fieldName)
219+
if err != nil {
220+
if errors.Is(err, errStructFieldNonExistent) {
221+
err = fmt.Errorf("unknown field in message '%v', cant resolve upcomming fields",
222+
fieldName)
223+
}
224+
225+
return 0, tags{}, fmt.Errorf("iso8583.unmarshal: %w", err)
226+
}
227+
228+
// founded field must implement Unmarshaler, otherwise an error is returned.
229+
fieldInterface, isValidUnmarshaler := fieldValue.Interface().(Unmarshaler)
230+
if !isValidUnmarshaler {
231+
return 0, tags{}, fmt.Errorf(
232+
"iso8583.unmarshal: field %s is present but does not implement Unmarshaler interface", fieldName)
233+
}
234+
235+
consumed, err := executeUnmarshal(fieldInterface, bytes, tag)
236+
237+
return consumed, tag, err
238+
}
239+
240+
// executeUnmarshal calls unmarshal method of objective, obtaining parameters from tags.
241+
// Returns consumed bytes from implementation.
242+
func executeUnmarshal(field Unmarshaler, b []byte, tag tags) (int, error) {
216243
// Execute unmarshal.
217-
n, err := field.UnmarshalISO8583(b, length, tag.Encoding)
244+
n, err := field.UnmarshalISO8583(b, tag.Length, tag.Encoding)
218245
if err != nil {
219246
return 0, fmt.Errorf("iso8583.unmarshal: cant unmarshal field %v: %w", tag.Field, err)
220247
}
@@ -223,11 +250,12 @@ func executeUnmarshal(field Unmarshaler, b []byte, tag tags) (int, error) {
223250
return n, nil
224251
}
225252

226-
// getUnmarshaler iterates over target struct until it finds a field that matches with the field name.
227-
// If field is not found all values are returned in nil.
228-
func getUnmarshaler(v reflect.Value, n string) (Unmarshaler, *tags, error) {
253+
// Returns the reflect value of the indicated field with its tags.
254+
// If v isn't a struct this function panics.
255+
// Returns errStructFieldNonExistent if the field is non existing.
256+
func searchStructField(v reflect.Value, n string) (reflect.Value, tags, error) {
229257
var strct reflect.Value
230-
var tag *tags
258+
var tag tags
231259

232260
// Obtain underlying struct.
233261
for strct = v; strct.Kind() == reflect.Ptr; {
@@ -237,44 +265,48 @@ func getUnmarshaler(v reflect.Value, n string) (Unmarshaler, *tags, error) {
237265
// Iterate over struct until a field match with the tag name.
238266
var vField reflect.Value
239267
for index, strctType := 0, strct.Type(); index < strctType.NumField(); index++ {
240-
if tags := readTags(strctType.Field(index).Tag); tags != nil && tags.Field == n {
268+
if structFieldValue, structFieldTags, err := getStructFieldData(strct, index); structFieldTags.Field == n {
269+
if err != nil {
270+
return reflect.Value{}, tags{}, err
271+
}
272+
241273
if !reflect.ValueOf(vField).IsZero() {
242-
return nil, nil, fmt.Errorf("iso8583.marshal: field %v is repeteated in struct", n)
274+
return reflect.Value{}, tags{}, fmt.Errorf("field %v is repeteated in struct", n)
243275
}
244276

245-
vField = strct.Field(index)
246-
if vField.Kind() != reflect.Ptr {
247-
vField = vField.Addr()
277+
if structFieldValue.Kind() != reflect.Ptr {
278+
structFieldValue = structFieldValue.Addr()
248279
}
249280

250-
tag = tags
281+
vField = structFieldValue
282+
tag = structFieldTags
251283
}
252284
}
253285

254286
// If field is not found all values are returned nil.
255287
if reflect.ValueOf(vField).IsZero() {
256288
// Field not in struct
257-
return nil, nil, nil
289+
return reflect.Value{}, tags{}, errStructFieldNonExistent
258290
}
259291

260-
// founded field must implement Unmarshaler, otherwise an error is returned.
261-
field, isValidUnmarshaler := vField.Interface().(Unmarshaler)
262-
if !isValidUnmarshaler {
263-
return nil, nil, fmt.Errorf(
264-
"iso8583.unmarshal: %s is present and its a struct but does not implement Unmarshaler interface", n)
265-
}
292+
return vField, tag, nil
293+
}
294+
295+
// getStructFieldData returns the specified struct field data using the struct index.
296+
func getStructFieldData(parentStructValue reflect.Value, index int) (reflect.Value, tags, error) {
297+
// search tags from current field, if there aren't field is ignored.
298+
tags, err := searchTags(parentStructValue.Type().Field(index))
266299

267-
// Return field data.
268-
return field, tag, nil
300+
return parentStructValue.Field(index), tags, err
269301
}
270302

271303
// getBitsMethod searches a iso8583.UnmarshalerBitmap implementation and returns it Bits method.
272304
func getBitsMethod(strct reflect.Value, fieldName string) (func() (map[int]bool, error), error) {
273305
// Reuse getUnmarshaler because iso8583.Unmarshaler contains iso8583.UnmarshalerBitmap
274-
bitmapUnmarshaler, _, _ := getUnmarshaler(strct, fieldName)
306+
bitmapUnmarshaler, _, _ := searchStructField(strct, fieldName)
275307

276308
// Field must implement iso8583.UnmarshalerBitmap.
277-
bitmap, ok := bitmapUnmarshaler.(UnmarshalerBitmap)
309+
bitmap, ok := bitmapUnmarshaler.Interface().(UnmarshalerBitmap)
278310
if !ok {
279311
return nil, fmt.Errorf("iso8583.unmarshal: %s field is present but does not implement UnmarshalerBitmap",
280312
fieldName)
@@ -285,7 +317,12 @@ func getBitsMethod(strct reflect.Value, fieldName string) (func() (map[int]bool,
285317
}
286318

287319
func isPointerToStruct(v reflect.Value) bool {
288-
return v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct
320+
underlyingObj := v
321+
for underlyingObj.Kind() == reflect.Ptr {
322+
underlyingObj = underlyingObj.Elem()
323+
}
324+
325+
return v.Kind() == reflect.Ptr && underlyingObj.Kind() == reflect.Struct
289326
}
290327

291328
func expandFieldList(list map[int]bool, expansion map[int]bool, bitmapN int) map[int]bool {

0 commit comments

Comments
 (0)