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 }