Skip to content

Commit

Permalink
Merge pull request #720 from kbolino/718-exclude-regex
Browse files Browse the repository at this point in the history
Add exclude-regex to config
  • Loading branch information
LandonTClipp committed Oct 16, 2023
2 parents 8939c75 + 25befa2 commit b919710
Show file tree
Hide file tree
Showing 4 changed files with 227 additions and 19 deletions.
3 changes: 2 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ Parameter Descriptions
| `disable-version-string` | :fontawesome-solid-x: | `#!yaml false` | Disable the version string in the generated mock files. |
| `dry-run` | :fontawesome-solid-x: | `#!yaml false` | Print the actions that would be taken, but don't perform the actions. |
| `exclude` | :fontawesome-solid-x: | `#!yaml []` | Specify subpackages to exclude when using `#!yaml recursive: True` |
| `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. |
| `filename` | :fontawesome-solid-check: | `#!yaml "mock_{{.InterfaceName}}.go"` | The name of the file the mock will reside in. |
| `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. |
| `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 |
| `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`. |
| `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. |
| `mockname` | :fontawesome-solid-check: | `#!yaml "Mock{{.InterfaceName}}"` | The name of the generated mock. |
| `outpkg` | :fontawesome-solid-check: | `#!yaml "{{.PackageName}}"` | Use `outpkg` to specify the package name of the generated mocks. |
Expand Down
15 changes: 14 additions & 1 deletion docs/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,21 @@ packages:
include-regex: ".*Client"
```

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`:

```yaml
packages:
github.com/user/project:
config:
recursive: true
include-regex: ".*"
exclude-regex: ".*Func"
```

You can only use `exclude-regex` with `include-regex`. If set by itself, `exclude-regex` has no effect.

??? note "all: true"
Using `all: true` will override `include-regex` and issue a warning.
Using `all: true` will override `include-regex` (and `exclude-regex`) and issue a warning.

