Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add exclude-regex to config #720

Merged
merged 3 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading