naming.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package schema
  2. import (
  3. "crypto/sha1"
  4. "encoding/hex"
  5. "fmt"
  6. "regexp"
  7. "strings"
  8. "unicode/utf8"
  9. "github.com/jinzhu/inflection"
  10. )
  11. // Namer namer interface
  12. type Namer interface {
  13. TableName(table string) string
  14. SchemaName(table string) string
  15. ColumnName(table, column string) string
  16. JoinTableName(joinTable string) string
  17. RelationshipFKName(Relationship) string
  18. CheckerName(table, column string) string
  19. IndexName(table, column string) string
  20. }
  21. // Replacer replacer interface like strings.Replacer
  22. type Replacer interface {
  23. Replace(name string) string
  24. }
  25. // NamingStrategy tables, columns naming strategy
  26. type NamingStrategy struct {
  27. TablePrefix string
  28. SingularTable bool
  29. NameReplacer Replacer
  30. NoLowerCase bool
  31. }
  32. // TableName convert string to table name
  33. func (ns NamingStrategy) TableName(str string) string {
  34. if ns.SingularTable {
  35. return ns.TablePrefix + ns.toDBName(str)
  36. }
  37. return ns.TablePrefix + inflection.Plural(ns.toDBName(str))
  38. }
  39. // SchemaName generate schema name from table name, don't guarantee it is the reverse value of TableName
  40. func (ns NamingStrategy) SchemaName(table string) string {
  41. table = strings.TrimPrefix(table, ns.TablePrefix)
  42. if ns.SingularTable {
  43. return ns.toSchemaName(table)
  44. }
  45. return ns.toSchemaName(inflection.Singular(table))
  46. }
  47. // ColumnName convert string to column name
  48. func (ns NamingStrategy) ColumnName(table, column string) string {
  49. return ns.toDBName(column)
  50. }
  51. // JoinTableName convert string to join table name
  52. func (ns NamingStrategy) JoinTableName(str string) string {
  53. if !ns.NoLowerCase && strings.ToLower(str) == str {
  54. return ns.TablePrefix + str
  55. }
  56. if ns.SingularTable {
  57. return ns.TablePrefix + ns.toDBName(str)
  58. }
  59. return ns.TablePrefix + inflection.Plural(ns.toDBName(str))
  60. }
  61. // RelationshipFKName generate fk name for relation
  62. func (ns NamingStrategy) RelationshipFKName(rel Relationship) string {
  63. return ns.formatName("fk", rel.Schema.Table, ns.toDBName(rel.Name))
  64. }
  65. // CheckerName generate checker name
  66. func (ns NamingStrategy) CheckerName(table, column string) string {
  67. return ns.formatName("chk", table, column)
  68. }
  69. // IndexName generate index name
  70. func (ns NamingStrategy) IndexName(table, column string) string {
  71. return ns.formatName("idx", table, ns.toDBName(column))
  72. }
  73. func (ns NamingStrategy) formatName(prefix, table, name string) string {
  74. formattedName := strings.Replace(strings.Join([]string{
  75. prefix, table, name,
  76. }, "_"), ".", "_", -1)
  77. if utf8.RuneCountInString(formattedName) > 64 {
  78. h := sha1.New()
  79. h.Write([]byte(formattedName))
  80. bs := h.Sum(nil)
  81. formattedName = fmt.Sprintf("%v%v%v", prefix, table, name)[0:56] + hex.EncodeToString(bs)[:8]
  82. }
  83. return formattedName
  84. }
  85. var (
  86. // https://github.com/golang/lint/blob/master/lint.go#L770
  87. commonInitialisms = []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"}
  88. commonInitialismsReplacer *strings.Replacer
  89. )
  90. func init() {
  91. commonInitialismsForReplacer := make([]string, 0, len(commonInitialisms))
  92. for _, initialism := range commonInitialisms {
  93. commonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, strings.Title(strings.ToLower(initialism)))
  94. }
  95. commonInitialismsReplacer = strings.NewReplacer(commonInitialismsForReplacer...)
  96. }
  97. func (ns NamingStrategy) toDBName(name string) string {
  98. if name == "" {
  99. return ""
  100. }
  101. if ns.NameReplacer != nil {
  102. name = ns.NameReplacer.Replace(name)
  103. }
  104. if ns.NoLowerCase {
  105. return name
  106. }
  107. var (
  108. value = commonInitialismsReplacer.Replace(name)
  109. buf strings.Builder
  110. lastCase, nextCase, nextNumber bool // upper case == true
  111. curCase = value[0] <= 'Z' && value[0] >= 'A'
  112. )
  113. for i, v := range value[:len(value)-1] {
  114. nextCase = value[i+1] <= 'Z' && value[i+1] >= 'A'
  115. nextNumber = value[i+1] >= '0' && value[i+1] <= '9'
  116. if curCase {
  117. if lastCase && (nextCase || nextNumber) {
  118. buf.WriteRune(v + 32)
  119. } else {
  120. if i > 0 && value[i-1] != '_' && value[i+1] != '_' {
  121. buf.WriteByte('_')
  122. }
  123. buf.WriteRune(v + 32)
  124. }
  125. } else {
  126. buf.WriteRune(v)
  127. }
  128. lastCase = curCase
  129. curCase = nextCase
  130. }
  131. if curCase {
  132. if !lastCase && len(value) > 1 {
  133. buf.WriteByte('_')
  134. }
  135. buf.WriteByte(value[len(value)-1] + 32)
  136. } else {
  137. buf.WriteByte(value[len(value)-1])
  138. }
  139. ret := buf.String()
  140. return ret
  141. }
  142. func (ns NamingStrategy) toSchemaName(name string) string {
  143. result := strings.Replace(strings.Title(strings.Replace(name, "_", " ", -1)), " ", "", -1)
  144. for _, initialism := range commonInitialisms {
  145. result = regexp.MustCompile(strings.Title(strings.ToLower(initialism))+"([A-Z]|$|_)").ReplaceAllString(result, initialism+"$1")
  146. }
  147. return result
  148. }