Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: support different case style for different format #1336

Merged
Merged
Show file tree
Hide file tree
Changes from 3 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
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,28 @@ down_singular = "teamName"
foreign = "Videos"
```


##### Custom Struct Tag Case

Sometimes you might want to customize the case style for different purpose, for example, use camel case for json format and use snake case for yaml,
You may create a section named `[struct-tag-cases]` to define these custom case for each different format:

```toml
[struct-tag-cases]
toml = "snake"
yaml = "camel"
json = "camel"
boil = "alias"
```

By default, the snake case will be used, so you can just setup only few formats:

```toml
[struct-tag-cases]
json = "camel"
```


##### Foreign Keys

You can add foreign keys not defined in the database to your models using the following configuration:
Expand Down
1 change: 1 addition & 0 deletions boilingcore/boilingcore.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ func (s *State) Run() error {
NoBackReferencing: s.Config.NoBackReferencing,
AlwaysWrapErrors: s.Config.AlwaysWrapErrors,
StructTagCasing: s.Config.StructTagCasing,
StructTagCases: s.Config.StructTagCases,
TagIgnore: make(map[string]struct{}),
Tags: s.Config.Tags,
RelationTag: s.Config.RelationTag,
Expand Down
29 changes: 26 additions & 3 deletions boilingcore/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ import (
"github.com/volatiletech/sqlboiler/v4/importers"
)

type TagCase string

const (
TagCaseCamel TagCase = "camel"
TagCaseSnake TagCase = "snake"
TagCaseTitle TagCase = "title"
TagCaseAlias TagCase = "alias"
)

// Config for the running of the commands
type Config struct {
DriverName string `toml:"driver_name,omitempty" json:"driver_name,omitempty"`
Expand All @@ -39,9 +48,16 @@ type Config struct {
NoBackReferencing bool `toml:"no_back_reference,omitempty" json:"no_back_reference,omitempty"`
AlwaysWrapErrors bool `toml:"always_wrap_errors,omitempty" json:"always_wrap_errors,omitempty"`
Wipe bool `toml:"wipe,omitempty" json:"wipe,omitempty"`
StructTagCasing string `toml:"struct_tag_casing,omitempty" json:"struct_tag_casing,omitempty"`
RelationTag string `toml:"relation_tag,omitempty" json:"relation_tag,omitempty"`
TagIgnore []string `toml:"tag_ignore,omitempty" json:"tag_ignore,omitempty"`

StructTagCases StructTagCases `toml:"struct_tag_cases,omitempty" json:"struct_tag_cases,omitempty"`

// StructTagCasing is a legacy config field, which will be migrated to StructTagCases in the future.
// When struct-tag-casing is defined, it will be converted to StructTagCases
// Deprecated: use StructTagCases instead.
StructTagCasing string `toml:"struct_tag_casing,omitempty" json:"struct_tag_casing,omitempty"`

RelationTag string `toml:"relation_tag,omitempty" json:"relation_tag,omitempty"`
TagIgnore []string `toml:"tag_ignore,omitempty" json:"tag_ignore,omitempty"`

Imports importers.Collection `toml:"imports,omitempty" json:"imports,omitempty"`

Expand All @@ -63,6 +79,13 @@ type AutoColumns struct {
Deleted string `toml:"deleted,omitempty" json:"deleted,omitempty"`
}

type StructTagCases struct {
Json TagCase `toml:"json,omitempty" json:"json,omitempty"`
Yaml TagCase `toml:"yaml,omitempty" json:"yaml,omitempty"`
Toml TagCase `toml:"toml,omitempty" json:"toml,omitempty"`
Boil TagCase `toml:"boil,omitempty" json:"boil,omitempty"`
}

// TypeReplace replaces a column type with something else
type TypeReplace struct {
Tables []string `toml:"tables,omitempty" json:"tables,omitempty"`
Expand Down
55 changes: 46 additions & 9 deletions boilingcore/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import (

"github.com/Masterminds/sprig/v3"
"github.com/friendsofgo/errors"
"github.com/volatiletech/sqlboiler/v4/drivers"
"github.com/volatiletech/strmangle"

"github.com/volatiletech/sqlboiler/v4/drivers"
)

// templateData for sqlboiler templates
Expand Down Expand Up @@ -58,8 +59,12 @@ type templateData struct {
RelationTag string

// Generate struct tags as camelCase or snake_case
// Deprecated: use StructTagCases instead.
StructTagCasing string

// Generate struct tags as camelCase or snake_case
StructTagCases StructTagCases

// Contains field names that should have tags values set to '-'
TagIgnore map[string]struct{}

Expand Down Expand Up @@ -133,7 +138,9 @@ func (t templateList) Templates() []string {
return ret
}

func loadTemplates(lazyTemplates []lazyTemplate, testTemplates bool, customFuncs template.FuncMap) (*templateList, error) {
func loadTemplates(
lazyTemplates []lazyTemplate, testTemplates bool, customFuncs template.FuncMap,
) (*templateList, error) {
tpl := template.New("")

for _, t := range lazyTemplates {
Expand Down Expand Up @@ -286,13 +293,14 @@ var templateFunctions = template.FuncMap{
"ignore": strmangle.Ignore,

// 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,
"generateTags": strmangle.GenerateTags,
"generateIgnoreTags": strmangle.GenerateIgnoreTags,
"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,
"generateTagWithCase": generateTagWithCase,
"generateIgnoreTags": strmangle.GenerateIgnoreTags,

// Enum ops
"parseEnumName": strmangle.ParseEnumName,
Expand Down Expand Up @@ -333,3 +341,32 @@ var templateFunctions = template.FuncMap{
"columnDBTypes": drivers.ColumnDBTypes,
"getTable": drivers.GetTable,
}

func generateTagWithCase(tagName, tagValue, alias string, c TagCase, nullable bool) string {
buf := strmangle.GetBuffer()
defer strmangle.PutBuffer(buf)

buf.WriteString(tagName)
buf.WriteString(`:"`)
switch c {
case TagCaseSnake:
// we use snake case by default, so we can simply render the value to the buffer
buf.WriteString(tagValue)
case TagCaseTitle:
buf.WriteString(strmangle.TitleCase(tagValue))
case TagCaseCamel:
buf.WriteString(strmangle.CamelCase(tagValue))
case TagCaseAlias:
buf.WriteString(alias)
default:
buf.WriteString(tagValue)
}

