github.com/danil/iso8583@v0.21.0/codec8583/unmarshal8583.go (about)

     1  package codec8583
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"reflect"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/danil/iso8583/bitmap64"
    13  )
    14  
    15  type Unmarshaler interface {
    16  	Unmarshal(raw []byte, v interface{}) error
    17  }
    18  
    19  type unmarshal struct {
    20  	format  Format
    21  	source  io.Reader
    22  	target  reflect.Value
    23  	hashify Hasher
    24  }
    25  
    26  func NewUnmarshaler(f Format) unmarshal { return unmarshal{format: f, hashify: NewHashify()} }
    27  
    28  func (umrs unmarshal) Unmarshal(raw []byte, v interface{}) error {
    29  	err := umrs.unmarshal(raw, v)
    30  	if err != nil {
    31  		return fmt.Errorf("ISO 8583 unmarshal: %w, message: %#v", err, raw)
    32  	}
    33  	return nil
    34  }
    35  
    36  func (umrs unmarshal) unmarshal(raw []byte, v interface{}) error {
    37  	mtiLen := umrs.format[-1].Len()
    38  	bmpLen := umrs.format[0].Len()
    39  	if len(raw) < mtiLen+bmpLen {
    40  		return fmt.Errorf("message too small to read: %d < %d", len(raw), mtiLen+bmpLen)
    41  	}
    42  	umrs.source = bytes.NewReader(raw)
    43  	umrs.target = reflect.ValueOf(v).Elem()
    44  	err := umrs.decodeMTI()
    45  	if err != nil {
    46  		return fmt.Errorf("decode MTI: %w", err)
    47  	}
    48  	fldNums, err := umrs.decodeBitmaps()
    49  	if err != nil {
    50  		return fmt.Errorf("decode bitmaps: %w", err)
    51  	}
    52  	return umrs.decodeFields(fldNums)
    53  }
    54  
    55  func (umrs *unmarshal) decodeMTI() error {
    56  	mtiCodec := umrs.format[-1]
    57  	raw, err := mtiCodec.Read(umrs.source)
    58  	if err != nil {
    59  		return err
    60  	}
    61  	mti, err := mtiCodec.Decode(umrs.hashify, raw)
    62  	if err != nil {
    63  		return err
    64  	}
    65  	var mtiVal reflect.Value
    66  	for i := 0; i < umrs.target.NumField(); i++ {
    67  		fld := umrs.target.Type().Field(i)
    68  		tag := strings.Split(fld.Tag.Get(Tag8583), ",")[0] // use split to ignore tag "options" like omitempty, etc.
    69  		if tag == "MTI" {
    70  			mtiVal = umrs.target.Field(i)
    71  			break
    72  		}
    73  	}
    74  	if !mtiVal.IsValid() {
    75  		return errors.New("struct must have MTI field")
    76  	}
    77  	mtiVal.SetString(string(mti))
    78  	return nil
    79  }
    80  
    81  // decodeBitmaps decode all bitmaps and return ISO 8583 field numbers.
    82  func (umrs *unmarshal) decodeBitmaps() ([]int, error) {
    83  	bmpCodec := umrs.format[0]
    84  	raw, err := bmpCodec.Read(umrs.source)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  	priBmp, err := bmpCodec.Decode(umrs.hashify, raw)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	var fldNums []int
    93  	bmp := bitmap64.New(priBmp)
    94  	for i := 0; i < 64; i++ {
    95  		if bmp.Get(i + 1) {
    96  			fldNums = append(fldNums, i+1)
    97  		}
    98  	}
    99  	if !bmp.Get(1) {
   100  		return fldNums, nil
   101  	}
   102  	bmpCodec = umrs.format[1]
   103  	raw, err = bmpCodec.Read(umrs.source)
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	secBmp, err := bmpCodec.Decode(umrs.hashify, raw)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	bmp = bitmap64.New(secBmp)
   112  	for i := 0; i < 64; i++ {
   113  		if bmp.Get(i + 1) {
   114  			fldNums = append(fldNums, i+64+1)
   115  		}
   116  	}
   117  	return fldNums, nil
   118  }
   119  
   120  func (umrs *unmarshal) decodeFields(fldNums []int) error {
   121  	fldToTgtFld, err := decodingFieldToTargetFields(umrs.target)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	for _, fld := range fldNums {
   126  		if fld == 1 { // ISO 8583 first field (secondary bitmap) already read.
   127  			continue
   128  		}
   129  		err = umrs.decodeField(fld, fldToTgtFld)
   130  		if err != nil {
   131  			return fmt.Errorf("decode field: %d, %w, all fields: %v", fld, err, fldNums)
   132  		}
   133  	}
   134  	return nil
   135  }
   136  
   137  func (umrs *unmarshal) decodeField(fld int, fldToTgtFld map[int]int) error {
   138  	fldCodec := umrs.format[fld]
   139  	raw, err := fldCodec.Read(umrs.source)
   140  	if err != nil {
   141  		return err
   142  	}
   143  	val, err := fldCodec.Decode(umrs.hashify, raw)
   144  	if err != nil {
   145  		return err
   146  	}
   147  	fldIdx, ok := fldToTgtFld[fld]
   148  	if !ok {
   149  		return fmt.Errorf("struct does not have a field with tag: %#[1]v, the message in field: %#[1]v, has a value: %#[2]v", fld, string(val))
   150  	}
   151  	valueField := umrs.target.Field(fldIdx)
   152  	valueField.SetString(string(val))
   153  	return nil
   154  }
   155  
   156  // decodingFieldToTargetFields returns mapping of the ISO 8583 feilds to the target fields .
   157  func decodingFieldToTargetFields(target reflect.Value) (map[int]int, error) {
   158  	fldToTgtFld := make(map[int]int, target.NumField())
   159  	for i := 0; i < target.NumField(); i++ {
   160  		fld := target.Type().Field(i)
   161  		tag := fld.Tag.Get(Tag8583)
   162  		if tag == "" || tag == "MTI" {
   163  			continue
   164  		}
   165  		idx, err := strconv.Atoi(tag)
   166  		if err != nil {
   167  			return nil, err
   168  		}
   169  		fldToTgtFld[idx] = i
   170  	}
   171  	return fldToTgtFld, nil
   172  }