naming.go 5.3 KB

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