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 }