Mock Constructors
-----------------
Expand Down
57 changes: 43 additions & 14 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type Config struct {
DisableConfigSearch bool `mapstructure:"disable-config-search"`
DisableVersionString bool `mapstructure:"disable-version-string"`
DryRun bool `mapstructure:"dry-run"`
ExcludeRegex string `mapstructure:"exclude-regex"`
Exported bool `mapstructure:"exported"`
FileName string `mapstructure:"filename"`
IncludeAutoGenerated bool `mapstructure:"include-auto-generated"`
Expand Down Expand Up @@ -262,28 +263,52 @@ func (c *Config) ExcludePath(path string) bool {
func (c *Config) ShouldGenerateInterface(ctx context.Context, packageName, interfaceName string) (bool, error) {
pkgConfig, err := c.GetPackageConfig(ctx, packageName)
if err != nil {
return false, err
return false, fmt.Errorf("getting package config: %w", err)
}

log := zerolog.Ctx(ctx)
if pkgConfig.All {
if pkgConfig.IncludeRegex != "" {
log.Warn().Msg("interface config has both `all` and `include-regex` set: `include-regex` will be ignored")
}
if pkgConfig.ExcludeRegex != "" {
log.Warn().Msg("interface config has both `all` and `exclude-regex` set: `exclude-regex` will be ignored")
}
return true, nil
}

interfacesSection, err := c.getInterfacesSection(ctx, packageName)
if err != nil {
return false, err
return false, fmt.Errorf("getting interfaces section: %w", err)
}
_, interfaceExists := interfacesSection[interfaceName]
if interfaceExists {
return true, nil
}

var matchedByRegex bool
if pkgConfig.IncludeRegex != "" {
if pkgConfig.All {
log := zerolog.Ctx(ctx)
log.Warn().Msg("interface config has both `all` and `include-regex` set. `include-regex` will be ignored")
} else {
matchedByRegex, err = regexp.MatchString(pkgConfig.IncludeRegex, interfaceName)
if err != nil {
return false, err
}
includeRegex := pkgConfig.IncludeRegex
excludeRegex := pkgConfig.ExcludeRegex
if includeRegex == "" {
if excludeRegex != "" {
log.Warn().Msg("interface config has `exclude-regex` set but not `include-regex`: `exclude-regex` will be ignored")
}
return false, nil
}
return pkgConfig.All || interfaceExists || matchedByRegex, nil
includedByRegex, err := regexp.MatchString(includeRegex, interfaceName)
if err != nil {
return false, fmt.Errorf("evaluating `include-regex`: %w", err)
}
if !includedByRegex {
return false, nil
}
if excludeRegex == "" {
return true, nil
}
excludedByRegex, err := regexp.MatchString(excludeRegex, interfaceName)
if err != nil {
return false, fmt.Errorf("evaluating `exclude-regex`: %w", err)
}
return !excludedByRegex, nil
}

func (c *Config) getInterfacesSection(ctx context.Context, packageName string) (map[string]any, error) {
Expand All @@ -295,7 +320,11 @@ func (c *Config) getInterfacesSection(ctx context.Context, packageName string) (
if !exists {
return make(map[string]any), nil
}
return interfaceSection.(map[string]any), nil
mapConfig, ok := interfaceSection.(map[string]any)
if !ok {
return nil, fmt.Errorf("interfaces section has type %T, expected map[string]any", interfaceSection)
}
return mapConfig, nil
}

func (c *Config) GetInterfaceConfig(ctx context.Context, packageName string, interfaceName string) ([]*Config, error) {
Expand Down
171 changes: 168 additions & 3 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,17 @@ func TestConfig_ShouldGenerateInterface(t *testing.T) {
want: false,
wantErr: true,
},
{
name: "invalid interfaces section returns error",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"interfaces": true,
},
},
},
wantErr: true,
},
{
name: "should generate all interfaces",
c: &Config{
Expand Down Expand Up @@ -609,7 +620,7 @@ func TestConfig_ShouldGenerateInterface(t *testing.T) {
want: true,
},
{
name: "should generate using included-regex",
name: "should generate using include-regex",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
Expand All @@ -622,7 +633,7 @@ func TestConfig_ShouldGenerateInterface(t *testing.T) {
want: true,
},
{
name: "should generate when using all and included-regex doesn't match",
name: "should generate when using all and include-regex doesn't match",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
Expand All @@ -636,18 +647,172 @@ func TestConfig_ShouldGenerateInterface(t *testing.T) {
want: true,
},
{
name: "should not generate when included-regex doesn't match",
name: "should not generate when include-regex doesn't match",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"include-regex": ".*XInterface",
},
},
},
},
want: false,
},
{
name: "should not generate when include-regex and exclude-regex both match",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"include-regex": ".*Interface",
"exclude-regex": "Some.*",
},
},
},
},
want: false,
},
{
name: "should generate when include-regex matches but not exclude-regex",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"include-regex": ".*Interface",
"exclude-regex": "Foo.*",
},
},
},
},
want: true,
},
{
name: "should not generate when neither include-regex nor exclude-regex match",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"include-regex": ".*XInterface",
"exclude-regex": "Foo.*",
},
},
},
},
want: false,
},
{
name: "should not generate when exclude-regex doesn't match but include-regex isn't set",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"exclude-regex": "Foo.*",
},
},
},
},
want: false,
},
{
name: "should generate when using all and exclude-regex matches",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"all": true,
"exclude-regex": ".*Interface",
},
},
},
},
want: true,
},
{
name: "should generate when interface is selected and exclude-regex matches",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"interfaces": map[string]interface{}{
"SomeInterface": struct{}{},
},
"config": map[string]interface{}{
"exclude-regex": ".*Interface",
},
},
},
},
want: true,
},
{
name: "invalid include-regex is ignored if all is set",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"all": true,
"include-regex": "[",
},
},
},
},
want: true,
},
{
name: "invalid include-regex results in error",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"include-regex": "[",
},
},
},
},
wantErr: true,
},
{
name: "invalid exclude-regex is ignored if all is set",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"all": true,
"include-regex": ".*",
"exclude-regex": "[",
},
},
},
},
want: true,
},
{
name: "invalid exclude-regex is ignored if include-regex is not set",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"exclude-regex": "[",
},
},
},
},
want: false,
},
{
name: "invalid exclude-regex results in error",
c: &Config{
Packages: map[string]interface{}{
"some_package": map[string]interface{}{
"config": map[string]interface{}{
"include-regex": ".*",
"exclude-regex": "[",
},
},
},
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down

0 comments on commit b919710

Please sign in to comment.