Skip to content

Commit a165ccf

Browse files
committed
Complete refactoring
1 parent 571bc78 commit a165ccf

File tree

9 files changed

+259
-249
lines changed

9 files changed

+259
-249
lines changed

.spaghetti-cutter.hjson

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
{
2-
tool: ["x/*", "data"],
2+
tool: ["x/*", "data"]
3+
db: ["config"]
34

45
size: 1024
56

67
allowAdditionally: {
78
// package parse is allowed in API tests
89
// so we can test with real source code
910
"*_test": ["parse"]
10-
11-
// the central data structures are filled with configuration data
12-
"x/config": ["data"]
1311
}
1412

1513
// document and restrict usage of external packages
1614
allowOnlyIn: {
17-
"github.com/hjson/**": ["x/config"]
15+
"github.com/hjson/**": ["config"]
1816
"golang.org/x/tools**": ["parse*", "x/pkgs*"]
1917
}
2018
}

x/config/config.go renamed to config/config.go

Lines changed: 8 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,6 @@ import (
44
"fmt"
55
"math"
66
"regexp"
7-
"sort"
8-
"strings"
97

108
"github.com/flowdev/spaghetti-cutter/data"
119
"github.com/hjson/hjson-go"
@@ -14,71 +12,10 @@ import (
1412
// File is the name of the configuration file
1513
const File = ".spaghetti-cutter.hjson"
1614

17-
type patternGroup struct {
18-
left data.Pattern
19-
right data.PatternList
20-
}
21-
22-
// PatternMap is a map from a single pattern to a list of patterns.
23-
type PatternMap map[string]patternGroup
24-
25-
// String implements Stringer and returns the map of patterns,
26-
// or "....." if it is empty.
27-
func (pm *PatternMap) String() string {
28-
if pm == nil || len(*pm) <= 0 {
29-
return "....."
30-
}
31-
keys := make([]string, 0, len(*pm))
32-
for k := range *pm {
33-
keys = append(keys, k)
34-
}
35-
sort.Strings(keys)
36-
var b strings.Builder
37-
for _, left := range keys {
38-
b.WriteString("`")
39-
b.WriteString(left)
40-
b.WriteString("`")
41-
b.WriteString(": ")
42-
b.WriteString((*pm)[left].right.String())
43-
b.WriteString(" ; ")
44-
}
45-
s := b.String()
46-
return s[:len(s)-3]
47-
}
48-
49-
// HasKeyValue checks if this pattern map contains the given key value pair.
50-
// The strict versions are checked first
51-
// (1. strictKey+strictValue, 2. strictKey+value, 3. key+strictValue, 4. key+value).
52-
func (pm *PatternMap) HasKeyValue(key, strictKey, value, strictValue string) (hasKey, hasValue bool) {
53-
if pm == nil {
54-
return false, false
55-
}
56-
57-
for _, k := range []string{strictKey, key} {
58-
if k == "" {
59-
continue
60-
}
61-
for _, group := range *pm {
62-
if m := group.left.Regexp.FindStringSubmatch(k); len(m) > 0 {
63-
dollars := m[1:]
64-
65-
if _, full := group.right.MatchString(strictValue, dollars); strictValue != "" && full {
66-
return true, true
67-
}
68-
if _, full := group.right.MatchString(value, dollars); value != "" && full {
69-
return true, true
70-
}
71-
hasKey = true
72-
}
73-
}
74-
}
75-
return hasKey, false
76-
}
77-
7815
// Config contains the parsed configuration.
7916
type Config struct {
80-
AllowOnlyIn *PatternMap
81-
AllowAdditionally *PatternMap
17+
AllowOnlyIn *data.PatternMap
18+
AllowAdditionally *data.PatternMap
8219
Tool data.PatternList
8320
DB data.PatternList
8421
God data.PatternList
@@ -111,7 +48,7 @@ func convertFromJSON(jcfg map[string]interface{}) (Config, error) {
11148
var size uint
11249
var noGod bool
11350
var pl data.PatternList
114-
var pm *PatternMap
51+
var pm *data.PatternMap
11552

11653
cfg := Config{}
11754

@@ -153,7 +90,7 @@ func convertFromJSON(jcfg map[string]interface{}) (Config, error) {
15390
return cfg, nil
15491
}
15592

156-
func convertPatternMapFromJSON(i interface{}, key string) (*PatternMap, error) {
93+
func convertPatternMapFromJSON(i interface{}, key string) (*data.PatternMap, error) {
15794
var err error
15895
var pl data.PatternList
15996
var re *regexp.Regexp
@@ -168,7 +105,7 @@ func convertPatternMapFromJSON(i interface{}, key string) (*PatternMap, error) {
168105
return nil, fmt.Errorf("expected string map for key '%s', got type: %T", key, i)
169106
}
170107

171-
pm := PatternMap(make(map[string]patternGroup, len(m)))
108+
pm := data.PatternMap(make(map[string]data.PatternGroup, len(m)))
172109

173110
for k, v := range m {
174111
if re, dollars, _, err = data.RegexpForPattern(k, data.EnumDollarStar, 0); err != nil {
@@ -177,9 +114,9 @@ func convertPatternMapFromJSON(i interface{}, key string) (*PatternMap, error) {
177114
if pl, err = convertPatternListFromJSON(v, key+": "+k, data.EnumDollarDigit, dollars); err != nil {
178115
return nil, err
179116
}
180-
pm[k] = patternGroup{
181-
left: data.Pattern{Pattern: k, Regexp: re},
182-
right: pl,
117+
pm[k] = data.PatternGroup{
118+
Left: data.Pattern{Pattern: k, Regexp: re},
119+
Right: pl,
183120
}
184121
}
185122

config/config_test.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package config_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/flowdev/spaghetti-cutter/config"
8+
)
9+
10+
func TestParseAndStringers(t *testing.T) {
11+
specs := []struct {
12+
name string
13+
givenConfigBytes []byte
14+
expectedConfigString string
15+
}{
16+
{
17+
name: "all-empty",
18+
givenConfigBytes: []byte(`{
19+
}`),
20+
expectedConfigString: "{" +
21+
"..... ..... ... ... `main` " +
22+
"2048 false" +
23+
"}",
24+
}, {
25+
name: "scalars-only",
26+
givenConfigBytes: []byte(`{
27+
"size": 3072,
28+
"noGod": true
29+
}`),
30+
expectedConfigString: "{" +
31+
"..... ..... ... ... ... " +
32+
"3072 true" +
33+
"}",
34+
}, {
35+
name: "list-one",
36+
givenConfigBytes: []byte(`{
37+
"god": ["a"]
38+
}`),
39+
expectedConfigString: "{" +
40+
"..... ..... " +
41+
"... " +
42+
"... " +
43+
"`a` " +
44+
"2048 false" +
45+
"}",
46+
}, {
47+
name: "list-many",
48+
givenConfigBytes: []byte(`{
49+
"god": ["a", "be", "do", "ra"]
50+
}`),
51+
expectedConfigString: "{" +
52+
"..... ..... " +
53+
"... " +
54+
"... " +
55+
"`a`, `be`, `do`, `ra` " +
56+
"2048 false" +
57+
"}",
58+
}, {
59+
name: "map-simple-pair",
60+
givenConfigBytes: []byte(`{
61+
"allowOnlyIn": {
62+
"a": ["b"]
63+
}
64+
}`),
65+
expectedConfigString: "{" +
66+
"`a`: `b` " +
67+
"..... " +
68+
"... ... `main` 2048 false" +
69+
"}",
70+
}, {
71+
name: "map-multiple-pairs",
72+
givenConfigBytes: []byte(`{
73+
"allowOnlyIn": {
74+
"a": ["b", "c", "do", "foo"],
75+
"e": ["bar", "car"]
76+
}
77+
}`),
78+
expectedConfigString: "{" +
79+
"`a`: `b`, `c`, `do`, `foo` ; `e`: `bar`, `car` " +
80+
"..... " +
81+
"... ... `main` 2048 false" +
82+
"}",
83+
}, {
84+
name: "map-one-pair-many-stars",
85+
givenConfigBytes: []byte(`{
86+
"allowOnlyIn": {
87+
"a/*/b/**": ["c/*/d/**"]
88+
}
89+
}`),
90+
expectedConfigString: "{" +
91+
"`a/*/b/**`: `c/*/d/**` " +
92+
"..... " +
93+
"... ... `main` 2048 false" +
94+
"}",
95+
}, {
96+
name: "map-all-complexity",
97+
givenConfigBytes: []byte(`{
98+
"allowAdditionally": {
99+
"*/*a/**": ["*/*b/**", "b*/c*d/**"]
100+
}
101+
}`),
102+
expectedConfigString: "{" +
103+
"..... " +
104+
"`*/*a/**`: `*/*b/**`, `b*/c*d/**` " +
105+
"... ... `main` 2048 false" +
106+
"}",
107+
}, {
108+
name: "maps-and-lists-only",
109+
givenConfigBytes: []byte(`{
110+
"allowOnlyIn": {
111+
"github.com/lib/pq": ["a"]
112+
},
113+
"allowAdditionally": {
114+
"a": ["b"]
115+
},
116+
"tool": ["x/**"],
117+
"db": ["pkg/db/*"],
118+
"god": ["main"]
119+
}`),
120+
expectedConfigString: "{" +
121+
"`github.com/lib/pq`: `a` " +
122+
"`a`: `b` " +
123+
"`x/**` " +
124+
"`pkg/db/*` " +
125+
"`main` " +
126+
"2048 " +
127+
"false" +
128+
"}",
129+
}, {
130+
name: "a-bit-of-everything",
131+
givenConfigBytes: []byte(`{
132+
"allowOnlyIn": {
133+
"github.com/lib/pq": ["a", "b"]
134+
},
135+
"allowAdditionally": {
136+
"a": ["b"],
137+
"c": ["d"]
138+
},
139+
"tool": ["pkg/mysupertool", "pkg/x/**"],
140+
"db": ["pkg/db", "pkg/entities"],
141+
"god": ["main", "pkg/service"],
142+
"size": 3072,
143+
"noGod": true
144+
}`),
145+
expectedConfigString: "{" +
146+
"`github.com/lib/pq`: `a`, `b` " +
147+
"`a`: `b` ; `c`: `d` " +
148+
"`pkg/mysupertool`, `pkg/x/**` " +
149+
"`pkg/db`, `pkg/entities` " +
150+
"`main`, `pkg/service` " +
151+
"3072 " +
152+
"true" +
153+
"}",
154+
},
155+
}
156+
157+
for _, spec := range specs {
158+
t.Run(spec.name, func(t *testing.T) {
159+
actualConfig, err := config.Parse(spec.givenConfigBytes, spec.name)
160+
if err != nil {
161+
t.Fatalf("got unexpected error: %v", err)
162+
}
163+
actualConfigString := fmt.Sprint(actualConfig)
164+
if actualConfigString != spec.expectedConfigString {
165+
t.Errorf("expected configuration %v, actual %v",
166+
spec.expectedConfigString, actualConfigString)
167+
}
168+
})
169+
}
170+
}

data/pattern_map.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package data
2+
3+
import (
4+
"sort"
5+
"strings"
6+
)
7+
8+
type PatternGroup struct {
9+
Left Pattern
10+
Right PatternList
11+
}
12+
13+
// PatternMap is a map from a single pattern to a list of patterns.
14+
type PatternMap map[string]PatternGroup
15+
16+
// String implements Stringer and returns the map of patterns,
17+
// or "....." if it is empty.
18+
// The stringer methods are tested in the config package: TestParseAndStringers
19+
func (pm *PatternMap) String() string {
20+
if pm == nil || len(*pm) <= 0 {
21+
return "....."
22+
}
23+
keys := make([]string, 0, len(*pm))
24+
for k := range *pm {
25+
keys = append(keys, k)
26+
}
27+
sort.Strings(keys)
28+
var b strings.Builder
29+
for _, left := range keys {
30+
b.WriteString("`")
31+
b.WriteString(left)
32+
b.WriteString("`")
33+
b.WriteString(": ")
34+
b.WriteString((*pm)[left].Right.String())
35+
b.WriteString(" ; ")
36+
}
37+
s := b.String()
38+
return s[:len(s)-3]
39+
}
40+
41+
// HasKeyValue checks if this pattern map contains the given key value pair.
42+
// The strict versions are checked first
43+
// (1. strictKey+strictValue, 2. strictKey+value, 3. key+strictValue, 4. key+value).
44+
func (pm *PatternMap) HasKeyValue(key, strictKey, value, strictValue string) (hasKey, hasValue bool) {
45+
if pm == nil {
46+
return false, false
47+
}
48+
49+
for _, k := range []string{strictKey, key} {
50+
if k == "" {
51+
continue
52+
}
53+
for _, group := range *pm {
54+
if m := group.Left.Regexp.FindStringSubmatch(k); len(m) > 0 {
55+
dollars := m[1:]
56+
57+
if _, full := group.Right.MatchString(strictValue, dollars); strictValue != "" && full {
58+
return true, true
59+
}
60+
if _, full := group.Right.MatchString(value, dollars); value != "" && full {
61+
return true, true
62+
}
63+
hasKey = true
64+
}
65+
}
66+
}
67+
return hasKey, false
68+
}

0 commit comments

Comments
 (0)