Skip to content

Commit b919710

Browse files
authoredOct 16, 2023
Merge pull request #720 from kbolino/718-exclude-regex
Add exclude-regex to config
2 parents 8939c75 + 25befa2 commit b919710

File tree

4 files changed

+227
-19
lines changed

4 files changed

+227
-19
lines changed
 

‎docs/configuration.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ Parameter Descriptions
6767
| `disable-version-string` | :fontawesome-solid-x: | `#!yaml false` | Disable the version string in the generated mock files. |
6868
| `dry-run` | :fontawesome-solid-x: | `#!yaml false` | Print the actions that would be taken, but don't perform the actions. |
6969
| `exclude` | :fontawesome-solid-x: | `#!yaml []` | Specify subpackages to exclude when using `#!yaml recursive: True` |
70+
| `exclude-regex` | :fontawesome-solid-x: | `#!yaml ""` | When set along with `include-regex`, then interfaces which match `include-regex` but also match `exclude-regex` will not be generated. If `all` is set, or if `include-regex` is not set, then `exclude-regex` has no effect. |
7071
| `filename` | :fontawesome-solid-check: | `#!yaml "mock_{{.InterfaceName}}.go"` | The name of the file the mock will reside in. |
7172
| `include-auto-generated` | :fontawesome-solid-x: | `#!yaml true` | Set to `#!yaml false` if you need mockery to skip auto-generated files during its recursive package discovery. When set to `#!yaml true`, mockery includes auto-generated files when determining if a particular directory is an importable package. |
72-
| `include-regex` | :fontawesome-solid-x: | `#!yaml ""` | When set, only interface names that match the expression will be generated. This setting is ignored if `all: True` is specified in the configuration |
73+
| `include-regex` | :fontawesome-solid-x: | `#!yaml ""` | When set, only interface names that match the expression will be generated. This setting is ignored if `all: True` is specified in the configuration. To further refine the interfaces generated, use `exclude-regex`. |
7374
| `inpackage` | :fontawesome-solid-x: | `#!yaml false` | When generating mocks alongside the original interfaces, you must specify `inpackage: True` to inform mockery that the mock is being placed in the same package as the original interface. |
7475
| `mockname` | :fontawesome-solid-check: | `#!yaml "Mock{{.InterfaceName}}"` | The name of the generated mock. |
7576
| `outpkg` | :fontawesome-solid-check: | `#!yaml "{{.PackageName}}"` | Use `outpkg` to specify the package name of the generated mocks. |

