Add struct tags flag

This commit is contained in:
Patrick O'brien 2016-09-04 23:44:54 +10:00
parent e35ecd76c1
commit 4e8191b8dd
7 changed files with 116 additions and 10 deletions

View file

@ -7,6 +7,7 @@ type Config struct {
OutFolder string
BaseDir string
ExcludeTables []string
Tags []string
Debug bool
NoTests bool
NoHooks bool

11
main.go
View file

@ -64,6 +64,7 @@ func main() {
rootCmd.PersistentFlags().StringP("pkgname", "p", "models", "The name you wish to assign to your generated package")
rootCmd.PersistentFlags().StringP("basedir", "b", "", "The base directory has the templates and templates_test folders")
rootCmd.PersistentFlags().StringSliceP("exclude", "x", nil, "Tables to be excluded from the generated package")
rootCmd.PersistentFlags().StringSliceP("tag", "t", nil, "Struct tags to be included on your models in addition to json, yaml, toml")
rootCmd.PersistentFlags().BoolP("debug", "d", false, "Debug mode prints stack traces on error")
rootCmd.PersistentFlags().BoolP("no-tests", "", false, "Disable generated go test files")
rootCmd.PersistentFlags().BoolP("no-hooks", "", false, "Disable hooks feature for your models")
@ -114,7 +115,7 @@ func preRun(cmd *cobra.Command, args []string) error {
}
// BUG: https://github.com/spf13/viper/issues/200
// Look up the value of ExcludeTables directly from PFlags in Cobra if we
// Look up the value of ExcludeTables & Tags directly from PFlags in Cobra if we
// detect a malformed value coming out of viper.
// Once the bug is fixed we'll be able to move this into the init above
cmdConfig.ExcludeTables = viper.GetStringSlice("exclude")
@ -125,6 +126,14 @@ func preRun(cmd *cobra.Command, args []string) error {
}
}
cmdConfig.Tags = viper.GetStringSlice("tag")
if len(cmdConfig.Tags) == 1 && strings.HasPrefix(cmdConfig.Tags[0], "[") {
cmdConfig.Tags, err = cmd.PersistentFlags().GetStringSlice("tag")
if err != nil {
return err
}
}
if viper.IsSet("postgres.dbname") {
cmdConfig.Postgres = PostgresConfig{
User: viper.GetString("postgres.user"),

View file

@ -8,6 +8,7 @@ import (
"go/build"
"os"
"path/filepath"
"regexp"
"strings"
"text/template"
@ -82,6 +83,11 @@ func New(config *Config) (*State, error) {
return nil, errors.Wrap(err, "unable to initialize templates")
}
err = s.initTags(config.Tags)
if err != nil {
return nil, errors.Wrap(err, "unable to initialize struct tags")
}
return s, nil
}
@ -126,6 +132,7 @@ func (s *State) Run(includeTests bool) error {
PkgName: s.Config.PkgName,
NoHooks: s.Config.NoHooks,
NoAutoTimestamps: s.Config.NoAutoTimestamps,
Tags: s.Config.Tags,
StringFuncs: templateStringMappers,
}
@ -250,6 +257,22 @@ func (s *State) initTables(exclude []string) error {
return nil
}
// Tags must be in a format like: json, xml, etc.
var rgxValidTag = regexp.MustCompile(`[a-zA-Z_\.]+`)
// initTags removes duplicate tags and validates the format
// of all user tags are simple strings without quotes: [a-zA-Z_\.]+
func (s *State) initTags(tags []string) error {
s.Config.Tags = removeDuplicates(s.Config.Tags)
for _, v := range s.Config.Tags {
if !rgxValidTag.MatchString(v) {
return errors.New("Invalid tag format %q supplied, only specify name, eg: xml")
}
}
return nil
}
// initOutFolder creates the folder that will hold the generated output.
func (s *State) initOutFolder() error {
return os.MkdirAll(s.Config.OutFolder, os.ModePerm)

View file

@ -487,3 +487,35 @@ func ContainsAny(a []string, finds ...string) bool {
return false
}
// GenerateTags converts a slice of tag strings into tags that
// can be passed onto the end of a struct, for example:
// tags: ["xml", "db"] convert to: xml:"column_name" db:"column_name"
func GenerateTags(tags []string, columnName string) string {
buf := GetBuffer()
defer PutBuffer(buf)
for _, tag := range tags {
buf.WriteString(tag)
buf.WriteString(`:"`)
buf.WriteString(columnName)
buf.WriteString(`" `)
}
return buf.String()
}
// GenerateIgnoreTags converts a slice of tag strings into
// ignore tags that can be passed onto the end of a struct, for example:
// tags: ["xml", "db"] convert to: xml:"-" db:"-"
func GenerateIgnoreTags(tags []string) string {
buf := GetBuffer()
defer PutBuffer(buf)
for _, tag := range tags {
buf.WriteString(tag)
buf.WriteString(`:"-" `)
}
return buf.String()
}

View file

@ -426,3 +426,41 @@ func TestContainsAny(t *testing.T) {
t.Errorf("Should not return true")
}
}
func TestGenerateTags(t *testing.T) {
tags := GenerateTags([]string{}, "col_name")
if tags != "" {
t.Errorf("Expected empty string, got %s", tags)
}
tags = GenerateTags([]string{"xml"}, "col_name")
exp := `xml:"col_name" `
if tags != exp {
t.Errorf("expected %s, got %s", exp, tags)
}
tags = GenerateTags([]string{"xml", "db"}, "col_name")
exp = `xml:"col_name" db:"col_name" `
if tags != exp {
t.Errorf("expected %s, got %s", exp, tags)
}
}
func TestGenerateIgnoreTags(t *testing.T) {
tags := GenerateIgnoreTags([]string{})
if tags != "" {
t.Errorf("Expected empty string, got %s", tags)
}
tags = GenerateIgnoreTags([]string{"xml"})
exp := `xml:"-" `
if tags != exp {
t.Errorf("expected %s, got %s", exp, tags)
}
tags = GenerateIgnoreTags([]string{"xml", "db"})
exp = `xml:"-" db:"-" `
if tags != exp {
t.Errorf("expected %s, got %s", exp, tags)
}
}

View file

@ -20,6 +20,7 @@ type templateData struct {
PkgName string
NoHooks bool
NoAutoTimestamps bool
Tags []string
StringFuncs map[string]func(string) string
}
@ -124,11 +125,13 @@ var templateFunctions = template.FuncMap{
"camelCase": strmangle.CamelCase,
// String Slice ops
"join": func(sep string, slice []string) string { return strings.Join(slice, sep) },
"joinSlices": strmangle.JoinSlices,
"stringMap": strmangle.StringMap,
"prefixStringSlice": strmangle.PrefixStringSlice,
"containsAny": strmangle.ContainsAny,
"join": func(sep string, slice []string) string { return strings.Join(slice, sep) },
"joinSlices": strmangle.JoinSlices,
"stringMap": strmangle.StringMap,
"prefixStringSlice": strmangle.PrefixStringSlice,
"containsAny": strmangle.ContainsAny,
"generateTags": strmangle.GenerateTags,
"generateIgnoreTags": strmangle.GenerateIgnoreTags,
// String Map ops
"makeStringMap": strmangle.MakeStringMap,

View file

@ -2,22 +2,22 @@
{{.Function.Name}} *{{.ForeignTable.NameGo}}
{{- end -}}
{{- $dot := . -}}
{{- $tableNameSingular := .Table.Name | singular -}}
{{- $modelName := $tableNameSingular | titleCase -}}
{{- $modelNameCamel := $tableNameSingular | camelCase -}}
// {{$modelName}} is an object representing the database table.
type {{$modelName}} struct {
{{range $column := .Table.Columns -}}
{{titleCase $column.Name}} {{$column.Type}} `boil:"{{$column.Name}}" json:"{{$column.Name}}{{if $column.Nullable}},omitempty{{end}}" toml:"{{$column.Name}}" yaml:"{{$column.Name}}{{if $column.Nullable}},omitempty{{end}}"`
{{titleCase $column.Name}} {{$column.Type}} `{{generateTags $dot.Tags $column.Name}}boil:"{{$column.Name}}" json:"{{$column.Name}}{{if $column.Nullable}},omitempty{{end}}" toml:"{{$column.Name}}" yaml:"{{$column.Name}}{{if $column.Nullable}},omitempty{{end}}"`
{{end -}}
{{- if .Table.IsJoinTable -}}
{{- else}}
R *{{$modelNameCamel}}R `boil:"-" json:"-" toml:"-" yaml:"-"`
L {{$modelNameCamel}}L `boil:"-" json:"-" toml:"-" yaml:"-"`
R *{{$modelNameCamel}}R `{{generateIgnoreTags $dot.Tags}}boil:"-" json:"-" toml:"-" yaml:"-"`
L {{$modelNameCamel}}L `{{generateIgnoreTags $dot.Tags}}boil:"-" json:"-" toml:"-" yaml:"-"`
{{end -}}
}
{{- $dot := . -}}
{{- if .Table.IsJoinTable -}}
{{- else}}
// {{$modelNameCamel}}R is where relationships are stored.