naming.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. package schema
  2. import (
  3. "crypto/sha1"
  4. "fmt"
  5. "strings"
  6. "sync"
  7. "unicode/utf8"
  8. "github.com/jinzhu/inflection"
  9. )
  10. // Namer namer interface
  11. type Namer interface {
  12. TableName(table string) string
  13. ColumnName(table, column string) string
  14. JoinTableName(joinTable string) string
  15. RelationshipFKName(Relationship) string
  16. CheckerName(table, column string) string
  17. IndexName(table, column string) string
  18. }
  19. // NamingStrategy tables, columns naming strategy
  20. type NamingStrategy struct {
  21. TablePrefix string
  22. SingularTable bool
  23. NameReplacer *strings.Replacer
  24. }
  25. // TableName convert string to table name
  26. func (ns NamingStrategy) TableName(str string) string {
  27. if ns.SingularTable {
  28. return ns.TablePrefix + ns.toDBName(str)
  29. }
  30. return ns.TablePrefix + inflection.Plural(ns.toDBName(str))
  31. }
  32. // ColumnName convert string to column name
  33. func (ns NamingStrategy) ColumnName(table, column string) string {
  34. return ns.toDBName(column)
  35. }
  36. // JoinTableName convert string to join table name
  37. func (ns NamingStrategy) JoinTableName(str string) string {
  38. if strings.ToLower(str) == str {
  39. return ns.TablePrefix + str
  40. }
  41. if ns.SingularTable {
  42. return ns.TablePrefix + ns.toDBName(str)
  43. }
  44. return ns.TablePrefix + inflection.Plural(ns.toDBName(str))
  45. }
  46. // RelationshipFKName generate fk name for relation
  47. func (ns NamingStrategy) RelationshipFKName(rel Relationship) string {
  48. return strings.Replace(fmt.Sprintf("fk_%s_%s", rel.Schema.Table, ns.toDBName(rel.Name)), ".", "_", -1)
  49. }
  50. // CheckerName generate checker name
  51. func (ns NamingStrategy) CheckerName(table, column string) string {
  52. return strings.Replace(fmt.Sprintf("chk_%s_%s", table, column), ".", "_", -1)
  53. }
  54. // IndexName generate index name
  55. func (ns NamingStrategy) IndexName(table, column string) string {
  56. idxName := fmt.Sprintf("idx_%v_%v", table, ns.toDBName(column))
  57. idxName = strings.Replace(idxName, ".", "_", -1)
  58. if utf8.RuneCountInString(idxName) > 64 {
  59. h := sha1.New()
  60. h.Write([]byte(idxName))
  61. bs := h.Sum(nil)
  62. idxName = fmt.Sprintf("idx%v%v", table, column)[0:56] + string(bs)[:8]
  63. }
  64. return idxName
  65. }
  66. var (
  67. smap sync.Map
  68. // https://github.com/golang/lint/blob/master/lint.go#L770
  69. 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"}
  70. commonInitialismsReplacer *strings.Replacer
  71. )
  72. func init() {
  73. var commonInitialismsForReplacer []string
  74. for _, initialism := range commonInitialisms {
  75. commonInitialismsForReplacer = append(commonInitialismsForReplacer, initialism, strings.Title(strings.ToLower(initialism)))
  76. }
  77. commonInitialismsReplacer = strings.NewReplacer(commonInitialismsForReplacer...)
  78. }
  79. func (ns NamingStrategy) toDBName(name string) string {
  80. if name == "" {
  81. return ""
  82. } else if v, ok := smap.Load(name); ok {
  83. return v.(string)
  84. }
  85. if ns.NameReplacer != nil {
  86. name = ns.NameReplacer.Replace(name)
  87. }
  88. var (
  89. value = commonInitialismsReplacer.Replace(name)
  90. buf strings.Builder
  91. lastCase, nextCase, nextNumber bool // upper case == true
  92. curCase = value[0] <= 'Z' && value[0] >= 'A'
  93. )
  94. for i, v := range value[:len(value)-1] {
  95. nextCase = value[i+1] <= 'Z' && value[i+1] >= 'A'
  96. nextNumber = value[i+1] >= '0' && value[i+1] <= '9'
  97. if curCase {
  98. if lastCase && (nextCase || nextNumber) {
  99. buf.WriteRune(v + 32)
  100. } else {
  101. if i > 0 && value[i-1] != '_' && value[i+1] != '_' {
  102. buf.WriteByte('_')
  103. }
  104. buf.WriteRune(v + 32)
  105. }
  106. } else {
  107. buf.WriteRune(v)
  108. }
  109. lastCase = curCase
  110. curCase = nextCase
  111. }
  112. if curCase {
  113. if !lastCase && len(value) > 1 {
  114. buf.WriteByte('_')
  115. }
  116. buf.WriteByte(value[len(value)-1] + 32)
  117. } else {
  118. buf.WriteByte(value[len(value)-1])
  119. }
  120. ret := buf.String()
  121. smap.Store(name, ret)
  122. return ret
  123. }