package main //go:generate sh -c "printf 'package main\n\nconst version = \"'%s'\"\n' $(git describe --tags --abbrev=0) > version.go" import ( "flag" "fmt" "github.com/go-jet/jet/v2/internal/utils/errfmt" "github.com/go-jet/jet/v2/internal/utils/strslice" "os" "strings" "github.com/go-jet/jet/v2/generator/metadata" sqlitegen "github.com/go-jet/jet/v2/generator/sqlite" "github.com/go-jet/jet/v2/generator/template" "github.com/go-jet/jet/v2/internal/jet" "github.com/go-jet/jet/v2/mysql" postgres2 "github.com/go-jet/jet/v2/postgres" "github.com/go-jet/jet/v2/sqlite" mysqlgen "github.com/go-jet/jet/v2/generator/mysql" postgresgen "github.com/go-jet/jet/v2/generator/postgres" _ "github.com/go-sql-driver/mysql" _ "github.com/lib/pq" _ "github.com/mattn/go-sqlite3" ) var ( source string dsn string host string port int user string password string sslmode string params string dbName string schemaName string ignoreTables string ignoreViews string ignoreEnums string destDir string modelPkg string tablePkg string viewPkg string enumPkg string ) func init() { flag.StringVar(&source, "source", "", "Database system name (postgres, mysql, cockroachdb, mariadb or sqlite)") flag.StringVar(&dsn, "dsn", "", `Data source name. Unified format for connecting to database. PostgreSQL: https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING Example: postgresql://user:pass@localhost:5432/dbname MySQL: https://dev.mysql.com/doc/refman/8.0/en/connecting-using-uri-or-key-value-pairs.html Example: mysql://jet:jet@tcp(localhost:3306)/dvds SQLite: https://www.sqlite.org/c3ref/open.html#urifilenameexamples Example: file://path/to/database/file`) flag.StringVar(&host, "host", "", "Database host path. Used only if dsn is not set. (Example: localhost)") flag.IntVar(&port, "port", 0, "Database port. Used only if dsn is not set.") flag.StringVar(&user, "user", "", "Database user. Used only if dsn is not set.") flag.StringVar(&password, "password", "", "The user’s password. Used only if dsn is not set.") flag.StringVar(&dbName, "dbname", "", "Database name. Used only if dsn is not set.") flag.StringVar(&schemaName, "schema", "public", `Database schema name. (default "public")(PostgreSQL only)`) flag.StringVar(¶ms, "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(&destDir, "path", "", "Destination directory for files generated.") flag.StringVar(&modelPkg, "model-pkg", "model", "Relative path for the Model files package from the destination directory.") flag.StringVar(&tablePkg, "table-pkg", "table", "Relative path for the Table files package from the destination directory.") flag.StringVar(&viewPkg, "view-pkg", "view", "Relative path for the View files package from the destination directory.") flag.StringVar(&enumPkg, "enum-pkg", "enum", "Relative path for the Enum files package from the destination directory.") } func main() { flag.Usage = usage flag.Parse() if dsn == "" && (source == "" || host == "" || port == 0 || user == "" || dbName == "") { printErrorAndExit("ERROR: required flag(s) missing") } source := getSource() ignoreTablesList := parseList(ignoreTables) ignoreViewsList := parseList(ignoreViews) ignoreEnumsList := parseList(ignoreEnums) var err error switch source { case "postgresql", "postgres", "cockroachdb", "cockroach": generatorTemplate := genTemplate(postgres2.Dialect, ignoreTablesList, ignoreViewsList, ignoreEnumsList) if dsn != "" { err = postgresgen.GenerateDSN(dsn, schemaName, destDir, generatorTemplate) break } dbConn := postgresgen.DBConnection{ Host: host, Port: port, User: user, Password: password, SslMode: sslmode, Params: params, DBName: dbName, SchemaName: schemaName, } err = postgresgen.Generate( destDir, dbConn, generatorTemplate, ) case "mysql", "mysqlx", "mariadb": generatorTemplate := genTemplate(mysql.Dialect, ignoreTablesList, ignoreViewsList, ignoreEnumsList) if dsn != "" { err = mysqlgen.GenerateDSN(dsn, destDir, generatorTemplate) break } dbConn := mysqlgen.DBConnection{ Host: host, Port: port, User: user, Password: password, Params: params, DBName: dbName, } err = mysqlgen.Generate( destDir, dbConn, generatorTemplate, ) case "sqlite": if dsn == "" { printErrorAndExit("ERROR: required -dsn flag missing.") } err = sqlitegen.GenerateDSN( dsn, destDir, genTemplate(sqlite.Dialect, ignoreTablesList, ignoreViewsList, ignoreEnumsList), ) case "": printErrorAndExit("ERROR: required -source or -dsn flag missing.") default: printErrorAndExit("ERROR: unknown data source " + source + ". Only postgres, mysql, mariadb and sqlite are supported.") } if err != nil { fmt.Println(errfmt.Trace(err)) os.Exit(2) } } func usage() { fmt.Println("Jet generator", version) fmt.Println() fmt.Println("Usage:") order := []string{ "source", "dsn", "host", "port", "user", "password", "dbname", "schema", "params", "sslmode", "path", "ignore-tables", "ignore-views", "ignore-enums", "model-pkg", "table-pkg", "view-pkg", "enum-pkg", } for _, name := range order { flagEntry := flag.CommandLine.Lookup(name) fmt.Printf(" -%s\n", flagEntry.Name) fmt.Printf("\t%s\n", flagEntry.Usage) } fmt.Println() fmt.Println(`Example commands: $ jet -dsn=postgresql://jet:jet@localhost:5432/jetdb?sslmode=disable -schema=dvds -path=./gen $ jet -dsn=postgres://jet:jet@localhost:26257/jetdb?sslmode=disable -schema=dvds -path=./gen #cockroachdb $ jet -source=postgres -dsn="user=jet password=jet host=localhost port=5432 dbname=jetdb" -schema=dvds -path=./gen $ jet -source=mysql -host=localhost -port=3306 -user=jet -password=jet -dbname=jetdb -path=./gen $ jet -source=sqlite -dsn="file://path/to/sqlite/database/file" -path=./gen $ jet -source=sqlite -dsn="file://path/to/sqlite/database/file" -path=./gen -model-pkg=./entity `) } func printErrorAndExit(error string) { fmt.Println("\n", error) fmt.Println() flag.Usage() os.Exit(1) } func getSource() string { if source != "" { return strings.TrimSpace(strings.ToLower(source)) } return detectSchema(dsn) } func detectSchema(dsn string) string { match := strings.SplitN(dsn, "://", 2) if len(match) < 2 { // not found return "" } protocol := match[0] if protocol == "file" { return "sqlite" } return strings.ToLower(match[0]) } func parseList(list string) []string { ret := strings.Split(list, ",") for i := 0; i < len(ret); i++ { ret[i] = strings.ToLower(strings.TrimSpace(ret[i])) } return ret } func genTemplate(dialect jet.Dialect, ignoreTables []string, ignoreViews []string, ignoreEnums []string) template.Template { shouldSkipTable := func(table metadata.Table) bool { return strslice.Contains(ignoreTables, strings.ToLower(table.Name)) } shouldSkipView := func(view metadata.Table) bool { return strslice.Contains(ignoreViews, strings.ToLower(view.Name)) } shouldSkipEnum := func(enum metadata.Enum) bool { return strslice.Contains(ignoreEnums, strings.ToLower(enum.Name)) } return template.Default(dialect). UseSchema(func(schemaMetaData metadata.Schema) template.Schema { return template.DefaultSchema(schemaMetaData). UseModel(template.DefaultModel().UsePath(modelPkg). UseTable(func(table metadata.Table) template.TableModel { if shouldSkipTable(table) { return template.TableModel{Skip: true} } return template.DefaultTableModel(table) }). UseView(func(view metadata.Table) template.ViewModel { if shouldSkipView(view) { return template.ViewModel{Skip: true} } return template.DefaultViewModel(view) }). UseEnum(func(enum metadata.Enum) template.EnumModel { if shouldSkipEnum(enum) { return template.EnumModel{Skip: true} } return template.DefaultEnumModel(enum) }), ). UseSQLBuilder(template.DefaultSQLBuilder(). UseTable(func(table metadata.Table) template.TableSQLBuilder { if shouldSkipTable(table) { return template.TableSQLBuilder{Skip: true} } return template.DefaultTableSQLBuilder(table).UsePath(tablePkg) }). UseView(func(table metadata.Table) template.ViewSQLBuilder { if shouldSkipView(table) { return template.ViewSQLBuilder{Skip: true} } return template.DefaultViewSQLBuilder(table).UsePath(viewPkg) }). UseEnum(func(enum metadata.Enum) template.EnumSQLBuilder { if shouldSkipEnum(enum) { return template.EnumSQLBuilder{Skip: true} } return template.DefaultEnumSQLBuilder(enum).UsePath(enumPkg) }), ) }) }