if nullable {
buf.WriteString(",omitempty")
}

buf.WriteString(`" `)

return buf.String()
}
35 changes: 28 additions & 7 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,20 @@ func preRun(cmd *cobra.Command, args []string) error {
AlwaysWrapErrors: viper.GetBool("always-wrap-errors"),
Wipe: viper.GetBool("wipe"),
StructTagCasing: strings.ToLower(viper.GetString("struct-tag-casing")), // camel | snake | title
TagIgnore: viper.GetStringSlice("tag-ignore"),
RelationTag: viper.GetString("relation-tag"),
TemplateDirs: viper.GetStringSlice("templates"),
Tags: viper.GetStringSlice("tag"),
Replacements: viper.GetStringSlice("replace"),
Aliases: boilingcore.ConvertAliases(viper.Get("aliases")),
TypeReplaces: boilingcore.ConvertTypeReplace(viper.Get("types")),
StructTagCases: boilingcore.StructTagCases{
// make this compatible with the legacy struct-tag-casing config
Json: withDefaultCase(viper.GetString("struct-tag-cases.json"), viper.GetString("struct-tag-casing")),
Yaml: withDefaultCase(viper.GetString("struct-tag-cases.yaml"), viper.GetString("struct-tag-casing")),
Toml: withDefaultCase(viper.GetString("struct-tag-cases.toml"), viper.GetString("struct-tag-casing")),
Boil: withDefaultCase(viper.GetString("struct-tag-cases.boil"), viper.GetString("struct-tag-casing")),
},
TagIgnore: viper.GetStringSlice("tag-ignore"),
RelationTag: viper.GetString("relation-tag"),
TemplateDirs: viper.GetStringSlice("templates"),
Tags: viper.GetStringSlice("tag"),
Replacements: viper.GetStringSlice("replace"),
Aliases: boilingcore.ConvertAliases(viper.Get("aliases")),
TypeReplaces: boilingcore.ConvertTypeReplace(viper.Get("types")),
AutoColumns: boilingcore.AutoColumns{
Created: viper.GetString("auto-columns.created"),
Updated: viper.GetString("auto-columns.updated"),
Expand Down Expand Up @@ -293,3 +300,17 @@ func allKeys(prefix string) []string {
}
return keySlice
}

func withDefaultCase(configCase string, defaultCases ...string) boilingcore.TagCase {
if len(configCase) > 0 {
return boilingcore.TagCase(strings.ToLower(configCase))
}

for _, c := range defaultCases {
if len(c) > 0 {
return boilingcore.TagCase(strings.ToLower(c))
}
}

return boilingcore.TagCaseSnake
}
40 changes: 30 additions & 10 deletions templates/main/00_struct.go.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,38 @@ type {{$alias.UpSingular}} struct {
{{- $orig_col_name := $column.Name -}}
{{- range $column.Comment | splitLines -}} // {{ . }}
{{end -}}

{{if ignore $orig_tbl_name $orig_col_name $.TagIgnore -}}
{{$colAlias}} {{$column.Type}} `{{generateIgnoreTags $.Tags}}boil:"{{$column.Name}}" json:"-" toml:"-" yaml:"-"`
{{else if eq $.StructTagCasing "title" -}}
{{$colAlias}} {{$column.Type}} `{{generateTags $.Tags $column.Name}}boil:"{{$column.Name}}" json:"{{$column.Name | titleCase}}{{if $column.Nullable}},omitempty{{end}}" toml:"{{$column.Name | titleCase}}" yaml:"{{$column.Name | titleCase}}{{if $column.Nullable}},omitempty{{end}}"`
{{else if eq $.StructTagCasing "camel" -}}
{{$colAlias}} {{$column.Type}} `{{generateTags $.Tags $column.Name}}boil:"{{$column.Name}}" json:"{{$column.Name | camelCase}}{{if $column.Nullable}},omitempty{{end}}" toml:"{{$column.Name | camelCase}}" yaml:"{{$column.Name | camelCase}}{{if $column.Nullable}},omitempty{{end}}"`
{{else if eq $.StructTagCasing "alias" -}}
{{$colAlias}} {{$column.Type}} `{{generateTags $.Tags $colAlias}}boil:"{{$column.Name}}" json:"{{$colAlias}}{{if $column.Nullable}},omitempty{{end}}" toml:"{{$colAlias}}" yaml:"{{$colAlias}}{{if $column.Nullable}},omitempty{{end}}"`
{{else -}}
{{$colAlias}} {{$column.Type}} `{{generateTags $.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 -}}
stephenafamo marked this conversation as resolved.
Show resolved Hide resolved
{{end -}}
{{- else -}}

{{- /* render column alias and column type */ -}}
{{ $colAlias }} {{ $column.Type -}}

{{- /*
handle struct tags
StructTagCasing will be replaced with $.StructTagCases
however we need to keep this backward compatible
$.StructTagCasing will only be used when it's set to "alias"
*/ -}}
`
{{- if eq $.StructTagCasing "alias" -}}
{{- generateTags $.Tags $colAlias -}}
{{- generateTagWithCase "json" $column.Name $colAlias "alias" $column.Nullable -}}
{{- generateTagWithCase "yaml" $column.Name $colAlias "alias" $column.Nullable -}}
{{- generateTagWithCase "toml" $column.Name $colAlias "alias" $column.Nullable -}}
{{- generateTagWithCase "boil" $column.Name $colAlias "alias" $column.Nullable -}}
{{- else -}}
{{- generateTags $.Tags $column.Name }}
{{- generateTagWithCase "json" $column.Name $colAlias $.StructTagCases.Json $column.Nullable -}}
{{- generateTagWithCase "yaml" $column.Name $colAlias $.StructTagCases.Yaml $column.Nullable -}}
{{- generateTagWithCase "toml" $column.Name $colAlias $.StructTagCases.Toml $column.Nullable -}}
{{- generateTagWithCase "boil" $column.Name $colAlias $.StructTagCases.Boil $column.Nullable -}}
{{- end -}}
`
{{ end -}}
{{ end -}}

{{- if or .Table.IsJoinTable .Table.IsView -}}
{{- else}}
R *{{$alias.DownSingular}}R `{{generateTags $.Tags $.RelationTag}}boil:"{{$.RelationTag}}" json:"{{$.RelationTag}}" toml:"{{$.RelationTag}}" yaml:"{{$.RelationTag}}"`
Expand Down