diff --git a/cmd/diff.go b/cmd/diff.go index 8786b109..6d7bed9c 100644 --- a/cmd/diff.go +++ b/cmd/diff.go @@ -27,6 +27,7 @@ import ( "github.com/k1LoW/tbls/config" "github.com/k1LoW/tbls/datasource" "github.com/k1LoW/tbls/output/md" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -47,7 +48,13 @@ var diffCmd = &cobra.Command{ configPath = additionalDataPath } - err = c.Load(configPath, args) + options, err := loadDiffArgs(args) + if err != nil { + printError(err) + os.Exit(1) + } + + err = c.Load(configPath, options...) if err != nil { printError(err) os.Exit(1) @@ -65,7 +72,7 @@ var diffCmd = &cobra.Command{ os.Exit(1) } - if sort { + if c.Format.Sort { err = s.Sort() if err != nil { printError(err) @@ -73,7 +80,7 @@ var diffCmd = &cobra.Command{ } } - diff, err := md.Diff(s, c.DocPath, adjust, erFormat) + diff, err := md.Diff(s, c) if err != nil { printError(err) os.Exit(2) @@ -85,6 +92,24 @@ var diffCmd = &cobra.Command{ }, } +func loadDiffArgs(args []string) ([]config.Option, error) { + options := []config.Option{} + if len(args) > 2 { + return options, errors.WithStack(errors.New("too many arguments")) + } + options = append(options, config.Adjust(adjust)) + options = append(options, config.Sort(sort)) + options = append(options, config.ERFormat(erFormat)) + if len(args) == 2 { + options = append(options, config.DSN(args[0])) + options = append(options, config.DocPath(args[1])) + } + if len(args) == 1 { + options = append(options, config.DSN(args[0])) + } + return options, nil +} + func init() { rootCmd.AddCommand(diffCmd) diffCmd.Flags().BoolVarP(&sort, "sort", "", false, "sort") diff --git a/cmd/doc.go b/cmd/doc.go index def131a4..6d597ebe 100644 --- a/cmd/doc.go +++ b/cmd/doc.go @@ -57,7 +57,13 @@ var docCmd = &cobra.Command{ configPath = additionalDataPath } - err = c.Load(configPath, args) + options, err := loadDocArgs(args) + if err != nil { + printError(err) + os.Exit(1) + } + + err = c.Load(configPath, options...) if err != nil { printError(err) os.Exit(1) @@ -75,7 +81,7 @@ var docCmd = &cobra.Command{ os.Exit(1) } - if sort { + if c.Format.Sort { err = s.Sort() if err != nil { printError(err) @@ -83,10 +89,10 @@ var docCmd = &cobra.Command{ } } - if !withoutER { + if !c.ER.Skip { _, err = exec.Command("which", "dot").Output() if err == nil { - err := withDot(s, c.DocPath, force) + err := withDot(s, c, force) if err != nil { printError(err) os.Exit(1) @@ -94,7 +100,7 @@ var docCmd = &cobra.Command{ } } - err = md.Output(s, c.DocPath, force, adjust, erFormat) + err = md.Output(s, c, force) if err != nil { printError(err) @@ -103,7 +109,9 @@ var docCmd = &cobra.Command{ }, } -func withDot(s *schema.Schema, outputPath string, force bool) error { +func withDot(s *schema.Schema, c *config.Config, force bool) error { + erFormat := c.ER.Format + outputPath := c.DocPath fullPath, err := filepath.Abs(outputPath) if err != nil { return errors.WithStack(err) @@ -120,9 +128,9 @@ func withDot(s *schema.Schema, outputPath string, force bool) error { fmt.Printf("%s\n", filepath.Join(outputPath, erFileName)) tmpfile, _ := ioutil.TempFile("", "tblstmp") - c := exec.Command("dot", dotFormatOption, "-o", filepath.Join(fullPath, erFileName), tmpfile.Name()) + cmd := exec.Command("dot", dotFormatOption, "-o", filepath.Join(fullPath, erFileName), tmpfile.Name()) var stderr bytes.Buffer - c.Stderr = &stderr + cmd.Stderr = &stderr dot := new(dot.Dot) @@ -137,7 +145,7 @@ func withDot(s *schema.Schema, outputPath string, force bool) error { os.Remove(tmpfile.Name()) return errors.WithStack(err) } - err = c.Run() + err = cmd.Run() if err != nil { os.Remove(tmpfile.Name()) return errors.WithStack(errors.Wrap(err, stderr.String())) @@ -176,6 +184,25 @@ func withDot(s *schema.Schema, outputPath string, force bool) error { return nil } +func loadDocArgs(args []string) ([]config.Option, error) { + options := []config.Option{} + if len(args) > 2 { + return options, errors.WithStack(errors.New("too many arguments")) + } + options = append(options, config.Adjust(adjust)) + options = append(options, config.Sort(sort)) + options = append(options, config.ERFormat(erFormat)) + options = append(options, config.ERSkip(withoutER)) + if len(args) == 2 { + options = append(options, config.DSN(args[0])) + options = append(options, config.DocPath(args[1])) + } + if len(args) == 1 { + options = append(options, config.DSN(args[0])) + } + return options, nil +} + func outputErExists(s *schema.Schema, path string) bool { // schema.png erFileName := fmt.Sprintf("schema.%s", erFormat) @@ -197,7 +224,7 @@ func init() { docCmd.Flags().BoolVarP(&force, "force", "f", false, "force") docCmd.Flags().BoolVarP(&sort, "sort", "", false, "sort") docCmd.Flags().StringVarP(&configPath, "config", "c", "", "config file path") - docCmd.Flags().StringVarP(&erFormat, "er-format", "t", "png", "ER diagrams output format [png, svg, jpg, ...]") + docCmd.Flags().StringVarP(&erFormat, "er-format", "t", "", fmt.Sprintf("ER diagrams output format [png, svg, jpg, ...]. default: %s", config.DefaultERFormat)) docCmd.Flags().BoolVarP(&withoutER, "without-er", "", false, "no generate ER diagrams") docCmd.Flags().BoolVarP(&adjust, "adjust-table", "j", false, "adjust column width of table") docCmd.Flags().StringVarP(&additionalDataPath, "add", "a", "", "additional schema data path (deprecated, use `config`)") diff --git a/cmd/lint.go b/cmd/lint.go index 15ec1e68..397537ce 100644 --- a/cmd/lint.go +++ b/cmd/lint.go @@ -28,6 +28,7 @@ import ( "github.com/k1LoW/tbls/config" "github.com/k1LoW/tbls/datasource" "github.com/labstack/gommon/color" + "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -43,7 +44,13 @@ var lintCmd = &cobra.Command{ os.Exit(1) } - err = c.Load(configPath, args) + options, err := loadLintArgs(args) + if err != nil { + printError(err) + os.Exit(1) + } + + err = c.Load(configPath, options...) if err != nil { printError(err) os.Exit(1) @@ -83,6 +90,21 @@ var lintCmd = &cobra.Command{ }, } +func loadLintArgs(args []string) ([]config.Option, error) { + options := []config.Option{} + if len(args) > 2 { + return options, errors.WithStack(errors.New("too many arguments")) + } + if len(args) == 2 { + options = append(options, config.DSN(args[0])) + options = append(options, config.DocPath(args[1])) + } + if len(args) == 1 { + options = append(options, config.DSN(args[0])) + } + return options, nil +} + func init() { rootCmd.AddCommand(lintCmd) lintCmd.Flags().StringVarP(&configPath, "config", "c", "", "config file path") diff --git a/cmd/out.go b/cmd/out.go index c850c2ba..f7ba406d 100644 --- a/cmd/out.go +++ b/cmd/out.go @@ -62,7 +62,7 @@ var outCmd = &cobra.Command{ configPath = additionalDataPath } - err = c.Load(configPath, args) + err = c.Load(configPath, config.DSN(args[0]), config.Sort(sort)) if err != nil { printError(err) os.Exit(1) @@ -80,7 +80,7 @@ var outCmd = &cobra.Command{ os.Exit(1) } - if sort { + if c.Format.Sort { err = s.Sort() if err != nil { printError(err) @@ -122,6 +122,7 @@ var outCmd = &cobra.Command{ func init() { rootCmd.AddCommand(outCmd) + outCmd.Flags().BoolVarP(&sort, "sort", "", false, "sort") outCmd.Flags().StringVarP(&configPath, "config", "c", "", "config file path") outCmd.Flags().StringVarP(&format, "format", "t", "json", "output format") outCmd.Flags().StringVar(&tableName, "table", "", "table name") diff --git a/config/config.go b/config/config.go index d652d122..928e94d6 100644 --- a/config/config.go +++ b/config/config.go @@ -17,15 +17,32 @@ import ( const defaultConfigFilePath = ".tbls.yml" const defaultDocPath = "dbdoc" +// DefaultERFormat is default ER diagram format +const DefaultERFormat = "png" + // Config is tbls config type Config struct { DSN string `yaml:"dsn"` DocPath string `yaml:"docPath"` + Format Format `yaml:"format"` + ER ER `yaml:"er"` Lint Lint `yaml:"lint"` Relations []AdditionalRelation `yaml:"relations"` Comments []AdditionalComment `yaml:"comments"` } +// Format is document format setting +type Format struct { + Adjust bool `yaml:"adjust"` + Sort bool `yaml:"sort"` +} + +// ER is er diagram setting +type ER struct { + Skip bool `yaml:"skip"` + Format string `yaml:"format"` +} + // AdditionalRelation is the struct for table relation from yaml type AdditionalRelation struct { Table string `yaml:"table"` @@ -42,6 +59,61 @@ type AdditionalComment struct { ColumnComments map[string]string `yaml:"columnComments"` } +// Option function change Config +type Option func(*Config) error + +// DSN return Option set Config.DSN +func DSN(dsn string) Option { + return func(c *Config) error { + c.DSN = dsn + return nil + } +} + +// DocPath return Option set Config.DocPath +func DocPath(docPath string) Option { + return func(c *Config) error { + c.DocPath = docPath + return nil + } +} + +// Adjust return Option set Config.Format.Adjust +func Adjust(adjust bool) Option { + return func(c *Config) error { + if adjust { + c.Format.Adjust = adjust + } + return nil + } +} + +// Sort return Option set Config.Format.Sort +func Sort(sort bool) Option { + return func(c *Config) error { + if sort { + c.Format.Sort = sort + } + return nil + } +} + +// ERSkip return Option set Config.ER.Skip +func ERSkip(skip bool) Option { + return func(c *Config) error { + c.ER.Skip = skip + return nil + } +} + +// ERFormat return Option set Config.ER.Format +func ERFormat(erFormat string) Option { + return func(c *Config) error { + c.ER.Format = erFormat + return nil + } +} + // NewConfig return Config func NewConfig() (*Config, error) { c := Config{ @@ -52,7 +124,7 @@ func NewConfig() (*Config, error) { } // Load load config with all method -func (c *Config) Load(configPath string, args []string) error { +func (c *Config) Load(configPath string, options ...Option) error { err := c.LoadConfigFile(configPath) if err != nil { return err @@ -63,15 +135,21 @@ func (c *Config) Load(configPath string, args []string) error { return err } - err = c.LoadArgs(args) - if err != nil { - return err + for _, option := range options { + err = option(c) + if err != nil { + return err + } } if c.DocPath == "" { c.DocPath = defaultDocPath } + if c.ER.Format == "" { + c.ER.Format = DefaultERFormat + } + return nil } @@ -88,21 +166,6 @@ func (c *Config) LoadEnviron() error { return nil } -// LoadArgs load args slice -func (c *Config) LoadArgs(args []string) error { - if len(args) > 2 { - return errors.WithStack(errors.New("too many arguments")) - } - if len(args) == 2 { - c.DSN = args[0] - c.DocPath = args[1] - } - if len(args) == 1 { - c.DSN = args[0] - } - return nil -} - // LoadConfigFile load config file func (c *Config) LoadConfigFile(path string) error { if path == "" { diff --git a/config/config_test.go b/config/config_test.go index 6f5de162..dcf6db9a 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -14,7 +14,7 @@ func TestLoadDefault(t *testing.T) { if err != nil { t.Fatal(err) } - err = config.Load(configFilepath, []string{}) + err = config.Load(configFilepath) if err != nil { t.Fatal(err) } diff --git a/output/md/md.go b/output/md/md.go index f8612c9c..49af169d 100644 --- a/output/md/md.go +++ b/output/md/md.go @@ -11,6 +11,7 @@ import ( "text/template" "github.com/gobuffalo/packr" + "github.com/k1LoW/tbls/config" "github.com/k1LoW/tbls/schema" "github.com/mattn/go-runewidth" "github.com/pkg/errors" @@ -66,8 +67,12 @@ func (m *Md) OutputTable(wr io.Writer, t *schema.Table) error { } // Output generate markdown files. -func Output(s *schema.Schema, path string, force bool, adjust bool, erFormat string) error { - fullPath, err := filepath.Abs(path) +func Output(s *schema.Schema, c *config.Config, force bool) error { + docPath := c.DocPath + adjust := c.Format.Adjust + erFormat := c.ER.Format + + fullPath, err := filepath.Abs(docPath) if err != nil { return errors.WithStack(err) } @@ -95,7 +100,7 @@ func Output(s *schema.Schema, path string, force bool, adjust bool, erFormat str if err != nil { return errors.WithStack(err) } - fmt.Printf("%s\n", filepath.Join(path, "README.md")) + fmt.Printf("%s\n", filepath.Join(docPath, "README.md")) // tables for _, t := range s.Tables { @@ -117,16 +122,20 @@ func Output(s *schema.Schema, path string, force bool, adjust bool, erFormat str file.Close() return errors.WithStack(err) } - fmt.Printf("%s\n", filepath.Join(path, fmt.Sprintf("%s.md", t.Name))) + fmt.Printf("%s\n", filepath.Join(docPath, fmt.Sprintf("%s.md", t.Name))) file.Close() } return nil } // Diff database and markdown files. -func Diff(s *schema.Schema, path string, adjust bool, erFormat string) (string, error) { +func Diff(s *schema.Schema, c *config.Config) (string, error) { + docPath := c.DocPath + adjust := c.Format.Adjust + erFormat := c.ER.Format + var diff string - fullPath, err := filepath.Abs(path) + fullPath, err := filepath.Abs(docPath) if err != nil { return "", errors.WithStack(err) } @@ -162,7 +171,7 @@ func Diff(s *schema.Schema, path string, adjust bool, erFormat string) (string, result := dmp.DiffCharsToLines(diffs, dc) if len(result) != 1 || result[0].Type != diffmatchpatch.DiffEqual { - diff += fmt.Sprintf("diff [database] %s\n", filepath.Join(path, "README.md")) + diff += fmt.Sprintf("diff [database] %s\n", filepath.Join(docPath, "README.md")) diff += fmt.Sprintln(dmp.DiffPrettyText(result)) } @@ -190,7 +199,7 @@ func Diff(s *schema.Schema, path string, adjust bool, erFormat string) (string, diffs := dmp.DiffMain(da, db, false) result := dmp.DiffCharsToLines(diffs, dc) if len(result) != 1 || result[0].Type != diffmatchpatch.DiffEqual { - diff += fmt.Sprintf("diff %s %s\n", t.Name, filepath.Join(path, fmt.Sprintf("%s.md", t.Name))) + diff += fmt.Sprintf("diff %s %s\n", t.Name, filepath.Join(docPath, fmt.Sprintf("%s.md", t.Name))) diff += fmt.Sprintln(dmp.DiffPrettyText(result)) } } diff --git a/output/md/md_test.go b/output/md/md_test.go index e1243d73..668f9f38 100644 --- a/output/md/md_test.go +++ b/output/md/md_test.go @@ -29,7 +29,12 @@ func TestOutput(t *testing.T) { if err != nil { t.Error(err) } - err = c.LoadConfigFile(filepath.Join(testdataDir(), "md_test_additional_data.yml")) + tempDir, _ := ioutil.TempDir("", "tbls") + force := true + adjust := tt.adjust + erFormat := "png" + defer os.RemoveAll(tempDir) + err = c.Load(filepath.Join(testdataDir(), "md_test_additional_data.yml"), config.DocPath(tempDir), config.Adjust(adjust), config.ERFormat(erFormat)) if err != nil { t.Error(err) } @@ -37,12 +42,7 @@ func TestOutput(t *testing.T) { if err != nil { t.Error(err) } - tempDir, _ := ioutil.TempDir("", "tbls") - force := true - adjust := tt.adjust - erFormat := "png" - defer os.RemoveAll(tempDir) - err = Output(s, tempDir, force, adjust, erFormat) + err = Output(s, c, force) if err != nil { t.Error(err) } @@ -67,7 +67,12 @@ func TestDiff(t *testing.T) { if err != nil { t.Error(err) } - err = c.LoadConfigFile(filepath.Join(testdataDir(), "md_test_additional_data.yml")) + tempDir, _ := ioutil.TempDir("", "tbls") + force := true + adjust := tt.adjust + erFormat := "png" + defer os.RemoveAll(tempDir) + err = c.Load(filepath.Join(testdataDir(), "md_test_additional_data.yml"), config.DocPath(tempDir), config.Adjust(adjust), config.ERFormat(erFormat)) if err != nil { t.Error(err) } @@ -75,17 +80,12 @@ func TestDiff(t *testing.T) { if err != nil { t.Error(err) } - tempDir, _ := ioutil.TempDir("", "tbls") - force := true - adjust := tt.adjust - erFormat := "png" - defer os.RemoveAll(tempDir) - err = Output(s, tempDir, force, adjust, erFormat) + err = Output(s, c, force) if err != nil { t.Error(err) } expected := "" - actual, err := Diff(s, tempDir, adjust, erFormat) + actual, err := Diff(s, c) if err != nil { t.Error(err) }