Skip to content

Commit 5552e5d

Browse files
authored
Merge pull request #501 from abdusco/500-unset-fields
fix(gen): Replacement configuration should only consider fields that are set
2 parents 403fd70 + cc08a97 commit 5552e5d

File tree

7 files changed

+269
-205
lines changed

7 files changed

+269
-205
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2828
- `Allx` now takes a `Transformer` type parameter to transform the result of the query.
2929
- Updated documentation for readability, added code gen examples. (thanks @singhsays)
3030
- Columns are now matched in a case-insensitive manner in type replacements. (thanks @abdusco)
31+
- Columns can now be matched with as many conditions as needed in type replacements. This removes the previous requirement that boolean fields had to be specified in addition to a string field. (thanks @abdusco)
3132

3233
### Removed
3334

gen/config.go

Lines changed: 82 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package gen
22

33
import (
4+
"github.com/aarondl/opt/omit"
45
"github.com/stephenafamo/bob/gen/drivers"
56
)
67

@@ -46,9 +47,87 @@ type Config[ConstraintExtra any] struct {
4647

4748
// Replace replaces a column type with something else
4849
type Replace struct {
49-
Tables []string `yaml:"tables"`
50-
Match drivers.Column `yaml:"match"`
51-
Replace string `yaml:"replace"`
50+
Tables []string `yaml:"tables"`
51+
Match ColumnFilter `yaml:"match"`
52+
Replace string `yaml:"replace"`
53+
}
54+
55+
// ColumnFilter is used to filter columns in the config file.
56+
// It should mirror the fields of drivers.Column
57+
type ColumnFilter struct {
58+
Name omit.Val[string] `yaml:"name"`
59+
DBType omit.Val[string] `yaml:"db_type"`
60+
Type omit.Val[string] `yaml:"type"`
61+
Default omit.Val[string] `yaml:"default"`
62+
Comment omit.Val[string] `yaml:"comment"`
63+
Nullable omit.Val[bool] `yaml:"nullable"`
64+
Generated omit.Val[bool] `yaml:"generated"`
65+
AutoIncr omit.Val[bool] `yaml:"autoincr"`
66+
67+
// DomainName is the domain type name associated to the column. See here:
68+
// https://www.postgresql.org/docs/16/extend-type-system.html
69+
DomainName omit.Val[string] `yaml:"domain_name"`
70+
}
71+
72+
func (f ColumnFilter) IsEmpty() bool {
73+
return f.Name.IsUnset() &&
74+
f.DBType.IsUnset() &&
75+
f.Type.IsUnset() &&
76+
f.Default.IsUnset() &&
77+
f.Comment.IsUnset() &&
78+
f.Nullable.IsUnset() &&
79+
f.Generated.IsUnset() &&
80+
f.AutoIncr.IsUnset() &&
81+
f.DomainName.IsUnset()
82+
}
83+
84+
// Matches determines if a drivers.Column matches all the specified criteria (logical AND).
85+
//
86+
// String fields are matched case-insensitively and by regex.
87+
func (f ColumnFilter) Matches(column drivers.Column) bool {
88+
// empty filters should not match anything
89+
if f.IsEmpty() {
90+
return false
91+
}
92+
93+
if val, ok := f.Name.Get(); ok && !matchString(val, column.Name) {
94+
return false
95+
}
96+
97+
if val, ok := f.DBType.Get(); ok && !matchString(val, column.DBType) {
98+
return false
99+
}
100+
101+
if val, ok := f.Type.Get(); ok && !matchString(val, column.Type) {
102+
return false
103+
}
104+
105+
if val, ok := f.Default.Get(); ok && !matchString(val, column.Default) {
106+
return false
107+
}
108+
109+
if val, ok := f.Comment.Get(); ok && !matchString(val, column.Comment) {
110+
return false
111+
}
112+
113+
if val, ok := f.DomainName.Get(); ok && !matchString(val, column.DomainName) {
114+
return false
115+
}
116+
117+
if val, ok := f.Nullable.Get(); ok && val != column.Nullable {
118+
return false
119+
}
120+
121+
if val, ok := f.Generated.Get(); ok && val != column.Generated {
122+
return false
123+
}
124+
125+
if val, ok := f.AutoIncr.Get(); ok && val != column.AutoIncr {
126+
return false
127+
}
128+
129+
// all specified conditions matched
130+
return true
52131
}
53132

54133
type Inflections struct {

gen/config_test.go

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
package gen
2+
3+
import (
4+
"testing"
5+
6+
"github.com/aarondl/opt/omit"
7+
"github.com/stephenafamo/bob/gen/drivers"
8+
)
9+
10+
func TestColumnFilter_IsEmpty(t *testing.T) {
11+
tests := []struct {
12+
name string
13+
filter ColumnFilter
14+
expected bool
15+
}{
16+
{
17+
name: "empty",
18+
filter: ColumnFilter{},
19+
expected: true,
20+
},
21+
{
22+
name: "not empty",
23+
filter: ColumnFilter{Name: omit.From("id")},
24+
expected: false,
25+
},
26+
}
27+
for _, tt := range tests {
28+
t.Run(tt.name, func(t *testing.T) {
29+
actual := tt.filter.IsEmpty()
30+
if actual != tt.expected {
31+
t.Errorf("ColumnFilter.IsEmpty() = %v, want %v", actual, tt.expected)
32+
}
33+
})
34+
}
35+
}
36+
37+
func TestColumnFilter_Matches(t *testing.T) {
38+
tests := []struct {
39+
name string
40+
filter ColumnFilter
41+
column drivers.Column
42+
expected bool
43+
}{
44+
{
45+
name: "empty filter doesn't match anything",
46+
filter: ColumnFilter{},
47+
column: drivers.Column{Name: "id"},
48+
expected: false,
49+
},
50+
{
51+
name: "names do not match",
52+
filter: ColumnFilter{Name: omit.From("not_id")},
53+
column: drivers.Column{Name: "id"},
54+
expected: false,
55+
},
56+
{
57+
name: "names match case-insensitively",
58+
filter: ColumnFilter{Name: omit.From("ID")},
59+
column: drivers.Column{Name: "id"},
60+
expected: true,
61+
},
62+
{
63+
name: "names match with regex",
64+
filter: ColumnFilter{Name: omit.From("/id$/")},
65+
column: drivers.Column{Name: "author_id"},
66+
expected: true,
67+
},
68+
{
69+
name: "regex is always case insensitive",
70+
filter: ColumnFilter{Name: omit.From("/ID$/")},
71+
column: drivers.Column{Name: "author_id"},
72+
expected: true,
73+
},
74+
{
75+
name: "types match case-insensitively",
76+
filter: ColumnFilter{Type: omit.From("INTEGER")},
77+
column: drivers.Column{Type: "integer"},
78+
expected: true,
79+
},
80+
{
81+
name: "db types match case-insensitively",
82+
filter: ColumnFilter{DBType: omit.From("int")},
83+
column: drivers.Column{DBType: "INT"},
84+
expected: true,
85+
},
86+
{
87+
name: "domain names match case-insensitively",
88+
filter: ColumnFilter{DomainName: omit.From("email")},
89+
column: drivers.Column{DomainName: "EMAIL"},
90+
expected: true,
91+
},
92+
{
93+
name: "default values match case-insensitively",
94+
filter: ColumnFilter{Default: omit.From("null")},
95+
column: drivers.Column{Default: "NULL"},
96+
expected: true,
97+
},
98+
{
99+
name: "comments match case-insensitively",
100+
filter: ColumnFilter{Comment: omit.From("primary key")},
101+
column: drivers.Column{Comment: "PRIMARY KEY"},
102+
expected: true,
103+
},
104+
{
105+
name: "generated matches exactly",
106+
filter: ColumnFilter{Generated: omit.From(false)},
107+
column: drivers.Column{Generated: false},
108+
expected: true,
109+
},
110+
{
111+
name: "generated does not match",
112+
filter: ColumnFilter{Generated: omit.From(false)},
113+
column: drivers.Column{Generated: true},
114+
expected: false,
115+
},
116+
{
117+
name: "autoincr matches exactly",
118+
filter: ColumnFilter{AutoIncr: omit.From(false)},
119+
column: drivers.Column{AutoIncr: false},
120+
expected: true,
121+
},
122+
{
123+
name: "autoincr does not match",
124+
filter: ColumnFilter{AutoIncr: omit.From(false)},
125+
column: drivers.Column{AutoIncr: true},
126+
expected: false,
127+
},
128+
{
129+
name: "nullable matches exactly",
130+
filter: ColumnFilter{Nullable: omit.From(true)},
131+
column: drivers.Column{Nullable: true},
132+
expected: true,
133+
},
134+
{
135+
name: "nullable does not match",
136+
filter: ColumnFilter{Nullable: omit.From(true)},
137+
column: drivers.Column{Nullable: false},
138+
expected: false,
139+
},
140+
{
141+
name: "filters are combined with AND",
142+
filter: ColumnFilter{
143+
Name: omit.From("id"),
144+
Type: omit.From("wrong_type"),
145+
},
146+
column: drivers.Column{
147+
Name: "id",
148+
Type: "integer",
149+
},
150+
expected: false,
151+
},
152+
}
153+
for _, tt := range tests {
154+
t.Run(tt.name, func(t *testing.T) {
155+
if actual := tt.filter.Matches(tt.column); actual != tt.expected {
156+
t.Errorf("ColumnFilter.Matches() = %v, want %v", actual, tt.expected)
157+
}
158+
})
159+
}
160+
}

gen/gen_test.go

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package gen
33
import (
44
"testing"
55

6+
"github.com/aarondl/opt/omit"
67
"github.com/stephenafamo/bob/gen/drivers"
78
)
89

@@ -116,53 +117,53 @@ func TestProcessTypeReplacements(t *testing.T) {
116117

117118
replacements := []Replace{
118119
{
119-
Match: drivers.Column{
120-
DBType: "SERIAL",
120+
Match: ColumnFilter{
121+
DBType: omit.From("SERIAL"),
121122
},
122123
Replace: "excellent.Type",
123124
},
124125
{
125126
Tables: []string{"named_table"},
126-
Match: drivers.Column{
127-
Name: "id",
127+
Match: ColumnFilter{
128+
Name: omit.From("id"),
128129
},
129130
Replace: "excellent.NamedType",
130131
},
131132
{
132-
Match: drivers.Column{
133-
Type: "null.String",
134-
Nullable: true,
133+
Match: ColumnFilter{
134+
Type: omit.From("null.String"),
135+
Nullable: omit.From(true),
135136
},
136137
Replace: "int",
137138
},
138139
{
139-
Match: drivers.Column{
140-
DomainName: "EMAIL",
140+
Match: ColumnFilter{
141+
DomainName: omit.From("EMAIL"),
141142
},
142143
Replace: "contextInt",
143144
},
144145
{
145-
Match: drivers.Column{
146-
Name: "by_named",
146+
Match: ColumnFilter{
147+
Name: omit.From("by_named"),
147148
},
148149
Replace: "big.Int",
149150
},
150151
{
151-
Match: drivers.Column{
152-
Comment: "xid",
152+
Match: ColumnFilter{
153+
Comment: omit.From("xid"),
153154
},
154155
Replace: "xid.ID",
155156
},
156157
{
157-
Match: drivers.Column{
158-
Name: "/_id$/",
158+
Match: ColumnFilter{
159+
Name: omit.From("/_id$/"),
159160
},
160161
Replace: "fk.ID",
161162
},
162163
{
163-
Match: drivers.Column{
164-
Name: "id",
165-
AutoIncr: true,
164+
Match: ColumnFilter{
165+
Name: omit.From("id"),
166+
AutoIncr: omit.From(true),
166167
},
167168
Replace: "pk.ID",
168169
},

0 commit comments

Comments
 (0)