diff --git a/internal/conf/db_sqlx.go b/internal/conf/db_sqlx.go index c2699c84..a8b1cee4 100644 --- a/internal/conf/db_sqlx.go +++ b/internal/conf/db_sqlx.go @@ -8,6 +8,7 @@ import ( "sync" "github.com/jmoiron/sqlx" + "github.com/rocboss/paopao-ce/pkg/naming" "github.com/sirupsen/logrus" ) @@ -23,6 +24,7 @@ func MustSqlxDB() *sqlx.DB { logrus.Fatalf("new sqlx db failed: %s", err) } _sqlxdb = sqlx.NewDb(db, driver) + _sqlxdb.MapperFunc(naming.NewSnakeNamingStrategy().Naming) }) return _sqlxdb } diff --git a/pkg/naming/naming.go b/pkg/naming/naming.go new file mode 100644 index 00000000..abf10f1c --- /dev/null +++ b/pkg/naming/naming.go @@ -0,0 +1,10 @@ +// Copyright 2020 Michael Li . All rights reserved. +// Use of this source code is governed by Apache License 2.0 that +// can be found in the LICENSE file. + +package naming + +// NamingStrategy naming strategy interface +type NamingStrategy interface { + Naming(string) string +} diff --git a/pkg/naming/naming_test.go b/pkg/naming/naming_test.go new file mode 100644 index 00000000..ee688822 --- /dev/null +++ b/pkg/naming/naming_test.go @@ -0,0 +1,53 @@ +// Copyright 2020 Michael Li . All rights reserved. +// Use of this source code is governed by Apache License 2.0 that +// can be found in the LICENSE file. + +package naming + +import "testing" + +func TestSnakeNamingStrategy_Naming(t *testing.T) { + ns := NewSnakeNamingStrategy() + for _, cs := range []struct { + name string + expected string + }{ + {name: "abc", expected: "abc"}, + {name: "Abc", expected: "abc"}, + {name: "HostName", expected: "host_name"}, + {name: "Host_Name", expected: "host_name"}, + {name: "RESTfulAPI", expected: "res_tful_api"}, + {name: "HTTPS_API", expected: "https_api"}, + {name: "PKG_Name", expected: "pkg_name"}, + {name: "API", expected: "api"}, + {name: "HTTP", expected: "http"}, + } { + result := ns.Naming(cs.name) + if result != cs.expected { + t.Errorf("give:%s expected:%s result:%s", cs.name, cs.expected, result) + } + } +} + +func TestSimpleNamingStrategy_Naming(t *testing.T) { + ns := NewSimpleNamingStrategy() + for _, cs := range []struct { + name string + expected string + }{ + {name: "abc", expected: "abc"}, + {name: "Abc", expected: "abc"}, + {name: "HostName", expected: "host_name"}, + {name: "Host_Name", expected: "host__name"}, + {name: "RESTfulAPI", expected: "r_e_s_tful_a_p_i"}, + {name: "HTTPS_API", expected: "h_t_t_p_s__a_p_i"}, + {name: "PKG_Name", expected: "p_k_g__name"}, + {name: "API", expected: "a_p_i"}, + {name: "HTTP", expected: "h_t_t_p"}, + } { + result := ns.Naming(cs.name) + if result != cs.expected { + t.Errorf("give:%s expected:%s result:%s", cs.name, cs.expected, result) + } + } +} diff --git a/pkg/naming/simple_ns.go b/pkg/naming/simple_ns.go new file mode 100644 index 00000000..2b4dd733 --- /dev/null +++ b/pkg/naming/simple_ns.go @@ -0,0 +1,37 @@ +// Copyright 2020 Michael Li . All rights reserved. +// Use of this source code is governed by Apache License 2.0 that +// can be found in the LICENSE file. + +package naming + +import ( + "strings" + "unicode" +) + +type simpleNamingStrategy struct { + // just empty +} + +func (s *simpleNamingStrategy) Naming(name string) string { + b := &strings.Builder{} + notFirst := false + b.Grow(len(name) + 3) + for _, r := range name { + if unicode.IsUpper(r) { + if notFirst { + b.WriteRune('_') + } + b.WriteRune(unicode.ToLower(r)) + } else { + b.WriteRune(r) + } + notFirst = true + } + return b.String() +} + +// NewSimpleNamingStrategy return simple naming strategy instance +func NewSimpleNamingStrategy() NamingStrategy { + return &simpleNamingStrategy{} +} diff --git a/pkg/naming/snake_ns.go b/pkg/naming/snake_ns.go new file mode 100644 index 00000000..0c870931 --- /dev/null +++ b/pkg/naming/snake_ns.go @@ -0,0 +1,76 @@ +// Copyright 2020 Michael Li . All rights reserved. +// Use of this source code is governed by Apache License 2.0 that +// can be found in the LICENSE file. + +package naming + +import ( + "bytes" + "strings" +) + +// snakeNamingStrategy snake naming strategy implement +// This implement is inspiration from https://github.com/jingzhu/gorm's naming logic. +type snakeNamingStrategy struct { + initialismsReplacer *strings.Replacer +} + +func (s *snakeNamingStrategy) Naming(name string) string { + if name == "" { + return "" + } + + var lastCase, currCase, nextCase, nextNumber bool + value := s.initialismsReplacer.Replace(name) + buf := bytes.NewBufferString("") + lower, upper := false, true + for i, v := range value[:len(value)-1] { + nextCase = value[i+1] >= 'A' && value[i+1] <= 'Z' + nextNumber = value[i+1] >= '0' && value[i+1] <= '9' + if i > 0 { + if currCase == upper { + if lastCase == upper && (nextCase == upper || nextNumber == upper) { + buf.WriteRune(v) + } else { + if value[i-1] != '_' && value[i+1] != '_' { + buf.WriteRune('_') + } + buf.WriteRune(v) + } + } else { + buf.WriteRune(v) + if i == len(value)-2 && (nextCase == upper && nextNumber == lower) { + buf.WriteRune('_') + } + } + } else { + currCase = upper + buf.WriteRune(v) + } + lastCase, currCase = currCase, nextCase + } + + buf.WriteByte(value[len(value)-1]) + res := strings.ToLower(buf.String()) + return res +} + +// NewSnakeNamingStrategy return snake naming strategy instance +func NewSnakeNamingStrategy() NamingStrategy { + // Copied from golint + initialisms := []string{ + "API", "ASCII", "CPU", "CSS", "DNS", "EOF", "GUID", "HTML", "HTTP", + "HTTPS", "ID", "IP", "JSON", "LHS", "QPS", "RAM", "RHS", "RPC", "SLA", + "SMTP", "SSH", "TLS", "TTL", "UID", "UI", "UUID", "URI", "URL", "UTF8", + "VM", "XML", "XSRF", "XSS"} + + var oldnews []string + for _, initialism := range initialisms { + oldnews = append(oldnews, initialism, strings.Title(strings.ToLower(initialism))) + } + replacer := strings.NewReplacer(oldnews...) + + return &snakeNamingStrategy{ + initialismsReplacer: replacer, + } +}