Skip to content
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
39 changes: 28 additions & 11 deletions cmd/jet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"flag"
"fmt"
"os"
"path"
"slices"
"strings"

Expand All @@ -21,7 +22,6 @@ import (
"github.com/go-jet/jet/v2/internal/3rdparty/snaker"
"github.com/go-jet/jet/v2/internal/jet"
"github.com/go-jet/jet/v2/internal/utils/errfmt"
"github.com/go-jet/jet/v2/internal/utils/strslice"
"github.com/go-jet/jet/v2/mysql"
postgres2 "github.com/go-jet/jet/v2/postgres"
"github.com/go-jet/jet/v2/sqlite"
Expand Down Expand Up @@ -86,9 +86,9 @@ func init() {
flag.StringVar(&schemaName, "schema", "public", `Database schema name. (default "public")(PostgreSQL only)`)
flag.StringVar(&params, "params", "", "Additional connection string parameters(optional). Used only if dsn is not set.")
flag.StringVar(&sslmode, "sslmode", "disable", `Whether or not to use SSL. Used only if dsn is not set. (optional)(default "disable")(PostgreSQL only)`)
flag.StringVar(&ignoreTables, "ignore-tables", "", `Comma-separated list of tables to ignore.`)
flag.StringVar(&ignoreViews, "ignore-views", "", `Comma-separated list of views to ignore.`)
flag.StringVar(&ignoreEnums, "ignore-enums", "", `Comma-separated list of enums to ignore.`)
flag.StringVar(&ignoreTables, "ignore-tables", "", `Comma-separated list of tables to ignore. Names may use shell wildcards, e.g. "user_*".`)
flag.StringVar(&ignoreViews, "ignore-views", "", `Comma-separated list of views to ignore. Names may use shell wildcards, e.g. "user_*".`)
flag.StringVar(&ignoreEnums, "ignore-enums", "", `Comma-separated list of enums to ignore. Names may use shell wildcards, e.g. "user_*".`)
flag.BoolVar(&skipModel, "skip-model", false, `Skip model generation.`)
flag.BoolVar(&skipSQLBuilder, "skip-sql-builder", false, `Skip SQL builder generation.`)

Expand All @@ -99,9 +99,9 @@ func init() {
flag.StringVar(&enumPkg, "rel-enum-path", "enum", "Relative path for the Enum files package from the destination directory.")
flag.StringVar(&modelJsonTag, "model-json-tag", "", "Json tag model to be included in Go structs. (optional)(default <empty>)(allowed values: <empty>, pascal-case, camel-case, snake-case")

flag.StringVar(&tables, "tables", "", `Comma-separated list of tables to generate.`)
flag.StringVar(&views, "views", "", `Comma-separated list of views to generate.`)
flag.StringVar(&enums, "enums", "", `Comma-separated list of enums to generate.`)
flag.StringVar(&tables, "tables", "", `Comma-separated list of tables to generate. Names may use shell wildcards, e.g. "user_*".`)
flag.StringVar(&views, "views", "", `Comma-separated list of views to generate. Names may use shell wildcards, e.g. "user_*".`)
flag.StringVar(&enums, "enums", "", `Comma-separated list of enums to generate. Names may use shell wildcards, e.g. "user_*".`)
}

func main() {
Expand Down Expand Up @@ -348,18 +348,35 @@ func createTemplateFilter(ignoreList, allowList, filterType string) templateFilt

func shouldSkipTable(table metadata.Table, filter templateFilter) bool {
if filter.ignore {
return strslice.Contains(filter.names, strings.ToLower(table.Name))
return matchesFilter(filter.names, table.Name)
}

return !strslice.Contains(filter.names, strings.ToLower(table.Name))
return !matchesFilter(filter.names, table.Name)
}

func shouldSkipEnum(enum metadata.Enum, filter templateFilter) bool {
if filter.ignore {
return strslice.Contains(filter.names, strings.ToLower(enum.Name))
return matchesFilter(filter.names, enum.Name)
}

return !strslice.Contains(filter.names, strings.ToLower(enum.Name))
return !matchesFilter(filter.names, enum.Name)
}

// matchesFilter reports whether name matches any of the filter patterns.
// Patterns support shell style wildcards (for example "user_*"), which makes it
// possible to match dynamically named tables such as partitions with a single
// entry. Patterns without wildcard characters still match exactly.
func matchesFilter(patterns []string, name string) bool {
name = strings.ToLower(name)

for _, pattern := range patterns {
// pattern is already lower cased and trimmed by parseList.
if matched, err := path.Match(pattern, name); err == nil && matched {
return true
}
}

return false
}

func createModelTags(columnMetaData metadata.Column) []string {
Expand Down
77 changes: 77 additions & 0 deletions cmd/jet/main_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package main

import (
"testing"

"github.com/go-jet/jet/v2/generator/metadata"
)

func TestMatchesFilter(t *testing.T) {
tests := []struct {
name string
patterns []string
input string
want bool
}{
{"exact match", []string{"actor"}, "actor", true},
{"exact no match", []string{"actor"}, "film", false},
{"case insensitive input", []string{"actor"}, "ACTOR", true},
{"underscore is literal", []string{"user_data"}, "user_data", true},
{"underscore does not match anything", []string{"user_data"}, "userxdata", false},
{"trailing wildcard", []string{"payment_*"}, "payment_2020", true},
{"trailing wildcard no match", []string{"payment_*"}, "orders", false},
{"single char wildcard", []string{"log_?"}, "log_1", true},
{"single char wildcard too long", []string{"log_?"}, "log_12", false},
{"one of many patterns", []string{"actor", "film_*"}, "film_category", true},
{"empty pattern from empty list", []string{""}, "actor", false},
{"malformed pattern is ignored", []string{"[bad"}, "actor", false},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := matchesFilter(tt.patterns, tt.input); got != tt.want {
t.Errorf("matchesFilter(%q, %q) = %v, want %v", tt.patterns, tt.input, got, tt.want)
}
})
}
}

func TestShouldSkipTableIgnore(t *testing.T) {
filter := templateFilter{names: parseList("payment_*, actor"), ignore: true}

skipped := []string{"payment_2020", "payment_2021", "actor"}
for _, name := range skipped {
if !shouldSkipTable(metadata.Table{Name: name}, filter) {
t.Errorf("expected table %q to be skipped", name)
}
}

kept := []string{"film", "category"}
for _, name := range kept {
if shouldSkipTable(metadata.Table{Name: name}, filter) {
t.Errorf("expected table %q to be generated", name)
}
}
}

func TestShouldSkipTableAllow(t *testing.T) {
filter := templateFilter{names: parseList("film_*"), ignore: false}

if shouldSkipTable(metadata.Table{Name: "film_actor"}, filter) {
t.Error("expected table film_actor to be generated")
}
if !shouldSkipTable(metadata.Table{Name: "payment"}, filter) {
t.Error("expected table payment to be skipped")
}
}

func TestShouldSkipEnum(t *testing.T) {
filter := templateFilter{names: parseList("mpaa_*"), ignore: true}

if !shouldSkipEnum(metadata.Enum{Name: "mpaa_rating"}, filter) {
t.Error("expected enum mpaa_rating to be skipped")
}
if shouldSkipEnum(metadata.Enum{Name: "status"}, filter) {
t.Error("expected enum status to be generated")
}
}
Loading