‎docs/features.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,21 @@ packages:
193193
include-regex: ".*Client"
194194
```
195195

196+
To further refine matched interfaces, you can also use `exclude-regex`. If an interface matches both `include-regex` and `exclude-regex` then it will not be generated. For example, to generate all interfaces except those ending in `Func`:
197+
198+
```yaml
199+
packages:
200+
github.com/user/project:
201+
config:
202+
recursive: true
203+
include-regex: ".*"
204+
exclude-regex: ".*Func"
205+
```
206+
207+
You can only use `exclude-regex` with `include-regex`. If set by itself, `exclude-regex` has no effect.
208+
196209
??? note "all: true"
197-
Using `all: true` will override `include-regex` and issue a warning.
210+
Using `all: true` will override `include-regex` (and `exclude-regex`) and issue a warning.
198211

199212
Mock Constructors
200213
-----------------

‎pkg/config/config.go

+43-14
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type Config struct {
4141
DisableConfigSearch bool `mapstructure:"disable-config-search"`
4242
DisableVersionString bool `mapstructure:"disable-version-string"`
4343
DryRun bool `mapstructure:"dry-run"`
44+
ExcludeRegex string `mapstructure:"exclude-regex"`
4445
Exported bool `mapstructure:"exported"`
4546
FileName string `mapstructure:"filename"`
4647
IncludeAutoGenerated bool `mapstructure:"include-auto-generated"`
@@ -262,28 +263,52 @@ func (c *Config) ExcludePath(path string) bool {
262263
func (c *Config) ShouldGenerateInterface(ctx context.Context, packageName, interfaceName string) (bool, error) {
263264
pkgConfig, err := c.GetPackageConfig(ctx, packageName)
264265
if err != nil {
265-
return false, err
266+
return false, fmt.Errorf("getting package config: %w", err)
267+
}
268+
269+
log := zerolog.Ctx(ctx)
270+
if pkgConfig.All {
271+
if pkgConfig.IncludeRegex != "" {
272+
log.Warn().Msg("interface config has both `all` and `include-regex` set: `include-regex` will be ignored")
273+
}
274+
if pkgConfig.ExcludeRegex != "" {
275+
log.Warn().Msg("interface config has both `all` and `exclude-regex` set: `exclude-regex` will be ignored")
276+
}
277+
return true, nil
266278
}
267279

268280
interfacesSection, err := c.getInterfacesSection(ctx, packageName)
269281
if err != nil {
270-
return false, err
282+
return false, fmt.Errorf("getting interfaces section: %w", err)
271283
}
272284
_, interfaceExists := interfacesSection[interfaceName]
285+
if interfaceExists {
286+
return true, nil
287+
}
273288

274-
var matchedByRegex bool
275-
if pkgConfig.IncludeRegex != "" {
276-
if pkgConfig.All {
277-
log := zerolog.Ctx(ctx)
278-
log.Warn().Msg("interface config has both `all` and `include-regex` set. `include-regex` will be ignored")
279-
} else {
280-
matchedByRegex, err = regexp.MatchString(pkgConfig.IncludeRegex, interfaceName)
281-
if err != nil {
282-
return false, err
283-
}
289+
includeRegex := pkgConfig.IncludeRegex
290+
excludeRegex := pkgConfig.ExcludeRegex
291+
if includeRegex == "" {
292+
if excludeRegex != "" {
293+
log.Warn().Msg("interface config has `exclude-regex` set but not `include-regex`: `exclude-regex` will be ignored")
284294
}
295+
return false, nil
285296
}
286-
return pkgConfig.All || interfaceExists || matchedByRegex, nil
297+
includedByRegex, err := regexp.MatchString(includeRegex, interfaceName)
298+
if err != nil {
299+
return false, fmt.Errorf("evaluating `include-regex`: %w", err)
300+
}
301+
if !includedByRegex {
302+
return false, nil
303+
}
304+
if excludeRegex == "" {
305+
return true, nil
306+
}
307+
excludedByRegex, err := regexp.MatchString(excludeRegex, interfaceName)
308+
if err != nil {
309+
return false, fmt.Errorf("evaluating `exclude-regex`: %w", err)
310+
}
311+
return !excludedByRegex, nil
287312
}
288313

289314
func (c *Config) getInterfacesSection(ctx context.Context, packageName string) (map[string]any, error) {
@@ -295,7 +320,11 @@ func (c *Config) getInterfacesSection(ctx context.Context, packageName string) (
295320
if !exists {
296321
return make(map[string]any), nil
297322
}
298-
return interfaceSection.(map[string]any), nil
323+
mapConfig, ok := interfaceSection.(map[string]any)
324+
if !ok {
325+
return nil, fmt.Errorf("interfaces section has type %T, expected map[string]any", interfaceSection)
326+
}
327+
return mapConfig, nil
299328
}
300329

301330
func (c *Config) GetInterfaceConfig(ctx context.Context, packageName string, interfaceName string) ([]*Config, error) {

‎pkg/config/config_test.go

+168-3
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,17 @@ func TestConfig_ShouldGenerateInterface(t *testing.T) {
572572
want: false,
573573
wantErr: true,
574574
},
575+
{
576+
name: "invalid interfaces section returns error",
577+
c: &Config{
578+
Packages: map[string]interface{}{
579+
"some_package": map[string]interface{}{
580+
"interfaces": true,
581+
},
582+
},
583+
},
584+
wantErr: true,
585+
},
575586
{
576587
name: "should generate all interfaces",
577588
c: &Config{
@@ -609,7 +620,7 @@ func TestConfig_ShouldGenerateInterface(t *testing.T) {
609620
want: true,
610621
},
611622
{
612-
name: "should generate using included-regex",
623+
name: "should generate using include-regex",
613624
c: &Config{
614625
Packages: map[string]interface{}{
615626
"some_package": map[string]interface{}{
@@ -622,7 +633,7 @@ func TestConfig_ShouldGenerateInterface(t *testing.T) {
622633
want: true,
623634
},
624635
{
625-
name: "should generate when using all and included-regex doesn't match",
636+
name: "should generate when using all and include-regex doesn't match",
626637
c: &Config{
627638
Packages: map[string]interface{}{
628639
"some_package": map[string]interface{}{
@@ -636,18 +647,172 @@ func TestConfig_ShouldGenerateInterface(t *testing.T) {
636647
want: true,
637648
},
638649
{
639-
name: "should not generate when included-regex doesn't match",
650+
name: "should not generate when include-regex doesn't match",
651+
c: &Config{
652+
Packages: map[string]interface{}{
653+
"some_package": map[string]interface{}{
654+
"config": map[string]interface{}{
655+
"include-regex": ".*XInterface",
656+
},
657+
},
658+
},
659+
},
660+
want: false,
661+
},
662+
{
663+
name: "should not generate when include-regex and exclude-regex both match",
664+
c: &Config{
665+
Packages: map[string]interface{}{
666+
"some_package": map[string]interface{}{
667+
"config": map[string]interface{}{
668+
"include-regex": ".*Interface",
669+
"exclude-regex": "Some.*",
670+
},
671+
},
672+
},
673+
},
674+
want: false,
675+
},
676+
{
677+
name: "should generate when include-regex matches but not exclude-regex",
678+
c: &Config{
679+
Packages: map[string]interface{}{
680+
"some_package": map[string]interface{}{
681+
"config": map[string]interface{}{
682+
"include-regex": ".*Interface",
683+
"exclude-regex": "Foo.*",
684+
},
685+
},
686+
},
687+
},
688+
want: true,
689+
},
690+
{
691+
name: "should not generate when neither include-regex nor exclude-regex match",
640692
c: &Config{
641693
Packages: map[string]interface{}{
642694
"some_package": map[string]interface{}{
643695
"config": map[string]interface{}{
644696
"include-regex": ".*XInterface",
697+
"exclude-regex": "Foo.*",
645698
},
646699
},
647700
},
648701
},
649702
want: false,
650703
},
704+
{
705+
name: "should not generate when exclude-regex doesn't match but include-regex isn't set",
706+
c: &Config{
707+
Packages: map[string]interface{}{
708+
"some_package": map[string]interface{}{
709+
"config": map[string]interface{}{
710+
"exclude-regex": "Foo.*",
711+
},
712+
},
713+
},
714+
},
715+
want: false,
716+
},
717+
{
718+
name: "should generate when using all and exclude-regex matches",
719+
c: &Config{
720+
Packages: map[string]interface{}{
721+
"some_package": map[string]interface{}{
722+
"config": map[string]interface{}{
723+
"all": true,
724+
"exclude-regex": ".*Interface",
725+
},
726+
},
727+
},
728+
},
729+
want: true,
730+
},
731+
{
732+
name: "should generate when interface is selected and exclude-regex matches",
733+
c: &Config{
734+
Packages: map[string]interface{}{
735+
"some_package": map[string]interface{}{
736+
"interfaces": map[string]interface{}{
737+
"SomeInterface": struct{}{},
738+
},
739+
"config": map[string]interface{}{
740+
"exclude-regex": ".*Interface",
741+
},
742+
},
743+
},
744+
},
745+
want: true,
746+
},
747+
{
748+
name: "invalid include-regex is ignored if all is set",
749+
c: &Config{
750+
Packages: map[string]interface{}{
751+
"some_package": map[string]interface{}{
752+
"config": map[string]interface{}{
753+
"all": true,
754+
"include-regex": "[",
755+
},
756+
},
757+
},
758+
},
759+
want: true,
760+
},
761+
{
762+
name: "invalid include-regex results in error",
763+
c: &Config{
764+
Packages: map[string]interface{}{
765+
"some_package": map[string]interface{}{
766+
"config": map[string]interface{}{
767+
"include-regex": "[",
768+
},
769+
},
770+
},
771+
},
772+
wantErr: true,
773+
},
774+
{
775+
name: "invalid exclude-regex is ignored if all is set",
776+
c: &Config{
777+
Packages: map[string]interface{}{
778+
"some_package": map[string]interface{}{
779+
"config": map[string]interface{}{
780+
"all": true,
781+
"include-regex": ".*",
782+
"exclude-regex": "[",
783+
},
784+
},
785+
},
786+
},
787+
want: true,
788+
},
789+
{
790+
name: "invalid exclude-regex is ignored if include-regex is not set",
791+
c: &Config{
792+
Packages: map[string]interface{}{
793+
"some_package": map[string]interface{}{
794+
"config": map[string]interface{}{
795+
"exclude-regex": "[",
796+
},
797+
},
798+
},
799+
},
800+
want: false,
801+
},
802+
{
803+
name: "invalid exclude-regex results in error",
804+
c: &Config{
805+
Packages: map[string]interface{}{
806+
"some_package": map[string]interface{}{
807+
"config": map[string]interface{}{
808+
"include-regex": ".*",
809+
"exclude-regex": "[",
810+
},
811+
},
812+
},
813+
},
814+
wantErr: true,
815+
},
651816
}
652817
for _, tt := range tests {
653818
t.Run(tt.name, func(t *testing.T) {

0 commit comments

Comments
 (0)
Please sign in to comment.