From 4e554c9a99ebc35f4e6d932f47e369cbdea84fe5 Mon Sep 17 00:00:00 2001 From: Cuong Manh Le Date: Mon, 9 Sep 2019 21:47:07 +0700 Subject: [PATCH] lib/tagset: add tag set as bitmask Add tag set as a bitmask, as part of refactoring lib.TagSet. Update #755 silent linter fix @imiric's review comments --- lib/tagset/tagset.go | 94 +++++++++++++++++++++++++++++++++++++++ lib/tagset/tagset_gen.go | 75 +++++++++++++++++++++++++++++++ lib/tagset/tagset_test.go | 64 ++++++++++++++++++++++++++ 3 files changed, 233 insertions(+) create mode 100644 lib/tagset/tagset.go create mode 100644 lib/tagset/tagset_gen.go create mode 100644 lib/tagset/tagset_test.go diff --git a/lib/tagset/tagset.go b/lib/tagset/tagset.go new file mode 100644 index 00000000000..5f51b706e95 --- /dev/null +++ b/lib/tagset/tagset.go @@ -0,0 +1,94 @@ +package tagset + +import ( + "bytes" + "encoding/json" + "strings" +) + +// TagSet is a bitmask that is used to keep track +// which system tags should be included with which metrics. +//go:generate enumer -type=TagSet -transform=snake -output tagset_gen.go +type TagSet uint32 + +//nolint: golint +const ( + // Default system tags includes all of the system tags emitted with metrics by default. + Proto TagSet = 1 << iota + SubProto + Status + Method + URL + Name + Group + Check + Error + ErrorCode + TLSVersion + + // System tags not enabled by default. + Iter + VU + OCSPStatus + IP +) + +// Add adds a tag to tag set. +func (ts *TagSet) Add(tag TagSet) { + *ts |= tag +} + +// Has checks a tag included in tag set. +func (ts *TagSet) Has(tag TagSet) bool { + return *ts&tag != 0 +} + +// FromList converts list of tags to TagSet +func FromList(tags []string) *TagSet { + ts := TagSet(0) + for _, tag := range tags { + if v, err := TagSetString(tag); err == nil { + ts.Add(v) + } + } + return &ts +} + +// MarshalJSON converts the TagSet to a list (JS array). +func (ts *TagSet) MarshalJSON() ([]byte, error) { + var tags []string + for _, tag := range TagSetValues() { + if ts.Has(tag) { + tags = append(tags, tag.String()) + } + } + return json.Marshal(tags) +} + +// UnmarshalJSON converts the tag list back to expected tag set. +func (ts *TagSet) UnmarshalJSON(data []byte) error { + var tags []string + if err := json.Unmarshal(data, &tags); err != nil { + return err + } + if len(tags) != 0 { + *ts = *FromList(tags) + } + return nil +} + +// UnmarshalText converts the tag list to TagSet. +func (ts *TagSet) UnmarshalText(data []byte) error { + var list = bytes.Split(data, []byte(",")) + + for _, key := range list { + key := strings.TrimSpace(string(key)) + if key == "" { + continue + } + if v, err := TagSetString(key); err == nil { + ts.Add(v) + } + } + return nil +} diff --git a/lib/tagset/tagset_gen.go b/lib/tagset/tagset_gen.go new file mode 100644 index 00000000000..40cbc0ab863 --- /dev/null +++ b/lib/tagset/tagset_gen.go @@ -0,0 +1,75 @@ +// Code generated by "enumer -type=TagSet -transform=snake -output tagset_gen.go"; DO NOT EDIT. + +// +package tagset + +import ( + "fmt" +) + +const _TagSetName = "protosub_protostatusmethodurlnamegroupcheckerrorerror_codetls_versionitervuocsp_statusip" + +var _TagSetMap = map[TagSet]string{ + 1: _TagSetName[0:5], + 2: _TagSetName[5:14], + 4: _TagSetName[14:20], + 8: _TagSetName[20:26], + 16: _TagSetName[26:29], + 32: _TagSetName[29:33], + 64: _TagSetName[33:38], + 128: _TagSetName[38:43], + 256: _TagSetName[43:48], + 512: _TagSetName[48:58], + 1024: _TagSetName[58:69], + 2048: _TagSetName[69:73], + 4096: _TagSetName[73:75], + 8192: _TagSetName[75:86], + 16384: _TagSetName[86:88], +} + +func (i TagSet) String() string { + if str, ok := _TagSetMap[i]; ok { + return str + } + return fmt.Sprintf("TagSet(%d)", i) +} + +var _TagSetValues = []TagSet{1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384} + +var _TagSetNameToValueMap = map[string]TagSet{ + _TagSetName[0:5]: 1, + _TagSetName[5:14]: 2, + _TagSetName[14:20]: 4, + _TagSetName[20:26]: 8, + _TagSetName[26:29]: 16, + _TagSetName[29:33]: 32, + _TagSetName[33:38]: 64, + _TagSetName[38:43]: 128, + _TagSetName[43:48]: 256, + _TagSetName[48:58]: 512, + _TagSetName[58:69]: 1024, + _TagSetName[69:73]: 2048, + _TagSetName[73:75]: 4096, + _TagSetName[75:86]: 8192, + _TagSetName[86:88]: 16384, +} + +// TagSetString retrieves an enum value from the enum constants string name. +// Throws an error if the param is not part of the enum. +func TagSetString(s string) (TagSet, error) { + if val, ok := _TagSetNameToValueMap[s]; ok { + return val, nil + } + return 0, fmt.Errorf("%s does not belong to TagSet values", s) +} + +// TagSetValues returns all values of the enum +func TagSetValues() []TagSet { + return _TagSetValues +} + +// IsATagSet returns "true" if the value is listed in the enum definition. "false" otherwise +func (i TagSet) IsATagSet() bool { + _, ok := _TagSetMap[i] + return ok +} diff --git a/lib/tagset/tagset_test.go b/lib/tagset/tagset_test.go new file mode 100644 index 00000000000..346fed9d69b --- /dev/null +++ b/lib/tagset/tagset_test.go @@ -0,0 +1,64 @@ +package tagset + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTagSetMarshalJSON(t *testing.T) { + var tests = []struct { + tagset TagSet + expected string + }{ + {IP, `["ip"]`}, + {0, `null`}, + } + + for _, tc := range tests { + ts := &tc.tagset + got, err := json.Marshal(ts) + require.Nil(t, err) + require.Equal(t, tc.expected, string(got)) + } + +} + +func TestTagSet_UnmarshalJSON(t *testing.T) { + var tests = []struct { + tags []byte + sets []TagSet + }{ + {[]byte(`[]`), []TagSet{}}, + {[]byte(`["ip", "proto"]`), []TagSet{IP, Proto}}, + } + + for _, tc := range tests { + ts := new(TagSet) + require.Nil(t, json.Unmarshal(tc.tags, ts)) + for _, tag := range tc.sets { + assert.True(t, ts.Has(tag)) + } + } + +} + +func TestTagSetTextUnmarshal(t *testing.T) { + var testMatrix = map[string]TagSet{ + "": 0, + "ip": IP, + "ip,proto": IP | Proto, + " ip , proto ": IP | Proto, + " ip , , proto ": IP | Proto, + " ip ,, proto ,,": IP | Proto, + } + + for input, expected := range testMatrix { + var set = new(TagSet) + err := set.UnmarshalText([]byte(input)) + require.NoError(t, err) + require.Equal(t, expected, *set) + } +}