feature: file namestyle (#223)

* add api filename style

* new feature: config.yaml

* optimize

* optimize logic generation

* check hanlder valid

* optimize

* reactor naming style

* optimize

* optimize test

* optimize gen middleware

* format

Co-authored-by: anqiansong <anqiansong@xiaoheiban.cn>
Co-authored-by: kim <xutao@xiaoheiban.cn>
This commit is contained in:
kingxt
2020-11-24 15:11:18 +08:00
committed by GitHub
parent 702e8d79ce
commit b9ac51b6c3
40 changed files with 896 additions and 296 deletions

View File

@@ -8,13 +8,21 @@ import (
const goctlDir = ".goctl"
func GetTemplateDir(category string) (string, error) {
func GetGoctlHome() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, goctlDir), nil
}
return filepath.Join(home, goctlDir, category), nil
func GetTemplateDir(category string) (string, error) {
goctlHome, err := GetGoctlHome()
if err != nil {
return "", err
}
return filepath.Join(goctlHome, category), nil
}
func InitTemplates(category string, templates map[string]string) error {

View File

@@ -0,0 +1,155 @@
package format
import (
"bytes"
"errors"
"fmt"
"io"
"strings"
)
const (
flagGo = "GO"
flagZero = "ZERO"
unknown style = iota
title
lower
upper
)
var ErrNamingFormat = errors.New("unsupported format")
type (
styleFormat struct {
before string
through string
after string
goStyle style
zeroStyle style
}
style int
)
// FileNamingFormat is used to format the file name. You can define the format style
// through the go and zero formatting characters. For example, you can define the snake
// format as go_zero, and the camel case format as goZero. You can even specify the split
// character, such as go#Zero, theoretically any combination can be used, but the prerequisite
// must meet the naming conventions of each operating system file name.
// Note: Formatting is based on snake or camel string
func FileNamingFormat(format, content string) (string, error) {
upperFormat := strings.ToUpper(format)
indexGo := strings.Index(upperFormat, flagGo)
indexZero := strings.Index(upperFormat, flagZero)
if indexGo < 0 || indexZero < 0 || indexGo > indexZero {
return "", ErrNamingFormat
}
var (
before, through, after string
flagGo, flagZero string
goStyle, zeroStyle style
err error
)
before = format[:indexGo]
flagGo = format[indexGo : indexGo+2]
through = format[indexGo+2 : indexZero]
flagZero = format[indexZero : indexZero+4]
after = format[indexZero+4:]
goStyle, err = getStyle(flagGo)
if err != nil {
return "", err
}
zeroStyle, err = getStyle(flagZero)
if err != nil {
return "", err
}
var formatStyle styleFormat
formatStyle.goStyle = goStyle
formatStyle.zeroStyle = zeroStyle
formatStyle.before = before
formatStyle.through = through
formatStyle.after = after
return doFormat(formatStyle, content)
}
func doFormat(f styleFormat, content string) (string, error) {
splits, err := split(content)
if err != nil {
return "", err
}
var join []string
for index, split := range splits {
if index == 0 {
join = append(join, transferTo(split, f.goStyle))
continue
}
join = append(join, transferTo(split, f.zeroStyle))
}
joined := strings.Join(join, f.through)
return f.before + joined + f.after, nil
}
func transferTo(in string, style style) string {
switch style {
case upper:
return strings.ToUpper(in)
case lower:
return strings.ToLower(in)
case title:
return strings.Title(in)
default:
return in
}
}
func split(content string) ([]string, error) {
var (
list []string
reader = strings.NewReader(content)
buffer = bytes.NewBuffer(nil)
)
for {
r, _, err := reader.ReadRune()
if err != nil {
if err == io.EOF {
if buffer.Len() > 0 {
list = append(list, buffer.String())
}
return list, nil
}
return nil, err
}
if r == '_' {
if buffer.Len() > 0 {
list = append(list, buffer.String())
}
buffer.Reset()
continue
}
if r >= 'A' && r <= 'Z' {
if buffer.Len() > 0 {
list = append(list, buffer.String())
}
buffer.Reset()
}
buffer.WriteRune(r)
}
}
func getStyle(flag string) (style, error) {
compare := strings.ToLower(flag)
switch flag {
case strings.ToLower(compare):
return lower, nil
case strings.ToUpper(compare):
return upper, nil
case strings.Title(compare):
return title, nil
default:
return unknown, fmt.Errorf("unexpected format: %s", flag)
}
}

View File

@@ -0,0 +1,112 @@
package format
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestSplit(t *testing.T) {
list, err := split("A")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("goZero")
assert.Nil(t, err)
assert.Equal(t, []string{"go", "Zero"}, list)
list, err = split("Gozero")
assert.Nil(t, err)
assert.Equal(t, []string{"Gozero"}, list)
list, err = split("go_zero")
assert.Nil(t, err)
assert.Equal(t, []string{"go", "zero"}, list)
list, err = split("talGo_zero")
assert.Nil(t, err)
assert.Equal(t, []string{"tal", "Go", "zero"}, list)
list, err = split("GOZERO")
assert.Nil(t, err)
assert.Equal(t, []string{"G", "O", "Z", "E", "R", "O"}, list)
list, err = split("gozero")
assert.Nil(t, err)
assert.Equal(t, []string{"gozero"}, list)
list, err = split("")
assert.Nil(t, err)
assert.Equal(t, 0, len(list))
list, err = split("a_b_CD_EF")
assert.Nil(t, err)
assert.Equal(t, []string{"a", "b", "C", "D", "E", "F"}, list)
list, err = split("_")
assert.Nil(t, err)
assert.Equal(t, 0, len(list))
list, err = split("__")
assert.Nil(t, err)
assert.Equal(t, 0, len(list))
list, err = split("_A")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("_A_")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("A_")
assert.Nil(t, err)
assert.Equal(t, []string{"A"}, list)
list, err = split("welcome_to_go_zero")
assert.Nil(t, err)
assert.Equal(t, []string{"welcome", "to", "go", "zero"}, list)
}
func TestFileNamingFormat(t *testing.T) {
testFileNamingFormat(t, "gozero", "welcome_to_go_zero", "welcometogozero")
testFileNamingFormat(t, "_go#zero_", "welcome_to_go_zero", "_welcome#to#go#zero_")
testFileNamingFormat(t, "Go#zero", "welcome_to_go_zero", "Welcome#to#go#zero")
testFileNamingFormat(t, "Go#Zero", "welcome_to_go_zero", "Welcome#To#Go#Zero")
testFileNamingFormat(t, "Go_Zero", "welcome_to_go_zero", "Welcome_To_Go_Zero")
testFileNamingFormat(t, "go_Zero", "welcome_to_go_zero", "welcome_To_Go_Zero")
testFileNamingFormat(t, "goZero", "welcome_to_go_zero", "welcomeToGoZero")
testFileNamingFormat(t, "GoZero", "welcome_to_go_zero", "WelcomeToGoZero")
testFileNamingFormat(t, "GOZero", "welcome_to_go_zero", "WELCOMEToGoZero")
testFileNamingFormat(t, "GoZERO", "welcome_to_go_zero", "WelcomeTOGOZERO")
testFileNamingFormat(t, "GOZERO", "welcome_to_go_zero", "WELCOMETOGOZERO")
testFileNamingFormat(t, "GO*ZERO", "welcome_to_go_zero", "WELCOME*TO*GO*ZERO")
testFileNamingFormat(t, "[GO#ZERO]", "welcome_to_go_zero", "[WELCOME#TO#GO#ZERO]")
testFileNamingFormat(t, "{go###zero}", "welcome_to_go_zero", "{welcome###to###go###zero}")
testFileNamingFormat(t, "{go###zerogo_zero}", "welcome_to_go_zero", "{welcome###to###go###zerogo_zero}")
testFileNamingFormat(t, "GogoZerozero", "welcome_to_go_zero", "WelcomegoTogoGogoZerozero")
testFileNamingFormat(t, "前缀GoZero后缀", "welcome_to_go_zero", "前缀WelcomeToGoZero后缀")
testFileNamingFormat(t, "GoZero", "welcometogozero", "Welcometogozero")
testFileNamingFormat(t, "GoZero", "WelcomeToGoZero", "WelcomeToGoZero")
testFileNamingFormat(t, "gozero", "WelcomeToGoZero", "welcometogozero")
testFileNamingFormat(t, "go_zero", "WelcomeToGoZero", "welcome_to_go_zero")
testFileNamingFormat(t, "Go_Zero", "WelcomeToGoZero", "Welcome_To_Go_Zero")
testFileNamingFormat(t, "Go_Zero", "", "")
testFileNamingFormatErr(t, "go", "")
testFileNamingFormatErr(t, "gOZero", "")
testFileNamingFormatErr(t, "zero", "")
testFileNamingFormatErr(t, "goZEro", "welcome_to_go_zero")
testFileNamingFormatErr(t, "goZERo", "welcome_to_go_zero")
testFileNamingFormatErr(t, "zerogo", "welcome_to_go_zero")
}
func testFileNamingFormat(t *testing.T, format, in, expected string) {
format, err := FileNamingFormat(format, in)
assert.Nil(t, err)
assert.Equal(t, expected, format)
}
func testFileNamingFormatErr(t *testing.T, format, in string) {
_, err := FileNamingFormat(format, in)
assert.Error(t, err)
}

View File

@@ -0,0 +1,41 @@
package name
import (
"strings"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
)
type NamingStyle = string
const (
NamingLower NamingStyle = "lower"
NamingCamel NamingStyle = "camel"
NamingSnake NamingStyle = "snake"
)
// IsNamingValid validates whether the namingStyle is valid or not,return
// namingStyle and true if it is valid, or else return empty string
// and false, and it is a valid value even namingStyle is empty string
func IsNamingValid(namingStyle string) (NamingStyle, bool) {
if len(namingStyle) == 0 {
namingStyle = NamingLower
}
switch namingStyle {
case NamingLower, NamingCamel, NamingSnake:
return namingStyle, true
default:
return "", false
}
}
func FormatFilename(filename string, style NamingStyle) string {
switch style {
case NamingCamel:
return stringx.From(filename).ToCamel()
case NamingSnake:
return stringx.From(filename).ToSnake()
default:
return strings.ToLower(stringx.From(filename).ToCamel())
}
}

View File

@@ -0,0 +1,35 @@
package name
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestIsNamingValid(t *testing.T) {
style, valid := IsNamingValid("")
assert.True(t, valid)
assert.Equal(t, NamingLower, style)
_, valid = IsNamingValid("lower1")
assert.False(t, valid)
_, valid = IsNamingValid("lower")
assert.True(t, valid)
_, valid = IsNamingValid("snake")
assert.True(t, valid)
_, valid = IsNamingValid("camel")
assert.True(t, valid)
}
func TestFormatFilename(t *testing.T) {
assert.Equal(t, "abc", FormatFilename("a_b_c", NamingLower))
assert.Equal(t, "ABC", FormatFilename("a_b_c", NamingCamel))
assert.Equal(t, "a_b_c", FormatFilename("a_b_c", NamingSnake))
assert.Equal(t, "a", FormatFilename("a", NamingSnake))
assert.Equal(t, "A", FormatFilename("a", NamingCamel))
// no flag to convert to snake
assert.Equal(t, "abc", FormatFilename("abc", NamingSnake))
}