diff --git a/field.go b/field.go index c6413e3f3..9df747e10 100644 --- a/field.go +++ b/field.go @@ -29,3 +29,22 @@ type Field interface { FieldWriter FieldValueReader } + +//FieldGroupWriter is an interface for writing a FieldGroup +type FieldGroupWriter interface { + Tag() Tag + Write() TagValues +} + +//FieldGroupReader is an interface for reading a FieldGroup +type FieldGroupReader interface { + Tag() Tag + Read(TagValues) (TagValues, error) +} + +//FieldGroup is the interface implemented by all typed Groups in a Message +type FieldGroup interface { + Tag() Tag + Write() TagValues + Read(TagValues) (TagValues, error) +} diff --git a/field_map.go b/field_map.go index af3244a0e..2b2f285e8 100644 --- a/field_map.go +++ b/field_map.go @@ -8,7 +8,7 @@ import ( //FieldMap is a collection of fix fields that make up a fix message. type FieldMap struct { - tagLookup map[Tag][]tagValue + tagLookup map[Tag]TagValues tagOrder } @@ -68,7 +68,7 @@ func trailerFieldOrder(i, j Tag) bool { } func (m *FieldMap) init(ordering tagOrder) { - m.tagLookup = make(map[Tag][]tagValue) + m.tagLookup = make(map[Tag]TagValues) m.tagOrder = ordering } @@ -100,7 +100,7 @@ func (m FieldMap) GetField(tag Tag, parser FieldValueReader) MessageRejectError return ConditionallyRequiredFieldMissing(tag) } - if err := parser.Read(tagValues[0].Value); err != nil { + if err := parser.Read(tagValues[0].value); err != nil { return IncorrectDataFormatForValue(tag) } @@ -126,17 +126,17 @@ func (m FieldMap) GetString(tag Tag) (string, MessageRejectError) { } //GetGroup is a Get function specific to Group Fields. -func (m FieldMap) GetGroup(parser *RepeatingGroup) MessageRejectError { - tagValues, ok := m.tagLookup[parser.Tag] +func (m FieldMap) GetGroup(parser FieldGroupReader) MessageRejectError { + tagValues, ok := m.tagLookup[parser.Tag()] if !ok { - return ConditionallyRequiredFieldMissing(parser.Tag) + return ConditionallyRequiredFieldMissing(parser.Tag()) } - if _, err := parser.read(tagValues); err != nil { + if _, err := parser.Read(tagValues); err != nil { if msgRejErr, ok := err.(MessageRejectError); ok { return msgRejErr } - return IncorrectDataFormatForValue(parser.Tag) + return IncorrectDataFormatForValue(parser.Tag()) } return nil @@ -144,7 +144,7 @@ func (m FieldMap) GetGroup(parser *RepeatingGroup) MessageRejectError { //SetField sets the field with Tag tag func (m FieldMap) SetField(tag Tag, field FieldValueWriter) FieldMap { - tValues := make([]tagValue, 1) + tValues := make(TagValues, 1) tValues[0].init(tag, field.Write()) m.tagLookup[tag] = tValues return m @@ -152,20 +152,20 @@ func (m FieldMap) SetField(tag Tag, field FieldValueWriter) FieldMap { //Clear purges all fields from field map func (m *FieldMap) Clear() { - m.tagLookup = make(map[Tag][]tagValue) + m.tagLookup = make(map[Tag]TagValues) } //Set is a setter for fields func (m FieldMap) Set(field FieldWriter) FieldMap { - tValues := make([]tagValue, 1) + tValues := make(TagValues, 1) tValues[0].init(field.Tag(), field.Write()) m.tagLookup[field.Tag()] = tValues return m } //SetGroup is a setter specific to group fields -func (m FieldMap) SetGroup(field RepeatingGroup) FieldMap { - m.tagLookup[field.Tag] = field.tagValues() +func (m FieldMap) SetGroup(field FieldGroupWriter) FieldMap { + m.tagLookup[field.Tag()] = field.Write() return m } @@ -195,7 +195,7 @@ func (m FieldMap) total() int { total := 0 for _, fields := range m.tagLookup { for _, tv := range fields { - switch tv.Tag { + switch tv.tag { case tagCheckSum: //tag does not contribute to total default: total += tv.total() @@ -210,7 +210,7 @@ func (m FieldMap) length() int { length := 0 for _, fields := range m.tagLookup { for _, tv := range fields { - switch tv.Tag { + switch tv.tag { case tagBeginString, tagBodyLength, tagCheckSum: //tags do not contribute to length default: length += tv.length() diff --git a/marshal.go b/marshal.go index d51af0865..582896939 100644 --- a/marshal.go +++ b/marshal.go @@ -64,7 +64,7 @@ func (e encoder) encodeValue(fixTag Tag, v reflect.Value, omitEmpty bool, defaul panic("repeating group must be a slice of type struct") } - repeatingGroup := RepeatingGroup{Tag: fixTag, GroupTemplate: buildGroupTemplate(elem)} + repeatingGroup := NewRepeatingGroup(fixTag, buildGroupTemplate(elem)) for i := 0; i < v.Len(); i++ { group := repeatingGroup.Add() diff --git a/marshal_test.go b/marshal_test.go index 2f22001d2..38384b6b6 100644 --- a/marshal_test.go +++ b/marshal_test.go @@ -243,14 +243,14 @@ func TestMarshal_RepeatingGroups(t *testing.T) { continue } - groupField := quickfix.RepeatingGroup{Tag: test.groupTag, GroupTemplate: test.template} - if err := fixMsg.Body.GetGroup(&groupField); err != nil { + groupField := quickfix.NewRepeatingGroup(test.groupTag, test.template) + if err := fixMsg.Body.GetGroup(groupField); err != nil { t.Error("Unexpected error", err) continue } - if len(groupField.Groups) != test.expectedGroupSize { - t.Errorf("Expected group %v to have size %v, got %v", test.groupTag, test.expectedGroupSize, len(groupField.Groups)) + if groupField.Len() != test.expectedGroupSize { + t.Errorf("Expected group %v to have size %v, got %v", test.groupTag, test.expectedGroupSize, groupField.Len()) } } } diff --git a/message.go b/message.go index 7af24d0c4..40a37cdf3 100644 --- a/message.go +++ b/message.go @@ -23,7 +23,7 @@ type Message struct { bodyBytes []byte //field bytes as they appear in the raw message - fields []tagValue + fields TagValues } //Marshal marshals the message itself @@ -61,7 +61,7 @@ func ParseMessage(rawMessage []byte) (Message, error) { fieldCount++ } } - msg.fields = make([]tagValue, fieldCount) + msg.fields = make(TagValues, fieldCount) fieldIndex := 0 var err error @@ -71,7 +71,7 @@ func ParseMessage(rawMessage []byte) (Message, error) { return msg, err } - msg.Header.tagLookup[msg.fields[fieldIndex].Tag] = msg.fields[fieldIndex : fieldIndex+1] + msg.Header.tagLookup[msg.fields[fieldIndex].tag] = msg.fields[fieldIndex : fieldIndex+1] fieldIndex++ parsedFieldBytes := &msg.fields[fieldIndex] @@ -79,7 +79,7 @@ func ParseMessage(rawMessage []byte) (Message, error) { return msg, err } - msg.Header.tagLookup[parsedFieldBytes.Tag] = msg.fields[fieldIndex : fieldIndex+1] + msg.Header.tagLookup[parsedFieldBytes.tag] = msg.fields[fieldIndex : fieldIndex+1] fieldIndex++ parsedFieldBytes = &msg.fields[fieldIndex] @@ -87,7 +87,7 @@ func ParseMessage(rawMessage []byte) (Message, error) { return msg, err } - msg.Header.tagLookup[parsedFieldBytes.Tag] = msg.fields[fieldIndex : fieldIndex+1] + msg.Header.tagLookup[parsedFieldBytes.tag] = msg.fields[fieldIndex : fieldIndex+1] fieldIndex++ trailerBytes := []byte{} @@ -100,16 +100,16 @@ func ParseMessage(rawMessage []byte) (Message, error) { } switch { - case parsedFieldBytes.Tag.IsHeader(): - msg.Header.tagLookup[parsedFieldBytes.Tag] = msg.fields[fieldIndex : fieldIndex+1] - case parsedFieldBytes.Tag.IsTrailer(): - msg.Trailer.tagLookup[parsedFieldBytes.Tag] = msg.fields[fieldIndex : fieldIndex+1] + case parsedFieldBytes.tag.IsHeader(): + msg.Header.tagLookup[parsedFieldBytes.tag] = msg.fields[fieldIndex : fieldIndex+1] + case parsedFieldBytes.tag.IsTrailer(): + msg.Trailer.tagLookup[parsedFieldBytes.tag] = msg.fields[fieldIndex : fieldIndex+1] default: foundBody = true trailerBytes = rawMessage - msg.Body.tagLookup[parsedFieldBytes.Tag] = msg.fields[fieldIndex : fieldIndex+1] + msg.Body.tagLookup[parsedFieldBytes.tag] = msg.fields[fieldIndex : fieldIndex+1] } - if parsedFieldBytes.Tag == tagCheckSum { + if parsedFieldBytes.tag == tagCheckSum { break } @@ -127,7 +127,7 @@ func ParseMessage(rawMessage []byte) (Message, error) { length := 0 for _, field := range msg.fields { - switch field.Tag { + switch field.tag { case tagBeginString, tagBodyLength, tagCheckSum: //tags do not contribute to length default: length += field.length() @@ -135,7 +135,10 @@ func ParseMessage(rawMessage []byte) (Message, error) { } var bodyLength FIXInt - msg.Header.GetField(tagBodyLength, &bodyLength) + if err := msg.Header.GetField(tagBodyLength, &bodyLength); err != nil { + return msg, parseError{OrigError: err.Error()} + } + if length != int(bodyLength) { return msg, parseError{OrigError: fmt.Sprintf("Incorrect Message Length, expected %d, got %d", bodyLength, length)} } @@ -181,20 +184,20 @@ func (m Message) reverseRoute() Message { return reverseMsg } -func extractSpecificField(field *tagValue, expectedTag Tag, buffer []byte) (remBuffer []byte, err error) { +func extractSpecificField(field *TagValue, expectedTag Tag, buffer []byte) (remBuffer []byte, err error) { remBuffer, err = extractField(field, buffer) switch { case err != nil: return - case field.Tag != expectedTag: - err = parseError{OrigError: fmt.Sprintf("extractSpecificField: Fields out of order, expected %d, got %d", expectedTag, field.Tag)} + case field.tag != expectedTag: + err = parseError{OrigError: fmt.Sprintf("extractSpecificField: Fields out of order, expected %d, got %d", expectedTag, field.tag)} return } return } -func extractField(parsedFieldBytes *tagValue, buffer []byte) (remBytes []byte, err error) { +func extractField(parsedFieldBytes *TagValue, buffer []byte) (remBytes []byte, err error) { endIndex := bytes.IndexByte(buffer, '\001') if endIndex == -1 { err = parseError{OrigError: "extractField: No Trailing Delim in " + string(buffer)} diff --git a/repeating_group.go b/repeating_group.go index ababcbbbe..79fc9402d 100644 --- a/repeating_group.go +++ b/repeating_group.go @@ -17,7 +17,7 @@ type GroupItem interface { // //The Read function returns the remaining tagValues not processed by the GroupItem. If there was a //problem reading the field, an error may be returned - read(tagValues) (tagValues, error) + Read(TagValues) (TagValues, error) } type protoGroupElement struct { @@ -25,8 +25,8 @@ type protoGroupElement struct { } func (t protoGroupElement) Tag() Tag { return t.tag } -func (t protoGroupElement) read(tv tagValues) (tagValues, error) { - if tv[0].Tag == t.tag { +func (t protoGroupElement) Read(tv TagValues) (TagValues, error) { + if tv[0].tag == t.tag { return tv[1:], nil } @@ -41,13 +41,37 @@ func GroupElement(tag Tag) GroupItem { //GroupTemplate specifies the group item order for a RepeatingGroup type GroupTemplate []GroupItem +//Group is a group of fields occuring in a repeating group type Group struct{ FieldMap } //RepeatingGroup is a FIX Repeating Group type type RepeatingGroup struct { - Tag - GroupTemplate - Groups []Group + tag Tag + template GroupTemplate + groups []Group +} + +//NewRepeatingGroup returns an initilized RepeatingGroup instance +func NewRepeatingGroup(tag Tag, template GroupTemplate) *RepeatingGroup { + return &RepeatingGroup{ + tag: tag, + template: template, + } +} + +//Tag returns the Tag for this repeating Group +func (f RepeatingGroup) Tag() Tag { + return f.tag +} + +//Len returns the number of Groups in this RepeatingGroup +func (f RepeatingGroup) Len() int { + return len(f.groups) +} + +//Get returns the ith group in this RepeatingGroup +func (f RepeatingGroup) Get(i int) Group { + return f.groups[i] } //Add appends a new group to the RepeatingGroup and returns the new Group @@ -55,17 +79,17 @@ func (f *RepeatingGroup) Add() Group { var g Group g.init(f.groupTagOrder()) - f.Groups = append(f.Groups, g) + f.groups = append(f.groups, g) return g } -//tagValues returns tagValues for all Items in the repeating group ordered by +//Write returns tagValues for all Items in the repeating group ordered by //Group sequence and Group template order -func (f RepeatingGroup) tagValues() tagValues { - tvs := make(tagValues, 1, 1) - tvs[0].init(f.Tag, []byte(strconv.Itoa(len(f.Groups)))) +func (f RepeatingGroup) Write() TagValues { + tvs := make(TagValues, 1, 1) + tvs[0].init(f.tag, []byte(strconv.Itoa(len(f.groups)))) - for _, group := range f.Groups { + for _, group := range f.groups { tags := group.sortedTags() for _, tag := range tags { @@ -79,7 +103,7 @@ func (f RepeatingGroup) tagValues() tagValues { } func (f RepeatingGroup) findItemInGroupTemplate(t Tag) (item GroupItem, ok bool) { - for _, templateField := range f.GroupTemplate { + for _, templateField := range f.template { if t == templateField.Tag() { ok = true item = templateField @@ -92,7 +116,7 @@ func (f RepeatingGroup) findItemInGroupTemplate(t Tag) (item GroupItem, ok bool) func (f RepeatingGroup) groupTagOrder() tagOrder { tagMap := make(map[Tag]int) - for i, f := range f.GroupTemplate { + for i, f := range f.template { tagMap[f.Tag()] = i } @@ -113,15 +137,15 @@ func (f RepeatingGroup) groupTagOrder() tagOrder { } func (f RepeatingGroup) delimiter() Tag { - return f.GroupTemplate[0].Tag() + return f.template[0].Tag() } func (f RepeatingGroup) isDelimiter(t Tag) bool { return t == f.delimiter() } -func (f *RepeatingGroup) read(tv tagValues) (tagValues, error) { - expectedGroupSize, err := atoi(tv[0].Value) +func (f *RepeatingGroup) Read(tv TagValues) (TagValues, error) { + expectedGroupSize, err := atoi(tv[0].value) if err != nil { return tv, err } @@ -135,13 +159,13 @@ func (f *RepeatingGroup) read(tv tagValues) (tagValues, error) { var group Group group.init(tagOrdering) for len(tv) > 0 { - field, ok := f.findItemInGroupTemplate(tv[0].Tag) + field, ok := f.findItemInGroupTemplate(tv[0].tag) if !ok { break } tvRange := tv - if tv, err = field.read(tv); err != nil { + if tv, err = field.Read(tv); err != nil { return tv, err } @@ -149,14 +173,14 @@ func (f *RepeatingGroup) read(tv tagValues) (tagValues, error) { group = Group{} group.init(tagOrdering) - f.Groups = append(f.Groups, group) + f.groups = append(f.groups, group) } - group.tagLookup[tvRange[0].Tag] = tvRange + group.tagLookup[tvRange[0].tag] = tvRange } - if len(f.Groups) != expectedGroupSize { - return tv, repeatingGroupFieldsOutOfOrder(f.Tag, fmt.Sprintf("group %v: template is wrong or delimiter %v not found: expected %v groups, but found %v", f.Tag, f.delimiter(), expectedGroupSize, len(f.Groups))) + if len(f.groups) != expectedGroupSize { + return tv, repeatingGroupFieldsOutOfOrder(f.tag, fmt.Sprintf("group %v: template is wrong or delimiter %v not found: expected %v groups, but found %v", f.tag, f.delimiter(), expectedGroupSize, len(f.groups))) } return tv, err diff --git a/repeating_group_test.go b/repeating_group_test.go index c8753c2bd..266c155a3 100644 --- a/repeating_group_test.go +++ b/repeating_group_test.go @@ -3,10 +3,12 @@ package quickfix import ( "bytes" "testing" + + "github.com/stretchr/testify/require" ) func TestRepeatingGroup_Add(t *testing.T) { - f := RepeatingGroup{GroupTemplate: GroupTemplate{GroupElement(1)}} + f := RepeatingGroup{template: GroupTemplate{GroupElement(1)}} var tests = []struct { expectedLen int @@ -17,18 +19,18 @@ func TestRepeatingGroup_Add(t *testing.T) { for _, test := range tests { g := f.Add() - if len(f.Groups) != test.expectedLen { - t.Errorf("Expected %v groups, got %v", test.expectedLen, len(f.Groups)) + if f.Len() != test.expectedLen { + t.Errorf("Expected %v groups, got %v", test.expectedLen, f.Len()) } g.SetField(Tag(1), FIXString("hello")) - if !f.Groups[test.expectedLen-1].Has(Tag(1)) { + if !f.groups[test.expectedLen-1].Has(Tag(1)) { t.Errorf("expected tag in group %v", test.expectedLen) } var v FIXString - f.Groups[test.expectedLen-1].GetField(Tag(1), &v) + require.Nil(t, f.groups[test.expectedLen-1].GetField(Tag(1), &v)) if string(v) != "hello" { t.Errorf("expected hello in group %v", test.expectedLen) @@ -36,28 +38,28 @@ func TestRepeatingGroup_Add(t *testing.T) { } } -func TestRepeatingGroup_TagValues(t *testing.T) { - f1 := RepeatingGroup{Tag: 10, GroupTemplate: GroupTemplate{ +func TestRepeatingGroup_Write(t *testing.T) { + f1 := RepeatingGroup{tag: 10, template: GroupTemplate{ GroupElement(1), GroupElement(2), }} f1.Add().SetField(Tag(1), FIXString("hello")) - f2 := RepeatingGroup{Tag: 11, GroupTemplate: GroupTemplate{ + f2 := RepeatingGroup{tag: 11, template: GroupTemplate{ GroupElement(1), GroupElement(2), }} f2.Add().SetField(Tag(1), FIXString("hello")).SetField(Tag(2), FIXString("world")) - f3 := RepeatingGroup{Tag: 12, GroupTemplate: GroupTemplate{ + f3 := RepeatingGroup{tag: 12, template: GroupTemplate{ GroupElement(1), GroupElement(2), }} f3.Add().SetField(Tag(1), FIXString("hello")) f3.Add().SetField(Tag(1), FIXString("world")) - f4 := RepeatingGroup{Tag: 13, GroupTemplate: GroupTemplate{ + f4 := RepeatingGroup{tag: 13, template: GroupTemplate{ GroupElement(1), GroupElement(2), }} @@ -76,7 +78,7 @@ func TestRepeatingGroup_TagValues(t *testing.T) { for _, test := range tests { tvbytes := []byte{} - for _, tv := range test.f.tagValues() { + for _, tv := range test.f.Write() { tvbytes = append(tvbytes, tv.bytes...) } @@ -89,28 +91,28 @@ func TestRepeatingGroup_TagValues(t *testing.T) { func TestRepeatingGroup_ReadError(t *testing.T) { singleFieldTemplate := GroupTemplate{GroupElement(1)} tests := []struct { - tv tagValues + tv TagValues expectedGroupNum int }{ { - tagValues{ - tagValue{Value: []byte("1")}, - tagValue{Tag: Tag(2), Value: []byte("not in template")}, - tagValue{Tag: Tag(1), Value: []byte("hello")}, + TagValues{ + TagValue{value: []byte("1")}, + TagValue{tag: Tag(2), value: []byte("not in template")}, + TagValue{tag: Tag(1), value: []byte("hello")}, }, 0}, { - tagValues{ - tagValue{Value: []byte("2")}, - tagValue{Tag: Tag(1), Value: []byte("hello")}, - tagValue{Tag: Tag(2), Value: []byte("not in template")}, - tagValue{Tag: Tag(1), Value: []byte("hello")}, + TagValues{ + TagValue{value: []byte("2")}, + TagValue{tag: Tag(1), value: []byte("hello")}, + TagValue{tag: Tag(2), value: []byte("not in template")}, + TagValue{tag: Tag(1), value: []byte("hello")}, }, 1}} for _, s := range tests { - f := RepeatingGroup{GroupTemplate: singleFieldTemplate} - _, err := f.read(s.tv) - if err == nil || len(f.Groups) != s.expectedGroupNum { - t.Errorf("Should have raised an error because expected group number is wrong: %v instead of %v", len(f.Groups), s.expectedGroupNum) + f := RepeatingGroup{template: singleFieldTemplate} + _, err := f.Read(s.tv) + if err == nil || len(f.groups) != s.expectedGroupNum { + t.Errorf("Should have raised an error because expected group number is wrong: %v instead of %v", len(f.groups), s.expectedGroupNum) } } } @@ -122,69 +124,69 @@ func TestRepeatingGroup_Read(t *testing.T) { tests := []struct { groupTemplate GroupTemplate - tv tagValues - expectedGroupTvs []tagValues + tv TagValues + expectedGroupTvs []TagValues }{ - {singleFieldTemplate, tagValues{tagValue{Value: []byte("0")}}, - []tagValues{}}, - {singleFieldTemplate, tagValues{tagValue{Value: []byte("1")}, tagValue{Tag: Tag(1), Value: []byte("hello")}}, - []tagValues{{tagValue{Tag: Tag(1), Value: []byte("hello")}}}}, + {singleFieldTemplate, TagValues{TagValue{value: []byte("0")}}, + []TagValues{}}, + {singleFieldTemplate, TagValues{TagValue{value: []byte("1")}, TagValue{tag: Tag(1), value: []byte("hello")}}, + []TagValues{{TagValue{tag: Tag(1), value: []byte("hello")}}}}, {singleFieldTemplate, - tagValues{tagValue{Value: []byte("1")}, - tagValue{Tag: Tag(1), Value: []byte("hello")}, - tagValue{Tag: Tag(2), Value: []byte("not in group")}}, - []tagValues{ - {tagValue{Tag: Tag(1), Value: []byte("hello")}}}}, + TagValues{TagValue{value: []byte("1")}, + TagValue{tag: Tag(1), value: []byte("hello")}, + TagValue{tag: Tag(2), value: []byte("not in group")}}, + []TagValues{ + {TagValue{tag: Tag(1), value: []byte("hello")}}}}, {singleFieldTemplate, - tagValues{tagValue{Value: []byte("2")}, - tagValue{Tag: Tag(1), Value: []byte("hello")}, - tagValue{Tag: Tag(1), Value: []byte("world")}}, - []tagValues{ - {tagValue{Tag: Tag(1), Value: []byte("hello")}}, - {tagValue{Tag: Tag(1), Value: []byte("world")}}, + TagValues{TagValue{value: []byte("2")}, + TagValue{tag: Tag(1), value: []byte("hello")}, + TagValue{tag: Tag(1), value: []byte("world")}}, + []TagValues{ + {TagValue{tag: Tag(1), value: []byte("hello")}}, + {TagValue{tag: Tag(1), value: []byte("world")}}, }}, {multiFieldTemplate, - tagValues{ - tagValue{Value: []byte("2")}, - tagValue{Tag: Tag(1), Value: []byte("hello")}, - tagValue{Tag: Tag(1), Value: []byte("goodbye")}, tagValue{Tag: Tag(2), Value: []byte("cruel")}, tagValue{Tag: Tag(3), Value: []byte("world")}, + TagValues{ + TagValue{value: []byte("2")}, + TagValue{tag: Tag(1), value: []byte("hello")}, + TagValue{tag: Tag(1), value: []byte("goodbye")}, TagValue{tag: Tag(2), value: []byte("cruel")}, TagValue{tag: Tag(3), value: []byte("world")}, }, - []tagValues{ - {tagValue{Tag: Tag(1), Value: []byte("hello")}}, - {tagValue{Tag: Tag(1), Value: []byte("goodbye")}, tagValue{Tag: Tag(2), Value: []byte("cruel")}, tagValue{Tag: Tag(3), Value: []byte("world")}}, + []TagValues{ + {TagValue{tag: Tag(1), value: []byte("hello")}}, + {TagValue{tag: Tag(1), value: []byte("goodbye")}, TagValue{tag: Tag(2), value: []byte("cruel")}, TagValue{tag: Tag(3), value: []byte("world")}}, }}, {multiFieldTemplate, - tagValues{ - tagValue{Value: []byte("3")}, - tagValue{Tag: Tag(1), Value: []byte("hello")}, - tagValue{Tag: Tag(1), Value: []byte("goodbye")}, tagValue{Tag: Tag(2), Value: []byte("cruel")}, tagValue{Tag: Tag(3), Value: []byte("world")}, - tagValue{Tag: Tag(1), Value: []byte("another")}, + TagValues{ + TagValue{value: []byte("3")}, + TagValue{tag: Tag(1), value: []byte("hello")}, + TagValue{tag: Tag(1), value: []byte("goodbye")}, TagValue{tag: Tag(2), value: []byte("cruel")}, TagValue{tag: Tag(3), value: []byte("world")}, + TagValue{tag: Tag(1), value: []byte("another")}, }, - []tagValues{ - {tagValue{Tag: Tag(1), Value: []byte("hello")}}, - {tagValue{Tag: Tag(1), Value: []byte("goodbye")}, tagValue{Tag: Tag(2), Value: []byte("cruel")}, tagValue{Tag: Tag(3), Value: []byte("world")}}, - {tagValue{Tag: Tag(1), Value: []byte("another")}}, + []TagValues{ + {TagValue{tag: Tag(1), value: []byte("hello")}}, + {TagValue{tag: Tag(1), value: []byte("goodbye")}, TagValue{tag: Tag(2), value: []byte("cruel")}, TagValue{tag: Tag(3), value: []byte("world")}}, + {TagValue{tag: Tag(1), value: []byte("another")}}, }}, } for _, test := range tests { - f := RepeatingGroup{GroupTemplate: test.groupTemplate} + f := RepeatingGroup{template: test.groupTemplate} - _, err := f.read(test.tv) + _, err := f.Read(test.tv) if err != nil { t.Error(err) } - if len(f.Groups) != len(test.expectedGroupTvs) { - t.Errorf("expected %v groups, got %v", len(test.expectedGroupTvs), len(f.Groups)) + if len(f.groups) != len(test.expectedGroupTvs) { + t.Errorf("expected %v groups, got %v", len(test.expectedGroupTvs), len(f.groups)) } - for g, group := range f.Groups { + for g, group := range f.groups { for _, expected := range test.expectedGroupTvs[g] { var actual FIXString - group.GetField(expected.Tag, &actual) + require.Nil(t, group.GetField(expected.tag, &actual)) - if !bytes.Equal(expected.Value, []byte(actual)) { - t.Errorf("%v, %v: expected %s, got %s", g, expected.Tag, expected.Value, actual) + if !bytes.Equal(expected.value, []byte(actual)) { + t.Errorf("%v, %v: expected %s, got %s", g, expected.tag, expected.value, actual) } } } @@ -209,14 +211,14 @@ func TestRepeatingGroup_ReadComplete(t *testing.T) { GroupElement(273), } - f := RepeatingGroup{Tag: 268, GroupTemplate: template} + f := RepeatingGroup{tag: 268, template: template} err = msg.Body.GetGroup(&f) if err != nil { t.Error("Unexpected error, ", err) } - if len(f.Groups) != 4 { - t.Errorf("expected %v groups, got %v", 4, len(f.Groups)) + if len(f.groups) != 4 { + t.Errorf("expected %v groups, got %v", 4, len(f.groups)) } expectedGroupTags := [][]Tag{ @@ -233,7 +235,7 @@ func TestRepeatingGroup_ReadComplete(t *testing.T) { {FIXString("B"), FIXString("60"), FIXString("20151027"), FIXString("18:41:52.698")}, } - for i, group := range f.Groups { + for i, group := range f.groups { for j, tag := range expectedGroupTags[i] { if !group.Has(tag) { diff --git a/tag_value.go b/tag_value.go index d659282fa..3234168d6 100644 --- a/tag_value.go +++ b/tag_value.go @@ -6,29 +6,29 @@ import ( "strconv" ) -//tagValue is a low-level FIX field abstraction -type tagValue struct { - Tag - Value []byte +//TagValue is a low-level FIX field abstraction +type TagValue struct { + tag Tag + value []byte bytes []byte } -//tagValues is a slice of tagValue -type tagValues []tagValue +//TagValues is a slice of TagValue +type TagValues []TagValue -func (tv *tagValue) init(tag Tag, value []byte) { +func (tv *TagValue) init(tag Tag, value []byte) { var buf bytes.Buffer buf.WriteString(strconv.Itoa(int(tag))) buf.WriteString("=") buf.Write(value) buf.WriteString("") - tv.Tag = tag + tv.tag = tag tv.bytes = buf.Bytes() - tv.Value = value + tv.value = value } -func (tv *tagValue) parse(rawFieldBytes []byte) (err error) { +func (tv *TagValue) parse(rawFieldBytes []byte) (err error) { sepIndex := bytes.IndexByte(rawFieldBytes, '=') if sepIndex == -1 { @@ -43,18 +43,18 @@ func (tv *tagValue) parse(rawFieldBytes []byte) (err error) { return } - tv.Tag = Tag(parsedTag) - tv.Value = rawFieldBytes[(sepIndex + 1):(len(rawFieldBytes) - 1)] + tv.tag = Tag(parsedTag) + tv.value = rawFieldBytes[(sepIndex + 1):(len(rawFieldBytes) - 1)] tv.bytes = rawFieldBytes return } -func (tv tagValue) String() string { +func (tv TagValue) String() string { return string(tv.bytes) } -func (tv tagValue) total() int { +func (tv TagValue) total() int { total := 0 for _, b := range []byte(tv.bytes) { @@ -64,6 +64,6 @@ func (tv tagValue) total() int { return total } -func (tv tagValue) length() int { +func (tv TagValue) length() int { return len(tv.bytes) } diff --git a/tag_value_test.go b/tag_value_test.go index ce2bac023..0f43a89d4 100644 --- a/tag_value_test.go +++ b/tag_value_test.go @@ -3,10 +3,13 @@ package quickfix import ( "bytes" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestTagValue_init(t *testing.T) { - var tv tagValue + var tv TagValue tv.init(Tag(8), []byte("blahblah")) expectedData := []byte("8=blahblah") @@ -14,75 +17,61 @@ func TestTagValue_init(t *testing.T) { t.Errorf("Expected %v, got %v", expectedData, tv.bytes) } expectedValue := []byte("blahblah") - if !bytes.Equal(tv.Value, expectedValue) { - t.Errorf("Expected %v, got %v", expectedValue, tv.Value) + if !bytes.Equal(tv.value, expectedValue) { + t.Errorf("Expected %v, got %v", expectedValue, tv.value) } } func TestTagValue_parse(t *testing.T) { stringField := "8=FIX.4.0" - tv := tagValue{} + tv := TagValue{} err := tv.parse([]byte(stringField)) if err != nil { t.Error("Unexpected error", err) } - if tv.Tag != Tag(8) { - t.Error("Unexpected tag", tv.Tag) + if tv.tag != Tag(8) { + t.Error("Unexpected tag", tv.tag) } if !bytes.Equal(tv.bytes, []byte(stringField)) { t.Errorf("Expected %v got %v", stringField, tv.bytes) } - if !bytes.Equal(tv.Value, []byte("FIX.4.0")) { - t.Error("Unxpected value", tv.Value) + if !bytes.Equal(tv.value, []byte("FIX.4.0")) { + t.Error("Unxpected value", tv.value) } } func TestTagValue_parseFail(t *testing.T) { stringField := "not_tag_equal_value" - tv := tagValue{} + var tv TagValue - err := tv.parse([]byte(stringField)) - - if err == nil { - t.Error("Expected Error") - } + assert.NotNil(t, tv.parse([]byte(stringField))) stringField = "tag_not_an_int=uhoh" - err = tv.parse([]byte(stringField)) - if err == nil { - t.Error("Expected Error") - } + assert.NotNil(t, tv.parse([]byte(stringField))) } func TestTagValue_String(t *testing.T) { stringField := "8=FIX.4.0" - tv := tagValue{} - tv.parse([]byte(stringField)) + var tv TagValue - if tv.String() != "8=FIX.4.0" { - t.Error("Unexpected string value", tv.String()) - } + require.Nil(t, tv.parse([]byte(stringField))) + assert.Equal(t, "8=FIX.4.0", tv.String()) } func TestTagValue_length(t *testing.T) { stringField := "8=FIX.4.0" - tv := tagValue{} - tv.parse([]byte(stringField)) - - if tv.length() != len(stringField) { - t.Error("Unexpected length", tv.length()) - } + var tv TagValue + require.Nil(t, tv.parse([]byte(stringField))) + assert.Equal(t, len(stringField), tv.length()) } func TestTagValue_total(t *testing.T) { stringField := "1=hello" - tv := tagValue{} - tv.parse([]byte(stringField)) - if tv.total() != 643 { - t.Error("Total is the summation of the ascii byte values of the field string, got ", tv.total()) - } + var tv TagValue + require.Nil(t, tv.parse([]byte(stringField))) + assert.Equal(t, 643, tv.total(), "Total is the summation of the ascii byte values of the field string") } diff --git a/unmarshal.go b/unmarshal.go index 9d0352ab8..92093f820 100644 --- a/unmarshal.go +++ b/unmarshal.go @@ -54,15 +54,15 @@ func (d decoder) decodeValue(fixTag Tag, t reflect.Type, v reflect.Value) Messag panic("repeating group must be a slice of type struct") } - repeatingGroup := RepeatingGroup{Tag: fixTag, GroupTemplate: buildGroupTemplate(elem)} - if err := d.FieldMap.GetGroup(&repeatingGroup); err != nil { + repeatingGroup := NewRepeatingGroup(fixTag, buildGroupTemplate(elem)) + if err := d.FieldMap.GetGroup(repeatingGroup); err != nil { return err } - v.Set(reflect.MakeSlice(v.Type(), len(repeatingGroup.Groups), len(repeatingGroup.Groups))) + v.Set(reflect.MakeSlice(v.Type(), repeatingGroup.Len(), repeatingGroup.Len())) - for i := 0; i < len(repeatingGroup.Groups); i++ { - groupDecoder := decoder{FieldMap: repeatingGroup.Groups[i].FieldMap} + for i := 0; i < repeatingGroup.Len(); i++ { + groupDecoder := decoder{FieldMap: repeatingGroup.Get(i).FieldMap} for j := 0; j < v.Type().Elem().NumField(); j++ { sf := v.Type().Elem().Field(j) diff --git a/unmarshal_test.go b/unmarshal_test.go index e7b7037fa..e1614a4e5 100644 --- a/unmarshal_test.go +++ b/unmarshal_test.go @@ -184,13 +184,13 @@ func TestUnmarshal_RepeatingGroups(t *testing.T) { quickfix.GroupElement(quickfix.Tag(3)), } - group1 := quickfix.RepeatingGroup{Tag: quickfix.Tag(100), GroupTemplate: group1Template} + group1 := quickfix.NewRepeatingGroup(quickfix.Tag(100), group1Template) group1.Add().SetField(quickfix.Tag(8), quickfix.FIXString("hello")).SetField(quickfix.Tag(9), quickfix.FIXString("world")) group1.Add().SetField(quickfix.Tag(8), quickfix.FIXString("goodbye")) group1.Add().SetField(quickfix.Tag(8), quickfix.FIXString("OHHAI")).SetField(quickfix.Tag(9), quickfix.FIXString("world")) fixMsg.Body.SetGroup(group1) - group2 := quickfix.RepeatingGroup{Tag: quickfix.Tag(101), GroupTemplate: group2Template} + group2 := quickfix.NewRepeatingGroup(quickfix.Tag(101), group2Template) group2.Add().SetField(quickfix.Tag(1), quickfix.FIXInt(1)).SetField(quickfix.Tag(2), quickfix.FIXInt(2)) fixMsg.Body.SetGroup(group2) diff --git a/validation.go b/validation.go index b65cb7932..6a9029021 100644 --- a/validation.go +++ b/validation.go @@ -129,7 +129,7 @@ func validateWalk(transportDD *datadictionary.DataDictionary, appDD *datadiction for len(remainingFields) > 0 { field := remainingFields[0] - tag := field.Tag + tag := field.tag switch { case tag.IsHeader(): @@ -155,13 +155,13 @@ func validateWalk(transportDD *datadictionary.DataDictionary, appDD *datadiction } if len(remainingFields) != 0 { - return TagNotDefinedForThisMessageType(remainingFields[0].Tag) + return TagNotDefinedForThisMessageType(remainingFields[0].tag) } return nil } -func validateVisitField(fieldDef *datadictionary.FieldDef, fields []tagValue) ([]tagValue, MessageRejectError) { +func validateVisitField(fieldDef *datadictionary.FieldDef, fields []TagValue) ([]TagValue, MessageRejectError) { if fieldDef.IsGroup() { var err MessageRejectError if fields, err = validateVisitGroupField(fieldDef, fields); err != nil { @@ -173,11 +173,11 @@ func validateVisitField(fieldDef *datadictionary.FieldDef, fields []tagValue) ([ return fields[1:], nil } -func validateVisitGroupField(fieldDef *datadictionary.FieldDef, fieldStack []tagValue) ([]tagValue, MessageRejectError) { - numInGroupTag := fieldStack[0].Tag +func validateVisitGroupField(fieldDef *datadictionary.FieldDef, fieldStack []TagValue) ([]TagValue, MessageRejectError) { + numInGroupTag := fieldStack[0].tag var numInGroup FIXInt - if err := numInGroup.Read(fieldStack[0].Value); err != nil { + if err := numInGroup.Read(fieldStack[0].value); err != nil { return nil, IncorrectDataFormatForValue(numInGroupTag) } @@ -189,7 +189,7 @@ func validateVisitGroupField(fieldDef *datadictionary.FieldDef, fieldStack []tag for len(fieldStack) > 0 { //start of repeating group - if int(fieldStack[0].Tag) == fieldDef.ChildFields[0].Tag() { + if int(fieldStack[0].tag) == fieldDef.ChildFields[0].Tag() { childDefs = fieldDef.ChildFields groupCount++ } @@ -199,7 +199,7 @@ func validateVisitGroupField(fieldDef *datadictionary.FieldDef, fieldStack []tag break } - if int(fieldStack[0].Tag) == childDefs[0].Tag() { + if int(fieldStack[0].tag) == childDefs[0].Tag() { var err MessageRejectError if fieldStack, err = validateVisitField(childDefs[0], fieldStack); err != nil { return fieldStack, err @@ -224,7 +224,7 @@ func validateOrder(msg Message) MessageRejectError { inHeader := true inTrailer := false for _, field := range msg.fields { - t := field.Tag + t := field.tag switch { case inHeader && t.IsHeader(): case inHeader && !t.IsHeader(): @@ -271,11 +271,11 @@ func validateRequiredFieldMap(msg Message, requiredTags map[int]struct{}, fieldM func validateFields(transportDD *datadictionary.DataDictionary, appDD *datadictionary.DataDictionary, msgType string, message Message) MessageRejectError { for _, field := range message.fields { switch { - case field.Tag.IsHeader(): + case field.tag.IsHeader(): if err := validateField(transportDD, transportDD.Header.Tags, field); err != nil { return err } - case field.Tag.IsTrailer(): + case field.tag.IsTrailer(): if err := validateField(transportDD, transportDD.Trailer.Tags, field); err != nil { return err } @@ -289,23 +289,23 @@ func validateFields(transportDD *datadictionary.DataDictionary, appDD *datadicti return nil } -func validateField(d *datadictionary.DataDictionary, validFields datadictionary.TagSet, field tagValue) MessageRejectError { - if len(field.Value) == 0 { - return TagSpecifiedWithoutAValue(field.Tag) +func validateField(d *datadictionary.DataDictionary, validFields datadictionary.TagSet, field TagValue) MessageRejectError { + if len(field.value) == 0 { + return TagSpecifiedWithoutAValue(field.tag) } - if _, valid := d.FieldTypeByTag[int(field.Tag)]; !valid { - return InvalidTagNumber(field.Tag) + if _, valid := d.FieldTypeByTag[int(field.tag)]; !valid { + return InvalidTagNumber(field.tag) } - allowedValues := d.FieldTypeByTag[int(field.Tag)].Enums + allowedValues := d.FieldTypeByTag[int(field.tag)].Enums if len(allowedValues) != 0 { - if _, validValue := allowedValues[string(field.Value)]; !validValue { - return ValueIsIncorrect(field.Tag) + if _, validValue := allowedValues[string(field.value)]; !validValue { + return ValueIsIncorrect(field.tag) } } - fieldType := d.FieldTypeByTag[int(field.Tag)] + fieldType := d.FieldTypeByTag[int(field.tag)] var prototype FieldValue switch fieldType.Type { case "MULTIPLESTRINGVALUE", "MULTIPLEVALUESTRING": @@ -373,8 +373,8 @@ func validateField(d *datadictionary.DataDictionary, validFields datadictionary. } - if err := prototype.Read(field.Value); err != nil { - return IncorrectDataFormatForValue(field.Tag) + if err := prototype.Read(field.value); err != nil { + return IncorrectDataFormatForValue(field.tag) } return nil diff --git a/validation_test.go b/validation_test.go index b3e94eb05..45f921256 100644 --- a/validation_test.go +++ b/validation_test.go @@ -453,26 +453,26 @@ func TestValidateVisitField(t *testing.T) { groupFieldType := datadictionary.NewFieldType("mygroupfield", 1, "INT") groupFieldDef := &datadictionary.FieldDef{FieldType: groupFieldType, ChildFields: []*datadictionary.FieldDef{fieldDef1, fieldDef2}} - var field tagValue + var field TagValue field.init(Tag(11), []byte("value")) - var repField1 tagValue - var repField2 tagValue + var repField1 TagValue + var repField2 TagValue repField1.init(Tag(2), []byte("a")) repField2.init(Tag(3), []byte("a")) - var groupID tagValue + var groupID TagValue groupID.init(Tag(1), []byte("1")) - var groupID2 tagValue + var groupID2 TagValue groupID2.init(Tag(1), []byte("2")) - var groupID3 tagValue + var groupID3 TagValue groupID3.init(Tag(1), []byte("3")) var tests = []struct { fieldDef *datadictionary.FieldDef - fields tagValues + fields TagValues expectedRemFields int expectReject bool expectedRejectReason int @@ -480,38 +480,38 @@ func TestValidateVisitField(t *testing.T) { //non-repeating {expectedRemFields: 0, fieldDef: fieldDef0, - fields: tagValues{field}}, + fields: TagValues{field}}, //single field group {expectedRemFields: 0, fieldDef: groupFieldDef, - fields: tagValues{groupID, repField1}}, + fields: TagValues{groupID, repField1}}, //multiple field group {expectedRemFields: 0, fieldDef: groupFieldDef, - fields: tagValues{groupID, repField1, repField2}}, + fields: TagValues{groupID, repField1, repField2}}, //test with trailing tag not in group {expectedRemFields: 1, fieldDef: groupFieldDef, - fields: tagValues{groupID, repField1, repField2, field}}, + fields: TagValues{groupID, repField1, repField2, field}}, //repeats {expectedRemFields: 1, fieldDef: groupFieldDef, - fields: tagValues{groupID2, repField1, repField2, repField1, repField2, field}}, + fields: TagValues{groupID2, repField1, repField2, repField1, repField2, field}}, //REJECT: group size declared > actual group size {expectReject: true, fieldDef: groupFieldDef, - fields: tagValues{groupID3, repField1, repField2, repField1, repField2, field}, + fields: TagValues{groupID3, repField1, repField2, repField1, repField2, field}, expectedRejectReason: rejectReasonIncorrectNumInGroupCountForRepeatingGroup, }, {expectReject: true, fieldDef: groupFieldDef, - fields: tagValues{groupID3, repField1, repField1, field}, + fields: TagValues{groupID3, repField1, repField1, field}, expectedRejectReason: rejectReasonIncorrectNumInGroupCountForRepeatingGroup, }, //REJECT: group size declared < actual group size {expectReject: true, fieldDef: groupFieldDef, - fields: tagValues{groupID, repField1, repField2, repField1, repField2, field}, + fields: TagValues{groupID, repField1, repField2, repField1, repField2, field}, expectedRejectReason: rejectReasonIncorrectNumInGroupCountForRepeatingGroup, }, }