Skip to content

Commit 4a65ead

Browse files
authored
Merge pull request #106 from dengsh12/NLB5207
Provide `-filter` and `-override` with command line generator
2 parents 2bbc14b + eaf1cad commit 4a65ead

File tree

13 files changed

+484
-40
lines changed

13 files changed

+484
-40
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ func main() {
7474
```
7575

7676
# Generate support for third-party modules
77-
This is an example that takes the path of a third-party module source code to generate support for it. Assume the source code path of that module is `./src`. You can call `go run cmd/generate/main.go ./src`. The stdout will be like
77+
This is a simple example that takes the path of a third-party module source code to generate support for it. For detailed usage of the tool, please run
78+
`go run ./cmd/generate/ --help`
79+
Assuming the source code path of that module is `./src`, you can call `go run ./cmd/generate/ --src-path=./src`. The output will be similar to:
7880

7981
```go
8082
/**

cmd/generate/cmd_util.go

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* Copyright (c) F5, Inc.
3+
*
4+
* This source code is licensed under the Apache License, Version 2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package main
9+
10+
import (
11+
"errors"
12+
"fmt"
13+
"strings"
14+
15+
"github.com/nginxinc/nginx-go-crossplane/internal/generator"
16+
)
17+
18+
type filterFlag struct {
19+
filter map[string]struct{}
20+
}
21+
22+
func (f *filterFlag) Set(value string) error {
23+
if f.filter == nil {
24+
f.filter = make(map[string]struct{})
25+
}
26+
f.filter[value] = struct{}{}
27+
return nil
28+
}
29+
30+
func (f *filterFlag) String() string {
31+
return fmt.Sprintf("%v", *f)
32+
}
33+
34+
type overrideItem struct {
35+
directive string
36+
masks []generator.Mask
37+
}
38+
39+
func (item *overrideItem) UnmarshalText(text []byte) error {
40+
rawOverride := string(text)
41+
42+
// rawStr should follow the format: directive:bitmask00|bitmask01|...,bitmask10|bitmask11|...
43+
directive, definition, found := strings.Cut(rawOverride, ":")
44+
if !found {
45+
return errors.New("colon not found")
46+
}
47+
directive = strings.TrimSpace(directive)
48+
49+
item.directive = directive
50+
if directive == "" {
51+
return errors.New("directive name is empty")
52+
}
53+
54+
definition = strings.TrimSpace(definition)
55+
if definition == "" {
56+
return errors.New("directive definition is empty")
57+
}
58+
59+
for _, varNamesStr := range strings.Split(definition, ",") {
60+
varNamesList := strings.Split(varNamesStr, "|")
61+
varNamesNum := len(varNamesList)
62+
directiveMask := make(generator.Mask, varNamesNum)
63+
64+
for idx, varName := range varNamesList {
65+
trimmedName := strings.TrimSpace(varName)
66+
if trimmedName == "" {
67+
return errors.New("one directive bitmask is empty, check if there are unnecessary |")
68+
}
69+
70+
directiveMask[idx] = trimmedName
71+
}
72+
item.masks = append(item.masks, directiveMask)
73+
}
74+
75+
return nil
76+
}
77+
78+
type override map[string][]generator.Mask
79+
80+
func (ov *override) String() string {
81+
if ov == nil {
82+
return "nil"
83+
}
84+
return fmt.Sprintf("%v", *ov)
85+
}
86+
87+
func (ov *override) Set(value string) error {
88+
if *ov == nil {
89+
*ov = override{}
90+
}
91+
var item overrideItem
92+
err := item.UnmarshalText([]byte(value))
93+
if err != nil {
94+
return fmt.Errorf("invalid override %s:%w", value, err)
95+
}
96+
97+
(*ov)[item.directive] = item.masks
98+
return nil
99+
}

cmd/generate/cmd_util_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/**
2+
* Copyright (c) F5, Inc.
3+
*
4+
* This source code is licensed under the Apache License, Version 2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package main
9+
10+
import (
11+
"testing"
12+
13+
"github.com/nginxinc/nginx-go-crossplane/internal/generator"
14+
"github.com/stretchr/testify/require"
15+
)
16+
17+
//nolint:funlen
18+
func TestOverrideParser(t *testing.T) {
19+
t.Parallel()
20+
tests := []struct {
21+
name string
22+
input string
23+
expected overrideItem
24+
wantErr bool
25+
}{
26+
{
27+
name: "normalFormat_pass",
28+
input: "location:ngxHTTPMainConf|ngxConfTake12,ngxStreamMainConf",
29+
30+
expected: overrideItem{
31+
directive: "location",
32+
masks: []generator.Mask{
33+
{"ngxHTTPMainConf", "ngxConfTake12"},
34+
{"ngxStreamMainConf"},
35+
},
36+
},
37+
wantErr: false,
38+
},
39+
{
40+
name: "withSpaces_pass",
41+
input: "hash:ngxHTTPUpsConf | ngxConfTake12, ngxStreamUpsConf | ngxConfTake12",
42+
expected: overrideItem{
43+
directive: "hash",
44+
masks: []generator.Mask{
45+
{"ngxHTTPUpsConf", "ngxConfTake12"},
46+
{"ngxStreamUpsConf", "ngxConfTake12"},
47+
},
48+
},
49+
wantErr: false,
50+
},
51+
{
52+
name: "withoutColon_fail",
53+
input: "hashngxHTTPUpsConf | ngxConfTake12,ngxStreamUpsConf | ngxConfTake12",
54+
wantErr: true,
55+
},
56+
{
57+
name: "colonLeftsideEmpty_fail",
58+
input: " :ngxHTTPUpsConf | ngxConfTake12,ngxStreamUpsConf | ngxConfTake12",
59+
wantErr: true,
60+
},
61+
{
62+
name: "colonRightsideEmpty_fail",
63+
input: "hash: ",
64+
wantErr: true,
65+
},
66+
{
67+
name: "emptyBitmask_fail",
68+
input: "hash: ngxHTTPUpsConf| ",
69+
wantErr: true,
70+
},
71+
}
72+
73+
for _, tc := range tests {
74+
tc := tc
75+
t.Run(tc.name, func(t *testing.T) {
76+
t.Parallel()
77+
var got overrideItem
78+
err := got.UnmarshalText([]byte(tc.input))
79+
80+
if tc.wantErr {
81+
require.Error(t, err)
82+
} else {
83+
require.NoError(t, err)
84+
}
85+
86+
// If the testcase wants an error and there is an error, skip the output file validation.
87+
// Output makes no sense when there is an error.
88+
if err != nil {
89+
return
90+
}
91+
92+
require.Equal(t, tc.expected, got)
93+
})
94+
}
95+
}

cmd/generate/main.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,22 @@ import (
1818
func main() {
1919
var (
2020
sourceCodePath = flag.String("src-path", "",
21-
"the path of source code your want to generate support from, it can be either a file or a directory. (required)")
21+
"The path of source code your want to generate support from, it can be either a file or a directory. (required)")
22+
filterflags filterFlag
23+
directiveOverride override
2224
)
25+
flag.Var(&filterflags, "filter",
26+
"A list of strings specifying the directives to exclude from the output. "+
27+
"An example is: -filter directive1 -filter directive2...(optional)")
28+
flag.Var(&directiveOverride, "override",
29+
"A list of strings, used to override the output. "+
30+
"It should follow the format:{directive:bitmask00|bitmask01...,bitmask10|bitmask11...}"+"\n"+
31+
"An example is -override=log_format:ngxHTTPMainConf|ngxConf2More,ngxStreamMainConf|ngxConf2More"+"\n"+
32+
`To use | and , in command line, you may need to enclose your input in quotes, i.e. -override="directive:mask1,mask2,...". (optional)`)
33+
2334
flag.Parse()
24-
err := generator.Generate(*sourceCodePath, os.Stdout)
35+
36+
err := generator.Generate(*sourceCodePath, os.Stdout, filterflags.filter, directiveOverride)
2537
if err != nil {
2638
log.Fatal(err)
2739
}

internal/generator/generator.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,6 @@ import (
1515
// extract all the directives definitions from the .c and .cpp files in
1616
// sourcePath and its subdirectories, then output the corresponding directive
1717
// masks map named "directives" and matchFunc named "Match" via writer.
18-
func Generate(sourcePath string, writer io.Writer) error {
19-
return genFromSrcCode(sourcePath, "directives", "Match", writer)
18+
func Generate(sourcePath string, writer io.Writer, filter map[string]struct{}, override map[string][]Mask) error {
19+
return genFromSrcCode(sourcePath, "directives", "Match", writer, filter, override)
2020
}

internal/generator/generator_util.go

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ import (
2020
"strings"
2121
)
2222

23-
// A mask is a list of string, includes several variable names,
23+
// A Mask is a list of string, includes several variable names,
2424
// which specify a behavior of a directive.
2525
// An example is []string{"ngxHTTPMainConf", "ngxConfFlag",}.
2626
// A directive can have several masks.
27-
type mask []string
27+
type Mask []string
2828

2929
type supportFileTmplStruct struct {
30-
Directive2Masks map[string][]mask
30+
Directive2Masks map[string][]Mask
3131
MapVariableName string
3232
MatchFnName string
3333
}
@@ -99,8 +99,8 @@ var ngxVarNameToGo = map[string]string{
9999
}
100100

101101
//nolint:nonamedreturns
102-
func masksFromFile(path string) (directive2Masks map[string][]mask, err error) {
103-
directive2Masks = make(map[string][]mask, 0)
102+
func masksFromFile(path string) (directive2Masks map[string][]Mask, err error) {
103+
directive2Masks = make(map[string][]Mask, 0)
104104
byteContent, err := os.ReadFile(path)
105105
if err != nil {
106106
return nil, err
@@ -143,8 +143,8 @@ func masksFromFile(path string) (directive2Masks map[string][]mask, err error) {
143143
}
144144

145145
//nolint:nonamedreturns
146-
func getMasksFromPath(path string) (directive2Masks map[string][]mask, err error) {
147-
directive2Masks = make(map[string][]mask, 0)
146+
func getMasksFromPath(path string) (directive2Masks map[string][]Mask, err error) {
147+
directive2Masks = make(map[string][]Mask, 0)
148148

149149
err = filepath.WalkDir(path, func(path string, d fs.DirEntry, err error) error {
150150
if err != nil {
@@ -184,12 +184,29 @@ func getMasksFromPath(path string) (directive2Masks map[string][]mask, err error
184184
return directive2Masks, nil
185185
}
186186

187-
func genFromSrcCode(codePath string, mapVariableName string, matchFnName string, writer io.Writer) error {
187+
func genFromSrcCode(codePath string, mapVariableName string, matchFnName string, writer io.Writer,
188+
filter map[string]struct{}, override map[string][]Mask) error {
188189
directive2Masks, err := getMasksFromPath(codePath)
189190
if err != nil {
190191
return err
191192
}
192193

194+
if len(filter) > 0 {
195+
for d := range directive2Masks {
196+
if _, found := filter[d]; found {
197+
delete(directive2Masks, d)
198+
}
199+
}
200+
}
201+
202+
if override != nil {
203+
for d := range directive2Masks {
204+
if newMasks, found := override[d]; found {
205+
directive2Masks[d] = newMasks
206+
}
207+
}
208+
}
209+
193210
err = supportFileTmpl.Execute(writer, supportFileTmplStruct{
194211
Directive2Masks: directive2Masks,
195212
MapVariableName: mapVariableName,

0 commit comments

Comments
 (0)