2016-02-23 09:27:32 +01:00
|
|
|
package cmds
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2016-02-29 04:30:54 +01:00
|
|
|
"fmt"
|
2016-02-24 06:40:07 +01:00
|
|
|
"os"
|
2016-02-29 04:30:54 +01:00
|
|
|
"path/filepath"
|
2016-02-29 12:45:28 +01:00
|
|
|
"sort"
|
2016-02-23 09:27:32 +01:00
|
|
|
"strings"
|
2016-02-29 04:30:54 +01:00
|
|
|
"text/template"
|
2016-02-23 09:27:32 +01:00
|
|
|
|
|
|
|
"github.com/pobri19/sqlboiler/dbdrivers"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
)
|
|
|
|
|
2016-03-02 05:05:25 +01:00
|
|
|
const (
|
|
|
|
templatesDirectory = "/cmds/templates"
|
|
|
|
)
|
|
|
|
|
2016-02-24 09:53:34 +01:00
|
|
|
// cmdData is used globally by all commands to access the table schema data,
|
|
|
|
// the database driver and the output file. cmdData is initialized by
|
|
|
|
// the root SQLBoiler cobra command at run time, before other commands execute.
|
2016-02-23 09:27:32 +01:00
|
|
|
var cmdData *CmdData
|
|
|
|
|
2016-02-29 04:30:54 +01:00
|
|
|
// templates holds a slice of pointers to all templates in the templates directory.
|
|
|
|
var templates []*template.Template
|
|
|
|
|
2016-02-29 10:39:49 +01:00
|
|
|
// SQLBoiler is the root app command
|
|
|
|
var SQLBoiler = &cobra.Command{
|
|
|
|
Use: "sqlboiler",
|
|
|
|
Short: "SQL Boiler generates boilerplate structs and statements",
|
|
|
|
Long: "SQL Boiler generates boilerplate structs and statements.\n" +
|
|
|
|
`Complete documentation is available at http://github.com/pobri19/sqlboiler`,
|
|
|
|
}
|
|
|
|
|
2016-02-24 09:53:34 +01:00
|
|
|
// init initializes the sqlboiler flags, such as driver, table, and output file.
|
|
|
|
// It also sets the global preRun hook and postRun hook. Every command will execute
|
|
|
|
// these hooks before and after running to initialize the shared state.
|
2016-02-23 09:27:32 +01:00
|
|
|
func init() {
|
2016-02-25 01:20:39 +01:00
|
|
|
SQLBoiler.PersistentFlags().StringP("driver", "d", "", "The name of the driver in your config.toml (mandatory)")
|
2016-02-23 09:27:32 +01:00
|
|
|
SQLBoiler.PersistentFlags().StringP("table", "t", "", "A comma seperated list of table names")
|
2016-03-01 15:20:13 +01:00
|
|
|
SQLBoiler.PersistentFlags().StringP("folder", "f", "", "The name of the output folder. If not specified will output to stdout")
|
|
|
|
SQLBoiler.PersistentFlags().StringP("pkgname", "p", "model", "The name you wish to assign to your generated package")
|
2016-02-23 09:27:32 +01:00
|
|
|
SQLBoiler.PersistentPreRun = sqlBoilerPreRun
|
2016-02-24 06:40:07 +01:00
|
|
|
SQLBoiler.PersistentPostRun = sqlBoilerPostRun
|
2016-02-23 09:27:32 +01:00
|
|
|
|
2016-02-29 10:39:49 +01:00
|
|
|
// Initialize the SQLBoiler commands and hook the custom Run functions
|
|
|
|
initCommands(SQLBoiler, sqlBoilerCommands, sqlBoilerCommandRuns)
|
2016-02-23 09:27:32 +01:00
|
|
|
}
|
|
|
|
|
2016-02-24 09:53:34 +01:00
|
|
|
// sqlBoilerPostRun cleans up the output file and database connection once
|
|
|
|
// all commands are finished running.
|
2016-02-24 06:40:07 +01:00
|
|
|
func sqlBoilerPostRun(cmd *cobra.Command, args []string) {
|
|
|
|
cmdData.DBDriver.Close()
|
|
|
|
}
|
|
|
|
|
2016-02-24 09:53:34 +01:00
|
|
|
// sqlBoilerPreRun executes before all commands start running. Its job is to
|
|
|
|
// initialize the shared state object (cmdData). Initialization tasks include
|
|
|
|
// assigning the database driver based off the driver flag and opening the database connection,
|
|
|
|
// retrieving a list of all the tables in the database (if specific tables are not provided
|
|
|
|
// via a flag), building the table schema for use in the templates, and opening the output file
|
|
|
|
// handle if one is specified with a flag.
|
2016-02-23 09:27:32 +01:00
|
|
|
func sqlBoilerPreRun(cmd *cobra.Command, args []string) {
|
|
|
|
var err error
|
|
|
|
cmdData = &CmdData{}
|
|
|
|
|
2016-02-24 09:53:34 +01:00
|
|
|
// Initialize the cmdData.DBDriver
|
|
|
|
initDBDriver()
|
|
|
|
|
|
|
|
// Connect to the driver database
|
|
|
|
if err = cmdData.DBDriver.Open(); err != nil {
|
2016-02-29 06:49:26 +01:00
|
|
|
errorQuit(fmt.Errorf("Unable to connect to the database: %s", err))
|
2016-02-24 09:53:34 +01:00
|
|
|
}
|
|
|
|
|
2016-03-02 05:05:25 +01:00
|
|
|
// Initialize the cmdData.Tables
|
|
|
|
initTables()
|
2016-02-24 09:53:34 +01:00
|
|
|
|
2016-03-02 05:05:25 +01:00
|
|
|
// Initialize the cmdData.Columns
|
|
|
|
initColumns()
|
2016-02-24 09:53:34 +01:00
|
|
|
|
2016-03-01 15:20:13 +01:00
|
|
|
// Initialize the package name
|
|
|
|
initPkgName()
|
|
|
|
|
2016-02-24 09:53:34 +01:00
|
|
|
// Initialize the cmdData.OutFile
|
2016-03-01 15:20:13 +01:00
|
|
|
initOutFolder()
|
2016-02-29 04:30:54 +01:00
|
|
|
|
|
|
|
// Initialize the templates
|
2016-03-02 05:05:25 +01:00
|
|
|
templates, err = initTemplates(templatesDirectory)
|
2016-02-29 04:30:54 +01:00
|
|
|
if err != nil {
|
|
|
|
errorQuit(fmt.Errorf("Unable to initialize templates: %s", err))
|
|
|
|
}
|
2016-02-24 09:53:34 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// initDBDriver attempts to set the cmdData DBDriver based off the passed in
|
|
|
|
// driver flag value. If an invalid flag string is provided the program will exit.
|
|
|
|
func initDBDriver() {
|
2016-02-23 09:27:32 +01:00
|
|
|
// Retrieve driver flag
|
|
|
|
driverName := SQLBoiler.PersistentFlags().Lookup("driver").Value.String()
|
|
|
|
if driverName == "" {
|
|
|
|
errorQuit(errors.New("Must supply a driver flag."))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a driver based off driver flag
|
|
|
|
switch driverName {
|
|
|
|
case "postgres":
|
|
|
|
cmdData.DBDriver = dbdrivers.NewPostgresDriver(
|
|
|
|
cfg.Postgres.User,
|
|
|
|
cfg.Postgres.Pass,
|
|
|
|
cfg.Postgres.DBName,
|
|
|
|
cfg.Postgres.Host,
|
|
|
|
cfg.Postgres.Port,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2016-02-24 09:53:34 +01:00
|
|
|
if cmdData.DBDriver == nil {
|
|
|
|
errorQuit(errors.New("An invalid driver name was provided"))
|
2016-02-23 09:27:32 +01:00
|
|
|
}
|
2016-02-24 09:53:34 +01:00
|
|
|
}
|
2016-02-23 09:27:32 +01:00
|
|
|
|
2016-03-02 05:05:25 +01:00
|
|
|
// initTables will create a string slice out of the passed in table flag value
|
2016-02-24 09:53:34 +01:00
|
|
|
// if one is provided. If no flag is provided, it will attempt to connect to the
|
|
|
|
// database to retrieve all "public" table names, and build a slice out of that result.
|
2016-03-02 05:05:25 +01:00
|
|
|
func initTables() {
|
2016-02-23 09:27:32 +01:00
|
|
|
// Retrieve the list of tables
|
|
|
|
tn := SQLBoiler.PersistentFlags().Lookup("table").Value.String()
|
|
|
|
|
|
|
|
if len(tn) != 0 {
|
2016-03-02 05:05:25 +01:00
|
|
|
cmdData.Tables = strings.Split(tn, ",")
|
|
|
|
for i, name := range cmdData.Tables {
|
|
|
|
cmdData.Tables[i] = strings.TrimSpace(name)
|
2016-02-23 09:27:32 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If no table names are provided attempt to process all tables in database
|
2016-03-02 05:05:25 +01:00
|
|
|
if len(cmdData.Tables) == 0 {
|
2016-02-23 09:27:32 +01:00
|
|
|
// get all table names
|
2016-02-24 09:53:34 +01:00
|
|
|
var err error
|
2016-03-02 05:05:25 +01:00
|
|
|
cmdData.Tables, err = cmdData.DBDriver.GetAllTables()
|
2016-02-23 09:27:32 +01:00
|
|
|
if err != nil {
|
2016-02-29 06:49:26 +01:00
|
|
|
errorQuit(fmt.Errorf("Unable to get all table names: %s", err))
|
2016-02-23 09:27:32 +01:00
|
|
|
}
|
|
|
|
|
2016-03-02 05:05:25 +01:00
|
|
|
if len(cmdData.Tables) == 0 {
|
2016-02-23 09:27:32 +01:00
|
|
|
errorQuit(errors.New("No tables found in database, migrate some tables first"))
|
|
|
|
}
|
|
|
|
}
|
2016-02-24 09:53:34 +01:00
|
|
|
}
|
2016-02-23 09:27:32 +01:00
|
|
|
|
2016-03-02 05:05:25 +01:00
|
|
|
// initColumns builds a description of each table (column name, column type)
|
|
|
|
// and assigns it to cmdData.Columns, the slice of dbdrivers.DBColumn slices.
|
|
|
|
func initColumns() {
|
|
|
|
// loop over table Names and build Columns
|
|
|
|
for i := 0; i < len(cmdData.Tables); i++ {
|
|
|
|
tInfo, err := cmdData.DBDriver.GetTableInfo(cmdData.Tables[i])
|
2016-02-23 09:27:32 +01:00
|
|
|
if err != nil {
|
2016-02-29 06:49:26 +01:00
|
|
|
errorQuit(fmt.Errorf("Unable to get the table info: %s", err))
|
2016-02-23 09:27:32 +01:00
|
|
|
}
|
|
|
|
|
2016-03-02 05:05:25 +01:00
|
|
|
cmdData.Columns = append(cmdData.Columns, tInfo)
|
2016-02-23 09:27:32 +01:00
|
|
|
}
|
2016-02-24 09:53:34 +01:00
|
|
|
}
|
2016-02-24 06:40:07 +01:00
|
|
|
|
2016-03-01 15:20:13 +01:00
|
|
|
// Initialize the package name provided by the flag
|
|
|
|
func initPkgName() {
|
|
|
|
cmdData.PkgName = SQLBoiler.PersistentFlags().Lookup("pkgname").Value.String()
|
|
|
|
}
|
|
|
|
|
2016-02-24 09:53:34 +01:00
|
|
|
// initOutFile opens a file handle to the file name specified by the out flag.
|
|
|
|
// If no file name is provided, out will remain nil and future output will be
|
|
|
|
// piped to Stdout instead of to a file.
|
2016-03-01 15:20:13 +01:00
|
|
|
func initOutFolder() {
|
2016-02-24 06:40:07 +01:00
|
|
|
// open the out file filehandle
|
2016-03-01 15:20:13 +01:00
|
|
|
cmdData.OutFolder = SQLBoiler.PersistentFlags().Lookup("folder").Value.String()
|
|
|
|
if cmdData.OutFolder == "" {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.MkdirAll(cmdData.OutFolder, os.ModePerm); err != nil {
|
|
|
|
errorQuit(fmt.Errorf("Unable to make output folder: %s", err))
|
2016-02-24 06:40:07 +01:00
|
|
|
}
|
2016-02-23 09:27:32 +01:00
|
|
|
}
|
2016-02-29 04:30:54 +01:00
|
|
|
|
2016-02-29 11:42:51 +01:00
|
|
|
// initTemplates loads all of the template files in the /cmds/templates directory
|
|
|
|
// and returns a slice of pointers to these templates.
|
2016-03-02 05:05:25 +01:00
|
|
|
func initTemplates(dir string) ([]*template.Template, error) {
|
2016-02-29 04:30:54 +01:00
|
|
|
wd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-03-02 05:05:25 +01:00
|
|
|
pattern := filepath.Join(wd, dir, "*.tpl")
|
2016-02-29 06:49:26 +01:00
|
|
|
tpl, err := template.New("").Funcs(sqlBoilerTemplateFuncs).ParseGlob(pattern)
|
2016-02-29 04:30:54 +01:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return tpl.Templates(), err
|
|
|
|
}
|
2016-02-29 06:49:26 +01:00
|
|
|
|
2016-02-29 11:42:51 +01:00
|
|
|
// initCommands loads all of the commands in the sqlBoilerCommands and hooks their run functions.
|
2016-02-29 10:39:49 +01:00
|
|
|
func initCommands(rootCmd *cobra.Command, commands map[string]*cobra.Command, commandRuns map[string]CobraRunFunc) {
|
2016-02-29 12:45:28 +01:00
|
|
|
var commandNames []string
|
|
|
|
|
|
|
|
// Build a list of command names to alphabetically sort them for ordered loading.
|
2016-02-29 06:49:26 +01:00
|
|
|
for _, c := range commands {
|
2016-02-29 12:45:28 +01:00
|
|
|
// Skip the boil command load, we do it manually below.
|
|
|
|
if c.Name() == "boil" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
commandNames = append(commandNames, c.Name())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the "boil" command first, and manually. It should be at the top of the help file.
|
|
|
|
commands["boil"].Run = commandRuns["boil"]
|
|
|
|
rootCmd.AddCommand(commands["boil"])
|
|
|
|
|
|
|
|
// Load commands alphabetically. This ensures proper order of help file.
|
|
|
|
sort.Strings(commandNames)
|
|
|
|
|
|
|
|
// Loop every command name, load it and hook it to its Run handler
|
|
|
|
for _, c := range commandNames {
|
2016-02-29 06:49:26 +01:00
|
|
|
// If there is a commandRun for the command (matched by name)
|
|
|
|
// then set the Run hook
|
2016-02-29 12:45:28 +01:00
|
|
|
r, ok := commandRuns[c]
|
2016-02-29 06:49:26 +01:00
|
|
|
if ok {
|
2016-02-29 12:45:28 +01:00
|
|
|
commands[c].Run = r
|
2016-02-29 10:39:49 +01:00
|
|
|
} else {
|
2016-02-29 12:45:28 +01:00
|
|
|
commands[c].Run = defaultRun // Load default run if no custom run is found
|
2016-02-29 06:49:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add the command
|
2016-02-29 12:45:28 +01:00
|
|
|
rootCmd.AddCommand(commands[c])
|
2016-02-29 06:49:26 +01:00
|
|
|
}
|
|
|
|
}
|