diff --git a/field/binary.go b/field/binary.go index 07a48708..6ae6df49 100644 --- a/field/binary.go +++ b/field/binary.go @@ -10,9 +10,11 @@ import ( "github.com/moov-io/iso8583/utils" ) -var _ Field = (*Binary)(nil) -var _ json.Marshaler = (*Binary)(nil) -var _ json.Unmarshaler = (*Binary)(nil) +var ( + _ Field = (*Binary)(nil) + _ json.Marshaler = (*Binary)(nil) + _ json.Unmarshaler = (*Binary)(nil) +) type Binary struct { value []byte @@ -72,43 +74,24 @@ func (f *Binary) SetValue(v []byte) { func (f *Binary) Pack() ([]byte, error) { data := f.value - if f.spec.Pad != nil { - data = f.spec.Pad.Pad(data, f.spec.Length) - } - - packed, err := f.spec.Enc.Encode(data) - if err != nil { - return nil, fmt.Errorf("failed to encode content: %w", err) - } - - packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(data)) - if err != nil { - return nil, fmt.Errorf("failed to encode length: %w", err) - } + packer := f.spec.getPacker() - return append(packedLength, packed...), nil + return packer.Pack(data, f.spec) } func (f *Binary) Unpack(data []byte) (int, error) { - dataLen, prefBytes, err := f.spec.Pref.DecodeLength(f.spec.Length, data) - if err != nil { - return 0, fmt.Errorf("failed to decode length: %w", err) - } + unpacker := f.spec.getUnpacker() - raw, read, err := f.spec.Enc.Decode(data[prefBytes:], dataLen) + raw, bytesRead, err := unpacker.Unpack(data, f.spec) if err != nil { - return 0, fmt.Errorf("failed to decode content: %w", err) - } - - if f.spec.Pad != nil { - raw = f.spec.Pad.Unpad(raw) + return 0, err } if err := f.SetBytes(raw); err != nil { return 0, fmt.Errorf("failed to set bytes: %w", err) } - return read + prefBytes, nil + return bytesRead, nil } // Deprecated. Use Marshal instead diff --git a/field/composite.go b/field/composite.go index 0db1399f..b46252b8 100644 --- a/field/composite.go +++ b/field/composite.go @@ -15,9 +15,11 @@ import ( "github.com/moov-io/iso8583/utils" ) -var _ Field = (*Composite)(nil) -var _ json.Marshaler = (*Composite)(nil) -var _ json.Unmarshaler = (*Composite)(nil) +var ( + _ Field = (*Composite)(nil) + _ json.Marshaler = (*Composite)(nil) + _ json.Unmarshaler = (*Composite)(nil) +) // Composite is a wrapper object designed to hold ISO8583 TLVs, subfields and // subelements. Because Composite handles both of these usecases generically, diff --git a/field/numeric.go b/field/numeric.go index e4ebc8eb..3f30ebd7 100644 --- a/field/numeric.go +++ b/field/numeric.go @@ -9,9 +9,11 @@ import ( "github.com/moov-io/iso8583/utils" ) -var _ Field = (*Numeric)(nil) -var _ json.Marshaler = (*Numeric)(nil) -var _ json.Unmarshaler = (*Numeric)(nil) +var ( + _ Field = (*Numeric)(nil) + _ json.Marshaler = (*Numeric)(nil) + _ json.Unmarshaler = (*Numeric)(nil) +) type Numeric struct { value int64 @@ -84,44 +86,25 @@ func (f *Numeric) SetValue(v int64) { func (f *Numeric) Pack() ([]byte, error) { data := []byte(strconv.FormatInt(f.value, 10)) - if f.spec.Pad != nil { - data = f.spec.Pad.Pad(data, f.spec.Length) - } - - packed, err := f.spec.Enc.Encode(data) - if err != nil { - return nil, fmt.Errorf("failed to encode content: %w", err) - } - - packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(data)) - if err != nil { - return nil, fmt.Errorf("failed to encode length: %w", err) - } + packer := f.spec.getPacker() - return append(packedLength, packed...), nil + return packer.Pack(data, f.spec) } // returns number of bytes was read func (f *Numeric) Unpack(data []byte) (int, error) { - dataLen, prefBytes, err := f.spec.Pref.DecodeLength(f.spec.Length, data) - if err != nil { - return 0, fmt.Errorf("failed to decode length: %w", err) - } + unpacker := f.spec.getUnpacker() - raw, read, err := f.spec.Enc.Decode(data[prefBytes:], dataLen) + raw, bytesRead, err := unpacker.Unpack(data, f.spec) if err != nil { - return 0, fmt.Errorf("failed to decode content: %w", err) - } - - if f.spec.Pad != nil { - raw = f.spec.Pad.Unpad(raw) + return 0, err } if err := f.SetBytes(raw); err != nil { return 0, fmt.Errorf("failed to set bytes: %w", err) } - return read + prefBytes, nil + return bytesRead, nil } // Deprecated. Use Marshal instead diff --git a/field/packer_unpacker.go b/field/packer_unpacker.go new file mode 100644 index 00000000..9c39acd4 --- /dev/null +++ b/field/packer_unpacker.go @@ -0,0 +1,43 @@ +package field + +import "fmt" + +type DefaultPacker struct{} + +func (p DefaultPacker) Pack(data []byte, spec *Spec) ([]byte, error) { + if spec.Pad != nil { + data = spec.Pad.Pad(data, spec.Length) + } + + packed, err := spec.Enc.Encode(data) + if err != nil { + return nil, fmt.Errorf("failed to encode content: %w", err) + } + + packedLength, err := spec.Pref.EncodeLength(spec.Length, len(data)) + if err != nil { + return nil, fmt.Errorf("failed to encode length: %w", err) + } + + return append(packedLength, packed...), nil +} + +type DefaultUnpacker struct{} + +func (u DefaultUnpacker) Unpack(data []byte, spec *Spec) ([]byte, int, error) { + dataLen, prefBytes, err := spec.Pref.DecodeLength(spec.Length, data) + if err != nil { + return nil, 0, fmt.Errorf("failed to decode length: %w", err) + } + + raw, read, err := spec.Enc.Decode(data[prefBytes:], dataLen) + if err != nil { + return nil, 0, fmt.Errorf("failed to decode content: %w", err) + } + + if spec.Pad != nil { + raw = spec.Pad.Unpad(raw) + } + + return raw, read + prefBytes, nil +} diff --git a/field/spec.go b/field/spec.go index 6fd14d5c..77956204 100644 --- a/field/spec.go +++ b/field/spec.go @@ -80,6 +80,22 @@ type Spec struct { // Bitmap defines a bitmap field that is used only by a composite field type. // It defines the way that the composite will determine its subflieds existence. Bitmap *Bitmap + // Packer is the packer used to pack the field. Default is DefaultPacker. + Packer Packer + // Unpacker is the unpacker used to unpack the field. Default is DefaultUnpacker. + Unpacker Unpacker +} + +// Packer is the interface that wraps the Pack method. +type Packer interface { + Pack(data []byte, spec *Spec) ([]byte, error) +} + +// Unpacker is the interface that wraps the Unpack method. +type Unpacker interface { + // Unpack unpacks the data according to the spec and returns the + // unpacked data and the number of bytes read. + Unpack(data []byte, spec *Spec) ([]byte, int, error) } func NewSpec(length int, desc string, enc encoding.Encoder, pref prefix.Prefixer) *Spec { @@ -91,6 +107,20 @@ func NewSpec(length int, desc string, enc encoding.Encoder, pref prefix.Prefixer } } +func (spec *Spec) getPacker() Packer { + if spec.Packer == nil { + return DefaultPacker{} + } + return spec.Packer +} + +func (spec *Spec) getUnpacker() Unpacker { + if spec.Unpacker == nil { + return DefaultUnpacker{} + } + return spec.Unpacker +} + // Validate validates the spec. func (s *Spec) Validate() error { if s.Enc != nil { diff --git a/field/string.go b/field/string.go index c0ed4fb7..5d977a5d 100644 --- a/field/string.go +++ b/field/string.go @@ -10,9 +10,11 @@ import ( "github.com/moov-io/iso8583/utils" ) -var _ Field = (*String)(nil) -var _ json.Marshaler = (*String)(nil) -var _ json.Unmarshaler = (*String)(nil) +var ( + _ Field = (*String)(nil) + _ json.Marshaler = (*String)(nil) + _ json.Unmarshaler = (*String)(nil) +) type String struct { value string @@ -72,43 +74,24 @@ func (f *String) SetValue(v string) { func (f *String) Pack() ([]byte, error) { data := []byte(f.value) - if f.spec.Pad != nil { - data = f.spec.Pad.Pad(data, f.spec.Length) - } - - packed, err := f.spec.Enc.Encode(data) - if err != nil { - return nil, fmt.Errorf("failed to encode content: %w", err) - } - - packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(data)) - if err != nil { - return nil, fmt.Errorf("failed to encode length: %w", err) - } + packer := f.spec.getPacker() - return append(packedLength, packed...), nil + return packer.Pack(data, f.spec) } func (f *String) Unpack(data []byte) (int, error) { - dataLen, prefBytes, err := f.spec.Pref.DecodeLength(f.spec.Length, data) - if err != nil { - return 0, fmt.Errorf("failed to decode length: %w", err) - } + unpacker := f.spec.getUnpacker() - raw, read, err := f.spec.Enc.Decode(data[prefBytes:], dataLen) + raw, bytesRead, err := unpacker.Unpack(data, f.spec) if err != nil { - return 0, fmt.Errorf("failed to decode content: %w", err) - } - - if f.spec.Pad != nil { - raw = f.spec.Pad.Unpad(raw) + return 0, err } if err := f.SetBytes(raw); err != nil { return 0, fmt.Errorf("failed to set bytes: %w", err) } - return read + prefBytes, nil + return bytesRead, nil } // Deprecated. Use Marshal instead diff --git a/field/track1.go b/field/track1.go index afc421d9..52280780 100644 --- a/field/track1.go +++ b/field/track1.go @@ -28,9 +28,7 @@ const ( track1Format = `%s%s^%s^%s%s%s` ) -var ( - track1Regex = regexp.MustCompile(`^([A-Z]{1})([0-9]{1,19})\^([^\^]{2,26})\^([0-9]{4}|\^)([0-9]{3}|\^)([^\?]+)$`) -) +var track1Regex = regexp.MustCompile(`^([A-Z]{1})([0-9]{1,19})\^([^\^]{2,26})\^([0-9]{4}|\^)([0-9]{3}|\^)([^\?]+)$`) func NewTrack1(spec *Spec) *Track1 { return &Track1{ @@ -68,37 +66,18 @@ func (f *Track1) Pack() ([]byte, error) { return nil, err } - if f.spec.Pad != nil { - data = f.spec.Pad.Pad(data, f.spec.Length) - } - - packed, err := f.spec.Enc.Encode(data) - if err != nil { - return nil, fmt.Errorf("failed to encode content: %w", err) - } - - packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(data)) - if err != nil { - return nil, fmt.Errorf("failed to encode length: %w", err) - } + packer := f.spec.getPacker() - return append(packedLength, packed...), nil + return packer.Pack(data, f.spec) } // returns number of bytes was read func (f *Track1) Unpack(data []byte) (int, error) { - dataLen, prefBytes, err := f.spec.Pref.DecodeLength(f.spec.Length, data) - if err != nil { - return 0, fmt.Errorf("failed to decode length: %w", err) - } + unpacker := f.spec.getUnpacker() - raw, read, err := f.spec.Enc.Decode(data[prefBytes:], dataLen) + raw, bytesRead, err := unpacker.Unpack(data, f.spec) if err != nil { - return 0, fmt.Errorf("failed to decode content: %w", err) - } - - if f.spec.Pad != nil { - raw = f.spec.Pad.Unpad(raw) + return 0, err } if len(raw) > 0 { @@ -108,7 +87,7 @@ func (f *Track1) Unpack(data []byte) (int, error) { } } - return read + prefBytes, nil + return bytesRead, nil } // Deprecated. Use Marshal instead diff --git a/field/track2.go b/field/track2.go index ec4aea9f..973bb924 100644 --- a/field/track2.go +++ b/field/track2.go @@ -27,9 +27,7 @@ const ( defaultSeparator = "=" ) -var ( - track2Regex = regexp.MustCompile(`^([0-9]{1,19})(=|D)([0-9]{4})([0-9]{3})([^?]+)$`) -) +var track2Regex = regexp.MustCompile(`^([0-9]{1,19})(=|D)([0-9]{4})([0-9]{3})([^?]+)$`) func NewTrack2(spec *Spec) *Track2 { return &Track2{ @@ -67,37 +65,18 @@ func (f *Track2) Pack() ([]byte, error) { return nil, err } - if f.spec.Pad != nil { - data = f.spec.Pad.Pad(data, f.spec.Length) - } + packer := f.spec.getPacker() - packed, err := f.spec.Enc.Encode(data) - if err != nil { - return nil, fmt.Errorf("failed to encode content: %w", err) - } - - packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(data)) - if err != nil { - return nil, fmt.Errorf("failed to encode length: %w", err) - } - - return append(packedLength, packed...), nil + return packer.Pack(data, f.spec) } // returns number of bytes was read func (f *Track2) Unpack(data []byte) (int, error) { - dataLen, prefBytes, err := f.spec.Pref.DecodeLength(f.spec.Length, data) - if err != nil { - return 0, fmt.Errorf("failed to decode length: %w", err) - } + unpacker := f.spec.getUnpacker() - raw, read, err := f.spec.Enc.Decode(data[prefBytes:], dataLen) + raw, bytesRead, err := unpacker.Unpack(data, f.spec) if err != nil { - return 0, fmt.Errorf("failed to decode content: %w", err) - } - - if f.spec.Pad != nil { - raw = f.spec.Pad.Unpad(raw) + return 0, err } if len(raw) > 0 { @@ -106,7 +85,8 @@ func (f *Track2) Unpack(data []byte) (int, error) { return 0, err } } - return read + prefBytes, nil + + return bytesRead, nil } // Deprecated. Use Marshal instead diff --git a/field/track3.go b/field/track3.go index 97390504..c4e889c3 100644 --- a/field/track3.go +++ b/field/track3.go @@ -22,9 +22,7 @@ const ( track3Format = `%s%s=%s` ) -var ( - track3Regex = regexp.MustCompile(`^([0-9]{2})([0-9]{1,19})\=([^\?]+)$`) -) +var track3Regex = regexp.MustCompile(`^([0-9]{2})([0-9]{1,19})\=([^\?]+)$`) func NewTrack3(spec *Spec) *Track3 { return &Track3{ @@ -65,37 +63,18 @@ func (f *Track3) Pack() ([]byte, error) { return nil, err } - if f.spec.Pad != nil { - data = f.spec.Pad.Pad(data, f.spec.Length) - } - - packed, err := f.spec.Enc.Encode(data) - if err != nil { - return nil, fmt.Errorf("failed to encode content: %w", err) - } - - packedLength, err := f.spec.Pref.EncodeLength(f.spec.Length, len(data)) - if err != nil { - return nil, fmt.Errorf("failed to encode length: %w", err) - } + packer := f.spec.getPacker() - return append(packedLength, packed...), nil + return packer.Pack(data, f.spec) } // returns number of bytes was read func (f *Track3) Unpack(data []byte) (int, error) { - dataLen, prefBytes, err := f.spec.Pref.DecodeLength(f.spec.Length, data) - if err != nil { - return 0, fmt.Errorf("failed to decode length: %w", err) - } + unpacker := f.spec.getUnpacker() - raw, read, err := f.spec.Enc.Decode(data[prefBytes:], dataLen) + raw, bytesRead, err := unpacker.Unpack(data, f.spec) if err != nil { - return 0, fmt.Errorf("failed to decode content: %w", err) - } - - if f.spec.Pad != nil { - raw = f.spec.Pad.Unpad(raw) + return 0, err } if len(raw) > 0 { @@ -105,7 +84,7 @@ func (f *Track3) Unpack(data []byte) (int, error) { } } - return read + prefBytes, nil + return bytesRead, nil } // Deprecated. Use Marshal instead