Skip to content

Commit db9adb2

Browse files
authored
Merge pull request #96: feat!: Introduce New function with functional options pattern
Today, we have separate functions for creating the Parser - NewFromSaved for using the built-in yaml definitions, NewFromBytes for providing your own regexes as bytes of a yaml file, and NewWithOptions for providing a path to a yaml file with various options from the library selected - e.g. configuring the LRU cache size, turning on the (unsafe) sorting mode, and options related to that. This PR implements a New function that using the function options pattern. So instead of using any of the old interfaces, you can now New(WithRegexDefinitions(...)) to provide your own regexes, New(WithCacheSize(...)) to customize the cache size, or any combination of our new With* functions - the full list added here is: WithMode, WithDebug, WithSort, WithCacheSize, WithMissesThreshold, WithMatchIdxNotOk, and WithRegexDefinitions
2 parents 9c86a9b + 76a10f6 commit db9adb2

10 files changed

Lines changed: 407 additions & 172 deletions

File tree

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,29 @@ package main
4242
import (
4343
"fmt"
4444
"log"
45+
"os"
4546

4647
"github.com/ua-parser/uap-go/uaparser"
48+
"gopkg.in/yaml.v3"
4749
)
4850

4951
func main() {
5052
uagent := "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_3; en-us; Silk/1.1.0-80) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16 Silk-Accelerated=true"
5153

52-
parser, err := uaparser.New("./regexes.yaml")
54+
55+
regexes, err := os.ReadFile("./regexes.yaml")
56+
if err != nil {
57+
log.Fatal(err)
58+
}
59+
60+
var def *uaparser.RegexesDefinitions
61+
62+
if err := yaml.Unmarshal(regexes, def); err != nil {
63+
fmt.Printf("error parsing regexes definitions. Error: %s\n", err.Error())
64+
return
65+
}
66+
67+
parser, err := uaparser.New(uaparser.WithRegexesDefinitions(def))
5368
if err != nil {
5469
log.Fatal(err)
5570
}

test.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,41 @@ import (
99
"time"
1010

1111
"github.com/ua-parser/uap-go/uaparser"
12+
"gopkg.in/yaml.v3"
1213
)
1314

1415
func main() {
1516
if len(os.Args) < 3 {
1617
fmt.Printf("Usage: %s [old|new|both] [concurrency level]\n", os.Args[0])
1718
return
1819
}
20+
21+
regexes, err := os.ReadFile("./uap-core/regexes.yaml")
22+
if err != nil {
23+
fmt.Printf("Failed to read regexes file. Error: %s\n", err.Error())
24+
return
25+
}
26+
27+
var def uaparser.RegexDefinitions
28+
29+
if err := yaml.Unmarshal(regexes, &def); err != nil {
30+
fmt.Printf("error parsing regexes definitions. Error: %s\n", err.Error())
31+
return
32+
}
33+
1934
var wg sync.WaitGroup
2035
cLevel, _ := strconv.Atoi(os.Args[2])
2136
switch os.Args[1] {
2237
case "new":
2338
fmt.Println("Running new version of uap...")
24-
uaParser, _ := uaparser.NewWithOptions("./uap-core/regexes.yaml", (uaparser.EOsLookUpMode | uaparser.EUserAgentLookUpMode), 100, 20, true, true, 1024)
39+
uaParser, _ := uaparser.New(
40+
uaparser.WithRegexDefinitions(def),
41+
uaparser.WithMode(uaparser.EOsLookUpMode|uaparser.EUserAgentLookUpMode),
42+
uaparser.WithMatchIdxNotOk(20),
43+
uaparser.WithSort(true, uaparser.WithMissesThreshold(100)),
44+
uaparser.WithDebug(true),
45+
uaparser.WithCacheSize(1024),
46+
)
2547
for i := 0; i < cLevel; i++ {
2648
wg.Add(1)
2749
go runTest(uaParser, i, &wg)
@@ -30,7 +52,7 @@ func main() {
3052
return
3153
case "old":
3254
fmt.Println("Running old version of uap...")
33-
uaParser, _ := uaparser.New("./uap-core/regexes.yaml")
55+
uaParser, _ := uaparser.New(uaparser.WithRegexDefinitions(def))
3456
for i := 0; i < cLevel; i++ {
3557
wg.Add(1)
3658
go runTest(uaParser, i, &wg)
@@ -39,13 +61,20 @@ func main() {
3961
return
4062
case "both":
4163
fmt.Println("Running new version of uap...")
42-
uaParser, _ := uaparser.NewWithOptions("./uap-core/regexes.yaml", (uaparser.EOsLookUpMode | uaparser.EUserAgentLookUpMode), 100, 20, true, true, 1024)
64+
uaParser, _ := uaparser.New(
65+
uaparser.WithRegexDefinitions(def),
66+
uaparser.WithMode(uaparser.EOsLookUpMode|uaparser.EUserAgentLookUpMode),
67+
uaparser.WithMatchIdxNotOk(20),
68+
uaparser.WithSort(true, uaparser.WithMissesThreshold(100)),
69+
uaparser.WithDebug(true),
70+
uaparser.WithCacheSize(1024),
71+
)
4372
for i := 0; i < cLevel; i++ {
4473
wg.Add(1)
4574
runTest(uaParser, i, &wg)
4675
}
4776
fmt.Println("Running old version of uap...")
48-
uaParser, _ = uaparser.New("./uap-core/regexes.yaml")
77+
uaParser, _ = uaparser.New(uaparser.WithRegexDefinitions(def))
4978
for i := 0; i < cLevel; i++ {
5079
wg.Add(1)
5180
runTest(uaParser, i, &wg)

uaparser/benchmark_test.go

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"os"
88
"strings"
99
"testing"
10+
11+
"gopkg.in/yaml.v3"
1012
)
1113

1214
var benchedParser *Parser
@@ -15,11 +17,30 @@ var largeUasSample []string
1517

1618
func init() {
1719
var err error
18-
benchedParser, err = New("../uap-core/regexes.yaml")
20+
21+
regexes, err := os.ReadFile("../uap-core/regexes.yaml")
1922
if err != nil {
2023
log.Fatal(err)
2124
}
22-
benchedParserWithOptions, err = NewWithOptions("../uap-core/regexes.yaml", (EOsLookUpMode | EUserAgentLookUpMode), 100, 20, true, true, cDefaultCacheSize)
25+
26+
var def RegexDefinitions
27+
28+
if err := yaml.Unmarshal(regexes, &def); err != nil {
29+
log.Fatal(err)
30+
}
31+
32+
benchedParser, err = New(WithRegexDefinitions(def))
33+
if err != nil {
34+
log.Fatal(err)
35+
}
36+
benchedParserWithOptions, err = New(WithRegexDefinitions(def),
37+
WithMode(EOsLookUpMode|EUserAgentLookUpMode),
38+
WithMatchIdxNotOk(20),
39+
WithSort(true, WithMissesThreshold(100)),
40+
WithDebug(true),
41+
WithCacheSize(cDefaultCacheSize),
42+
)
43+
2344
if err != nil {
2445
log.Fatal(err)
2546
}
@@ -48,17 +69,27 @@ func BenchmarkParserWithOptions(b *testing.B) {
4869
}
4970

5071
func BenchmarkParserWithDifferentCacheSize(b *testing.B) {
51-
sizes := []int{cDefaultCacheSize, cDefaultCacheSize*2, cDefaultCacheSize*3, cDefaultCacheSize*4}
72+
sizes := []int{cDefaultCacheSize, cDefaultCacheSize * 2, cDefaultCacheSize * 3, cDefaultCacheSize * 4}
73+
74+
regexes, err := os.ReadFile("../uap-core/regexes.yaml")
75+
if err != nil {
76+
log.Fatal(err)
77+
}
78+
79+
var def RegexDefinitions
80+
81+
if err := yaml.Unmarshal(regexes, &def); err != nil {
82+
log.Fatal(err)
83+
}
5284

5385
for _, size := range sizes {
54-
parser, err := NewWithOptions(
55-
"../uap-core/regexes.yaml",
56-
EOsLookUpMode | EUserAgentLookUpMode | EDeviceLookUpMode,
57-
cDefaultMissesTreshold,
58-
cDefaultMatchIdxNotOk,
59-
false,
60-
false,
61-
size)
86+
parser, err := New(WithRegexDefinitions(def),
87+
WithMode(EOsLookUpMode|EUserAgentLookUpMode|EDeviceLookUpMode),
88+
WithMatchIdxNotOk(cDefaultMatchIdxNotOk),
89+
WithSort(false, WithMissesThreshold(cDefaultMissesTreshold)),
90+
WithDebug(false),
91+
WithCacheSize(size),
92+
)
6293
if err != nil {
6394
log.Fatal(err)
6495
}

uaparser/deprecated.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package uaparser
2+
3+
import (
4+
"os"
5+
6+
"gopkg.in/yaml.v3"
7+
)
8+
9+
// NewWithOptions is deprecated.
10+
// Deprecated: Use New and option functions instead.
11+
func NewWithOptions(regexFile string, mode, treshold, topCnt int, useSort, debugMode bool, cacheSize int) (*Parser, error) {
12+
uaRegexes, err := os.ReadFile(regexFile)
13+
if err != nil {
14+
return nil, err
15+
}
16+
17+
var def RegexDefinitions
18+
19+
if err := yaml.Unmarshal(uaRegexes, &def); err != nil {
20+
return nil, err
21+
}
22+
23+
return New(
24+
WithRegexDefinitions(def),
25+
WithMode(LookupMode(mode)),
26+
WithMatchIdxNotOk(topCnt),
27+
WithSort(useSort, WithMissesThreshold(uint64(treshold))),
28+
WithDebug(debugMode),
29+
WithCacheSize(cacheSize),
30+
)
31+
}
32+
33+
// NewFromSaved is deprecated.
34+
// Deprecated: Use New() instead.
35+
func NewFromSaved() *Parser {
36+
parser, err := New()
37+
if err != nil {
38+
// if the YAML is malformed, it's a programmatic error inside what
39+
// we've statically-compiled in our binary. Panic!
40+
panic(err.Error())
41+
}
42+
43+
return parser
44+
}
45+
46+
// NewFromBytes is deprecated.
47+
// Deprecated: Use New(WithRegexDefinitions(...)) instead
48+
func NewFromBytes(data []byte) (*Parser, error) {
49+
var def RegexDefinitions
50+
51+
if err := yaml.Unmarshal(data, &def); err != nil {
52+
return nil, err
53+
}
54+
55+
return New(WithRegexDefinitions(def))
56+
}

uaparser/device.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,20 @@ type Device struct {
88
Model string
99
}
1010

11-
func (parser *deviceParser) Match(line string, dvc *Device) {
12-
matches := parser.Reg.FindStringSubmatchIndex(line)
11+
func (dp *deviceParser) Match(line string, dvc *Device) {
12+
matches := dp.Reg.FindStringSubmatchIndex(line)
1313

1414
if len(matches) == 0 {
1515
return
1616
}
1717

18-
dvc.Family = string(parser.Reg.ExpandString(nil, parser.DeviceReplacement, line, matches))
18+
dvc.Family = string(dp.Reg.ExpandString(nil, dp.DeviceReplacement, line, matches))
1919
dvc.Family = strings.TrimSpace(dvc.Family)
2020

21-
dvc.Brand = string(parser.Reg.ExpandString(nil, parser.BrandReplacement, line, matches))
21+
dvc.Brand = string(dp.Reg.ExpandString(nil, dp.BrandReplacement, line, matches))
2222
dvc.Brand = strings.TrimSpace(dvc.Brand)
2323

24-
dvc.Model = string(parser.Reg.ExpandString(nil, parser.ModelReplacement, line, matches))
24+
dvc.Model = string(dp.Reg.ExpandString(nil, dp.ModelReplacement, line, matches))
2525
dvc.Model = strings.TrimSpace(dvc.Model)
2626
}
2727

uaparser/options.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package uaparser
2+
3+
type Option func(*Parser)
4+
5+
func WithMode(mode LookupMode) Option {
6+
return func(s *Parser) {
7+
s.config.Mode = mode
8+
}
9+
}
10+
11+
func WithSort(useSort bool, options ...SortOption) Option {
12+
return func(s *Parser) {
13+
s.config.UseSort = useSort
14+
15+
for _, o := range options {
16+
o(s)
17+
}
18+
}
19+
}
20+
21+
func WithDebug(debug bool) Option {
22+
return func(s *Parser) {
23+
s.config.DebugMode = debug
24+
}
25+
}
26+
27+
func WithCacheSize(size int) Option {
28+
return func(s *Parser) {
29+
s.config.CacheSize = size
30+
}
31+
}
32+
33+
func WithMatchIdxNotOk(idx int) Option {
34+
return func(s *Parser) {
35+
s.config.MatchIdxNotOk = idx
36+
}
37+
}
38+
39+
func WithRegexDefinitions(def RegexDefinitions) Option {
40+
return func(s *Parser) {
41+
s.RegexDefinitions = &def
42+
}
43+
}
44+
45+
type SortOption func(*Parser)
46+
47+
func WithMissesThreshold(threshold uint64) SortOption {
48+
return func(s *Parser) {
49+
s.config.MissesThreshold = threshold
50+
}
51+
}

uaparser/os.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,14 @@ type Os struct {
88
PatchMinor string `yaml:"patch_minor"`
99
}
1010

11-
func (parser *osParser) Match(line string, os *Os) {
12-
matches := parser.Reg.FindStringSubmatchIndex(line)
11+
func (osp *osParser) Match(line string, os *Os) {
12+
matches := osp.Reg.FindStringSubmatchIndex(line)
1313
if len(matches) > 0 {
14-
os.Family = string(parser.Reg.ExpandString(nil, parser.OSReplacement, line, matches))
15-
os.Major = string(parser.Reg.ExpandString(nil, parser.V1Replacement, line, matches))
16-
os.Minor = string(parser.Reg.ExpandString(nil, parser.V2Replacement, line, matches))
17-
os.Patch = string(parser.Reg.ExpandString(nil, parser.V3Replacement, line, matches))
18-
os.PatchMinor = string(parser.Reg.ExpandString(nil, parser.V4Replacement, line, matches))
14+
os.Family = string(osp.Reg.ExpandString(nil, osp.OSReplacement, line, matches))
15+
os.Major = string(osp.Reg.ExpandString(nil, osp.V1Replacement, line, matches))
16+
os.Minor = string(osp.Reg.ExpandString(nil, osp.V2Replacement, line, matches))
17+
os.Patch = string(osp.Reg.ExpandString(nil, osp.V3Replacement, line, matches))
18+
os.PatchMinor = string(osp.Reg.ExpandString(nil, osp.V4Replacement, line, matches))
1919
}
2020
}
2121

0 commit comments

Comments
 (0)