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

     1  package codec8583
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"reflect"
     9  	"sort"
    10  	"strconv"
    11  	"strings"
    12  
    13  	"github.com/danil/iso8583/bitmap64"
    14  )
    15  
    16  type Marshaler interface {
    17  	Marshal(interface{}) ([]byte, error)
    18  }
    19  
    20  type marshal struct {
    21  	format  Format
    22  	source  reflect.Value
    23  	target  io.Writer
    24  	hashify Hasher
    25  }
    26  
    27  func NewMarshaler(f Format) marshal { return marshal{format: f, hashify: NewHashify()} }
    28  
    29  func (mrs marshal) Marshal(v interface{}) ([]byte, error) {
    30  	enc, err := mrs.marshal(v)
    31  	if err != nil {
    32  		return enc, fmt.Errorf("ISO 8583 marshal: %w, struct: %#v", err, v)
    33  	}
    34  	return enc, nil
    35  }
    36  
    37  func (mrs marshal) marshal(v interface{}) ([]byte, error) {
    38  	var target bytes.Buffer
    39  	mrs.source = reflect.ValueOf(v).Elem()
    40  	mrs.target = &target
    41  	var mtiVal reflect.Value
    42  	for i := 0; i < mrs.source.NumField(); i++ {
    43  		fld := mrs.source.Type().Field(i)
    44  		tag := strings.Split(fld.Tag.Get(Tag8583), ",")[0] // use split to ignore tag "options" like omitempty, etc.
    45  		if tag == "MTI" {
    46  			mtiVal = mrs.source.Field(i)
    47  			break
    48  		}
    49  	}
    50  	if !mtiVal.IsValid() {
    51  		return nil, errors.New("must have MTI field")
    52  	}
    53  	fldNums, fldVals := mrs.fieldNumbersAndValues()
    54  	priBmp, secBmp := mrs.bitmaps(fldNums)
    55  	if secBmp != [8]byte{} {
    56  		fldVals[1] = secBmp[:]
    57  		fldNums = append([]int{1}, fldNums...)
    58  	}
    59  	err := mrs.encodeMTI([]byte(mtiVal.String()))
    60  	if err != nil {
    61  		return nil, fmt.Errorf("encode MTI: %w", err)
    62  	}
    63  	err = mrs.encodePrimaryBitmap(priBmp)
    64  	if err != nil {
    65  		return nil, fmt.Errorf("encode primary bitmap: %w", err)
    66  	}
    67  	err = mrs.encodeFields(fldNums, fldVals)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	return target.Bytes(), nil
    72  }
    73  
    74  func (mrs *marshal) bitmaps(fldNums []int) ([8]byte, [8]byte) {
    75  	priBmp := bitmap64.Bitmap([8]byte{})
    76  	i := 0
    77  	for i < len(fldNums) {
    78  		n := fldNums[i]
    79  		if n > 64 {
    80  			break
    81  		}
    82  		priBmp.Set(n)
    83  		if i == len(fldNums)-1 {
    84  			return priBmp, [8]byte{}
    85  		}
    86  		i++
    87  	}
    88  	priBmp.Set(1)
    89  	secBmp := bitmap64.Bitmap([8]byte{})
    90  	for ; i < len(fldNums); i++ {
    91  		secBmp.Set(fldNums[i] - 64)
    92  	}
    93  	return priBmp, secBmp
    94  }
    95  
    96  func (mrs *marshal) fieldNumbersAndValues() ([]int, map[int][]byte) {
    97  	fldVals := map[int][]byte{}
    98  	for i := 0; i < mrs.source.NumField(); i++ {
    99  		fldVal := mrs.source.Field(i)
   100  		fld := mrs.source.Type().Field(i)
   101  		tag := fld.Tag.Get(Tag8583)
   102  		if tag == "" {
   103  			continue
   104  		}
   105  		idx, err := strconv.ParseInt(tag, 10, 64)
   106  		if err != nil {
   107  			continue
   108  		}
   109  		f := fldVal.Interface()
   110  		val := reflect.ValueOf(f).String()
   111  		if val == "" {
   112  			continue
   113  		}
   114  		fldVals[int(idx)] = []byte(val)
   115  	}
   116  	fldNums := make([]int, 0, len(fldVals))
   117  	for fld := range fldVals {
   118  		fldNums = append(fldNums, fld)
   119  	}
   120  	sort.Ints(fldNums)
   121  	return fldNums, fldVals
   122  }
   123  
   124  func (mrs *marshal) encodeMTI(mti []byte) error {
   125  	mtiCodec := mrs.format[-1]
   126  	raw, err := mtiCodec.Encode(mrs.hashify, mti)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	return mtiCodec.Write(mrs.target, raw)
   131  }
   132  
   133  func (mrs *marshal) encodePrimaryBitmap(priBmp [8]byte) error {
   134  	bmpCodec := mrs.format[1]
   135  	raw, err := bmpCodec.Encode(mrs.hashify, priBmp[:])
   136  	if err != nil {
   137  		return err
   138  	}
   139  	return bmpCodec.Write(mrs.target, raw)
   140  }
   141  
   142  func (mrs *marshal) encodeFields(fldNums []int, fldVals map[int][]byte) error {
   143  	for _, fld := range fldNums {
   144  		val := fldVals[fld]
   145  		err := mrs.encodeField(fld, val)
   146  		if err != nil {
   147  			return fmt.Errorf("encode field: %w, field: %d, value: %#v, all fields %v", err, fld, val, fldNums)
   148  		}
   149  	}
   150  	return nil
   151  }
   152  
   153  func (mrs *marshal) encodeField(fld int, val []byte) error {
   154  	fldCodec := mrs.format[fld]
   155  	raw, err := fldCodec.Encode(mrs.hashify, val)
   156  	if err != nil {
   157  		return err
   158  	}
   159  	return fldCodec.Write(mrs.target, raw)
   160  }