parent
1fbda33ac3
commit
ef05f896b7
17
README.md
17
README.md
|
@ -1,3 +1,5 @@
|
|||
![gotygen](assets/logo.png)
|
||||
|
||||
Golang types generator tool
|
||||
|
||||
### Configuration
|
||||
|
@ -6,13 +8,14 @@ Example `.gotygen`
|
|||
|
||||
```yaml
|
||||
packages:
|
||||
- name: test
|
||||
output: ./pkc/test/types.go
|
||||
- name: "test"
|
||||
output: "./pkc/test/types.go"
|
||||
acronyms: ["ID", "JSON"]
|
||||
inputs:
|
||||
- name: Response1
|
||||
path: ./api/test/response1.json
|
||||
- name: Response2
|
||||
path: ./api/test/response2.json
|
||||
- name: "Response"
|
||||
path: "./api/test/response1.json"
|
||||
- name: "Response"
|
||||
path: "./api/test/response2.json"
|
||||
replacements:
|
||||
"Response.Context": "ResponseContext"
|
||||
- "Response.Context:ResponseContext"
|
||||
```
|
||||
|
|
11
cmd/root.go
11
cmd/root.go
|
@ -10,7 +10,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"githouse.ru/macrox/gotygen/pkg/conv"
|
||||
"githouse.ru/macrox/gotygen/pkg/strcase"
|
||||
|
||||
"githouse.ru/macrox/gotygen/pkg/filler"
|
||||
"githouse.ru/macrox/gotygen/pkg/utils"
|
||||
|
||||
|
@ -44,7 +45,7 @@ var rootCmd = &cobra.Command{
|
|||
)
|
||||
for _, i := range p.Inputs {
|
||||
if len(i.Name) < 1 {
|
||||
i.Name = conv.ToCamelCase(utils.TrimmedFilenameFromPath(i.Path))
|
||||
i.Name = strcase.ToCamel(utils.TrimmedFilenameFromPath(i.Path))
|
||||
if len(i.Name) < 1 {
|
||||
slog.Warn("input config constrain empty name")
|
||||
continue
|
||||
|
@ -57,13 +58,13 @@ var rootCmd = &cobra.Command{
|
|||
}
|
||||
f := filler.NewJsonFiller(data, i.Name, p.Acronyms...)
|
||||
if len(p.Replacements) > 0 {
|
||||
f.UseReplacements(p.Replacements...)
|
||||
f.UseReplacements(p.Replacements)
|
||||
}
|
||||
if len(i.Replacements) > 0 {
|
||||
f.UseReplacements(i.Replacements...)
|
||||
f.UseReplacements(i.Replacements)
|
||||
}
|
||||
if len(i.Acronyms) > 0 {
|
||||
f.UseCaps(i.Acronyms...)
|
||||
f.UseCaps(i.Acronyms)
|
||||
}
|
||||
if err = f.Fill(doc); err != nil {
|
||||
slog.Error("failed fill document", err)
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
package conv
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"githouse.ru/macrox/gotygen/pkg/utils"
|
||||
)
|
||||
|
||||
type CamelCaseConverter interface {
|
||||
UseCaps(caps ...string) CamelCaseConverter
|
||||
ToCamelCase(s string) string
|
||||
}
|
||||
|
||||
type DefaultCamelCaseConverter struct {
|
||||
allCaps map[string]bool
|
||||
converted map[string]string
|
||||
}
|
||||
|
||||
func NewDefaultCamelCaseConverter(allCaps []string) CamelCaseConverter {
|
||||
return &DefaultCamelCaseConverter{
|
||||
allCaps: utils.SliceToMap(allCaps, func(s string) (string, bool) {
|
||||
return s, true
|
||||
}),
|
||||
converted: make(map[string]string),
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
splitRE = regexp.MustCompile(`_+|-+|\s+`)
|
||||
allCapsRE = regexp.MustCompile(`^[A-Z]{2,}\d*$`)
|
||||
capitalizedRE = regexp.MustCompile(`[A-Z][a-z]+\d*`)
|
||||
ToCamelCase = NewDefaultCamelCaseConverter(nil).ToCamelCase
|
||||
)
|
||||
|
||||
func (c *DefaultCamelCaseConverter) UseCaps(v ...string) CamelCaseConverter {
|
||||
if c.allCaps == nil {
|
||||
c.allCaps = make(map[string]bool, len(v))
|
||||
}
|
||||
for _, s := range v {
|
||||
c.allCaps[s] = true
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *DefaultCamelCaseConverter) ToCamelCase(s string) string {
|
||||
if r, ok := c.converted[s]; ok {
|
||||
return r
|
||||
}
|
||||
|
||||
words := splitRE.Split(s, -1)
|
||||
|
||||
for i, word := range words {
|
||||
if len(word) != 0 {
|
||||
if allCapsRE.MatchString(word) {
|
||||
words[i] = word[:1] + strings.ToLower(word[1:])
|
||||
} else {
|
||||
words[i] = strings.ToUpper(word[:1]) + word[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
r := capitalizedRE.ReplaceAllStringFunc(strings.Join(words, ""), func(s string) string {
|
||||
if word := strings.ToUpper(s); c.allCaps[word] {
|
||||
return word
|
||||
}
|
||||
return s
|
||||
})
|
||||
|
||||
c.converted[s] = r
|
||||
return r
|
||||
}
|
||||
|
||||
var (
|
||||
followNonCapRE = regexp.MustCompile(`([a-z\d])([A-Z])`)
|
||||
followCapRE = regexp.MustCompile(`([A-Z])([A-Z][a-z]+)`)
|
||||
)
|
||||
|
||||
func ToSnakeCase(s string) string {
|
||||
s = followNonCapRE.ReplaceAllString(s, "${1}_$2")
|
||||
s = followCapRE.ReplaceAllString(s, "${1}_$2")
|
||||
return strings.ToLower(s)
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package conv
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestToCamelCase(t *testing.T) {
|
||||
assert.Equal(t, "", ToCamelCase(""))
|
||||
assert.Equal(t, "T", ToCamelCase("t"))
|
||||
assert.Equal(t, "To", ToCamelCase("to"))
|
||||
assert.Equal(t, "ToCamelCase", ToCamelCase("to camel case "))
|
||||
assert.Equal(t, "ToCamelCase", ToCamelCase("to_camel__case"))
|
||||
assert.Equal(t, "ToCamelCase", ToCamelCase("to-camel--case"))
|
||||
assert.Equal(t, "ToCamelCase", ToCamelCase("to_camel -case"))
|
||||
assert.Equal(t, "JsonToGo", ToCamelCase("JSON to Go"))
|
||||
|
||||
converter := NewDefaultCamelCaseConverter([]string{"ID", "JSON"})
|
||||
assert.Equal(t, "ID", converter.ToCamelCase("id"))
|
||||
assert.Equal(t, "JSON", converter.ToCamelCase("json"))
|
||||
assert.Equal(t, "JSONToGo", converter.ToCamelCase("JSON to Go"))
|
||||
}
|
||||
|
||||
func TestToSnakeCase(t *testing.T) {
|
||||
assert.Equal(t, "", ToSnakeCase(""))
|
||||
assert.Equal(t, "json_to_go", ToSnakeCase("JSONToGo"))
|
||||
}
|
|
@ -1,14 +1,13 @@
|
|||
package filler
|
||||
|
||||
import (
|
||||
"githouse.ru/macrox/gotygen/pkg/conv"
|
||||
"githouse.ru/macrox/gotygen/pkg/document"
|
||||
"githouse.ru/macrox/gotygen/pkg/scanner"
|
||||
)
|
||||
|
||||
type Filler interface {
|
||||
scanner.Scanner
|
||||
conv.CamelCaseConverter
|
||||
UseReplacements(replacements ...string) Filler
|
||||
UseReplacements(a []string) Filler
|
||||
UseCaps(a []string) Filler
|
||||
Fill(out *document.Document) error
|
||||
}
|
||||
|
|
|
@ -4,18 +4,20 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"githouse.ru/macrox/gotygen/pkg/strcase"
|
||||
|
||||
"githouse.ru/macrox/gotygen/pkg/utils"
|
||||
|
||||
"githouse.ru/macrox/gotygen/pkg/token"
|
||||
|
||||
"githouse.ru/macrox/gotygen/pkg/conv"
|
||||
"githouse.ru/macrox/gotygen/pkg/document"
|
||||
"githouse.ru/macrox/gotygen/pkg/scanner"
|
||||
)
|
||||
|
||||
type jsonFiller struct {
|
||||
scanner.Scanner
|
||||
conv.CamelCaseConverter
|
||||
|
||||
camelCtx strcase.CamelContext
|
||||
|
||||
replacements map[string]string
|
||||
declaredName string
|
||||
|
@ -147,7 +149,7 @@ func (f *jsonFiller) scanObjectType(refOut *document.Document, rootName string)
|
|||
return nil, err
|
||||
}
|
||||
keys = append(keys, key)
|
||||
if t, err = f.scanType(refOut, f.ToCamelCase(key)); err != nil {
|
||||
if t, err = f.scanType(refOut, strcase.ToCamel(key, f.camelCtx)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
types = append(types, t)
|
||||
|
@ -171,7 +173,7 @@ func (f *jsonFiller) scanObjectType(refOut *document.Document, rootName string)
|
|||
}
|
||||
s := document.NewStruct()
|
||||
for i, key := range keys {
|
||||
name, omitEmpty := f.ToCamelCase(key), utils.IsType[document.Map](types[i]) || utils.IsType[document.Array](types[i])
|
||||
name, omitEmpty := strcase.ToCamel(key, f.camelCtx), utils.IsType[document.Map](types[i]) || utils.IsType[document.Array](types[i])
|
||||
if refOut != nil {
|
||||
if arr, ok := types[i].(document.Array); ok {
|
||||
element := arr.Element()
|
||||
|
@ -237,23 +239,6 @@ func (f *jsonFiller) scanType(refOut *document.Document, rootName string) (docum
|
|||
}
|
||||
}
|
||||
|
||||
func (f *jsonFiller) Fill(out *document.Document) error {
|
||||
if out == nil {
|
||||
return fmt.Errorf("document is nill")
|
||||
}
|
||||
t, err := f.scanType(out, f.declaredName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.PutDeclaredType(document.NewDeclaredType(f.declaredName, t), true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *jsonFiller) UseCaps(caps ...string) conv.CamelCaseConverter {
|
||||
f.CamelCaseConverter.UseCaps(caps...)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *jsonFiller) normalizeId(parts ...string) string {
|
||||
if len(parts) < 1 {
|
||||
return ""
|
||||
|
@ -269,11 +254,28 @@ func (f *jsonFiller) normalizeId(parts ...string) string {
|
|||
return key
|
||||
}
|
||||
|
||||
func (f *jsonFiller) UseReplacements(replacements ...string) Filler {
|
||||
for _, replacement := range replacements {
|
||||
parts := strings.Split(replacement, ":")
|
||||
if len(parts) > 1 {
|
||||
f.replacements[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[len(parts)-1])
|
||||
func (f *jsonFiller) Fill(out *document.Document) error {
|
||||
if out == nil {
|
||||
return fmt.Errorf("document is nill")
|
||||
}
|
||||
t, err := f.scanType(out, f.declaredName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.PutDeclaredType(document.NewDeclaredType(f.declaredName, t), true)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *jsonFiller) UseCaps(a []string) Filler {
|
||||
f.camelCtx.UseCaps(a)
|
||||
return f
|
||||
}
|
||||
|
||||
func (f *jsonFiller) UseReplacements(a []string) Filler {
|
||||
for _, s := range a {
|
||||
p := strings.Split(s, ":")
|
||||
if len(p) > 1 {
|
||||
f.replacements[strings.TrimSpace(p[0])] = strings.TrimSpace(p[len(p)-1])
|
||||
}
|
||||
}
|
||||
return f
|
||||
|
@ -281,9 +283,9 @@ func (f *jsonFiller) UseReplacements(replacements ...string) Filler {
|
|||
|
||||
func NewJsonFiller(data []byte, declaredName string, allCaps ...string) Filler {
|
||||
return &jsonFiller{
|
||||
CamelCaseConverter: conv.NewDefaultCamelCaseConverter(allCaps),
|
||||
Scanner: scanner.NewFromBytes(data),
|
||||
replacements: make(map[string]string),
|
||||
declaredName: declaredName,
|
||||
Scanner: scanner.NewFromBytes(data),
|
||||
camelCtx: strcase.NewCamelContext(allCaps),
|
||||
replacements: make(map[string]string),
|
||||
declaredName: declaredName,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
package strcase
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
splitRE = regexp.MustCompile(`_+|-+|\s+`)
|
||||
allCapsRE = regexp.MustCompile(`^[A-Z]{2,}\d*$`)
|
||||
capitalizedRE = regexp.MustCompile(`[A-Z][a-z]+\d*`)
|
||||
)
|
||||
|
||||
type CamelContext interface {
|
||||
UseCaps(a []string) CamelContext
|
||||
HasCaps(w string) bool
|
||||
Find(s string) (string, bool)
|
||||
Save(s, r string)
|
||||
}
|
||||
|
||||
type camelContext struct {
|
||||
allCaps map[string]bool
|
||||
cache map[string]string
|
||||
}
|
||||
|
||||
func (c *camelContext) UseCaps(a []string) CamelContext {
|
||||
if c.allCaps == nil {
|
||||
c.allCaps = make(map[string]bool, len(a))
|
||||
}
|
||||
for _, s := range a {
|
||||
c.allCaps[s] = true
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *camelContext) HasCaps(w string) (ok bool) {
|
||||
if c.allCaps != nil {
|
||||
_, ok = c.allCaps[w]
|
||||
}
|
||||
return
|
||||
}
|
||||
func (c *camelContext) Find(s string) (r string, ok bool) {
|
||||
if c.cache != nil {
|
||||
r, ok = c.cache[s]
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c *camelContext) Save(s, r string) {
|
||||
if c.cache == nil {
|
||||
c.cache = make(map[string]string)
|
||||
}
|
||||
c.cache[s] = r
|
||||
}
|
||||
|
||||
func NewCamelContext(caps []string) CamelContext {
|
||||
return new(camelContext).UseCaps(caps)
|
||||
}
|
||||
|
||||
func ToCamel(s string, c ...CamelContext) (r string) {
|
||||
var ctx CamelContext
|
||||
if len(c) > 0 {
|
||||
ctx = c[0]
|
||||
}
|
||||
if ctx != nil {
|
||||
var ok bool
|
||||
if r, ok = ctx.Find(s); ok {
|
||||
return
|
||||
}
|
||||
}
|
||||
words := splitRE.Split(s, -1)
|
||||
for i, word := range words {
|
||||
if len(word) != 0 {
|
||||
if allCapsRE.MatchString(word) {
|
||||
words[i] = word[:1] + strings.ToLower(word[1:])
|
||||
} else {
|
||||
words[i] = strings.ToUpper(word[:1]) + word[1:]
|
||||
}
|
||||
}
|
||||
}
|
||||
if ctx != nil {
|
||||
r = capitalizedRE.ReplaceAllStringFunc(strings.Join(words, ""), func(s string) string {
|
||||
if w := strings.ToUpper(s); ctx.HasCaps(w) {
|
||||
return w
|
||||
}
|
||||
return s
|
||||
})
|
||||
ctx.Save(s, r)
|
||||
} else {
|
||||
r = strings.Join(words, "")
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package strcase
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestToCamel(t *testing.T) {
|
||||
assert.Equal(t, "", ToCamel(""))
|
||||
assert.Equal(t, "T", ToCamel("t"))
|
||||
assert.Equal(t, "To", ToCamel("to"))
|
||||
assert.Equal(t, "ToCamelCase", ToCamel("to camel case "))
|
||||
assert.Equal(t, "ToCamelCase", ToCamel("to_camel__case"))
|
||||
assert.Equal(t, "ToCamelCase", ToCamel("to-camel--case"))
|
||||
assert.Equal(t, "ToCamelCase", ToCamel("to_camel -case"))
|
||||
assert.Equal(t, "JsonToGo", ToCamel("JSON to Go"))
|
||||
|
||||
ctx := NewCamelContext([]string{"ID", "JSON"})
|
||||
assert.Equal(t, "ID", ToCamel("id", ctx))
|
||||
assert.Equal(t, "JSON", ToCamel("json", ctx))
|
||||
assert.Equal(t, "JSONToGo", ToCamel("JSON to Go", ctx))
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package strcase
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
followNonCapRE = regexp.MustCompile(`([a-z\d])([A-Z])`)
|
||||
followCapRE = regexp.MustCompile(`([A-Z])([A-Z][a-z]+)`)
|
||||
)
|
||||
|
||||
func ToSnake(s string) string {
|
||||
s = followNonCapRE.ReplaceAllString(s, "${1}_$2")
|
||||
s = followCapRE.ReplaceAllString(s, "${1}_$2")
|
||||
return strings.ToLower(s)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package strcase
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestToSnake(t *testing.T) {
|
||||
assert.Equal(t, "", ToSnake(""))
|
||||
assert.Equal(t, "json_to_go", ToSnake("JSONToGo"))
|
||||
}
|
Loading…
Reference in New Issue