mapper.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. package core
  2. import (
  3. "strings"
  4. "sync"
  5. )
  6. // name translation between struct, fields names and table, column names
  7. type IMapper interface {
  8. Obj2Table(string) string
  9. Table2Obj(string) string
  10. }
  11. type CacheMapper struct {
  12. oriMapper IMapper
  13. obj2tableCache map[string]string
  14. obj2tableMutex sync.RWMutex
  15. table2objCache map[string]string
  16. table2objMutex sync.RWMutex
  17. }
  18. func NewCacheMapper(mapper IMapper) *CacheMapper {
  19. return &CacheMapper{oriMapper: mapper, obj2tableCache: make(map[string]string),
  20. table2objCache: make(map[string]string),
  21. }
  22. }
  23. func (m *CacheMapper) Obj2Table(o string) string {
  24. m.obj2tableMutex.RLock()
  25. t, ok := m.obj2tableCache[o]
  26. m.obj2tableMutex.RUnlock()
  27. if ok {
  28. return t
  29. }
  30. t = m.oriMapper.Obj2Table(o)
  31. m.obj2tableMutex.Lock()
  32. m.obj2tableCache[o] = t
  33. m.obj2tableMutex.Unlock()
  34. return t
  35. }
  36. func (m *CacheMapper) Table2Obj(t string) string {
  37. m.table2objMutex.RLock()
  38. o, ok := m.table2objCache[t]
  39. m.table2objMutex.RUnlock()
  40. if ok {
  41. return o
  42. }
  43. o = m.oriMapper.Table2Obj(t)
  44. m.table2objMutex.Lock()
  45. m.table2objCache[t] = o
  46. m.table2objMutex.Unlock()
  47. return o
  48. }
  49. // SameMapper implements IMapper and provides same name between struct and
  50. // database table
  51. type SameMapper struct {
  52. }
  53. func (m SameMapper) Obj2Table(o string) string {
  54. return o
  55. }
  56. func (m SameMapper) Table2Obj(t string) string {
  57. return t
  58. }
  59. // SnakeMapper implements IMapper and provides name transaltion between
  60. // struct and database table
  61. type SnakeMapper struct {
  62. }
  63. func snakeCasedName(name string) string {
  64. newstr := make([]rune, 0)
  65. for idx, chr := range name {
  66. if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
  67. if idx > 0 {
  68. newstr = append(newstr, '_')
  69. }
  70. chr -= ('A' - 'a')
  71. }
  72. newstr = append(newstr, chr)
  73. }
  74. return string(newstr)
  75. }
  76. func (mapper SnakeMapper) Obj2Table(name string) string {
  77. return snakeCasedName(name)
  78. }
  79. func titleCasedName(name string) string {
  80. newstr := make([]rune, 0)
  81. upNextChar := true
  82. name = strings.ToLower(name)
  83. for _, chr := range name {
  84. switch {
  85. case upNextChar:
  86. upNextChar = false
  87. if 'a' <= chr && chr <= 'z' {
  88. chr -= ('a' - 'A')
  89. }
  90. case chr == '_':
  91. upNextChar = true
  92. continue
  93. }
  94. newstr = append(newstr, chr)
  95. }
  96. return string(newstr)
  97. }
  98. func (mapper SnakeMapper) Table2Obj(name string) string {
  99. return titleCasedName(name)
  100. }
  101. // GonicMapper implements IMapper. It will consider initialisms when mapping names.
  102. // E.g. id -> ID, user -> User and to table names: UserID -> user_id, MyUID -> my_uid
  103. type GonicMapper map[string]bool
  104. func isASCIIUpper(r rune) bool {
  105. return 'A' <= r && r <= 'Z'
  106. }
  107. func toASCIIUpper(r rune) rune {
  108. if 'a' <= r && r <= 'z' {
  109. r -= ('a' - 'A')
  110. }
  111. return r
  112. }
  113. func gonicCasedName(name string) string {
  114. newstr := make([]rune, 0, len(name)+3)
  115. for idx, chr := range name {
  116. if isASCIIUpper(chr) && idx > 0 {
  117. if !isASCIIUpper(newstr[len(newstr)-1]) {
  118. newstr = append(newstr, '_')
  119. }
  120. }
  121. if !isASCIIUpper(chr) && idx > 1 {
  122. l := len(newstr)
  123. if isASCIIUpper(newstr[l-1]) && isASCIIUpper(newstr[l-2]) {
  124. newstr = append(newstr, newstr[l-1])
  125. newstr[l-1] = '_'
  126. }
  127. }
  128. newstr = append(newstr, chr)
  129. }
  130. return strings.ToLower(string(newstr))
  131. }
  132. func (mapper GonicMapper) Obj2Table(name string) string {
  133. return gonicCasedName(name)
  134. }
  135. func (mapper GonicMapper) Table2Obj(name string) string {
  136. newstr := make([]rune, 0)
  137. name = strings.ToLower(name)
  138. parts := strings.Split(name, "_")
  139. for _, p := range parts {
  140. _, isInitialism := mapper[strings.ToUpper(p)]
  141. for i, r := range p {
  142. if i == 0 || isInitialism {
  143. r = toASCIIUpper(r)
  144. }
  145. newstr = append(newstr, r)
  146. }
  147. }
  148. return string(newstr)
  149. }
  150. // A GonicMapper that contains a list of common initialisms taken from golang/lint
  151. var LintGonicMapper = GonicMapper{
  152. "API": true,
  153. "ASCII": true,
  154. "CPU": true,
  155. "CSS": true,
  156. "DNS": true,
  157. "EOF": true,
  158. "GUID": true,
  159. "HTML": true,
  160. "HTTP": true,
  161. "HTTPS": true,
  162. "ID": true,
  163. "IP": true,
  164. "JSON": true,
  165. "LHS": true,
  166. "QPS": true,
  167. "RAM": true,
  168. "RHS": true,
  169. "RPC": true,
  170. "SLA": true,
  171. "SMTP": true,
  172. "SSH": true,
  173. "TLS": true,
  174. "TTL": true,
  175. "UI": true,
  176. "UID": true,
  177. "UUID": true,
  178. "URI": true,
  179. "URL": true,
  180. "UTF8": true,
  181. "VM": true,
  182. "XML": true,
  183. "XSRF": true,
  184. "XSS": true,
  185. }
  186. // provide prefix table name support
  187. type PrefixMapper struct {
  188. Mapper IMapper
  189. Prefix string
  190. }
  191. func (mapper PrefixMapper) Obj2Table(name string) string {
  192. return mapper.Prefix + mapper.Mapper.Obj2Table(name)
  193. }
  194. func (mapper PrefixMapper) Table2Obj(name string) string {
  195. return mapper.Mapper.Table2Obj(name[len(mapper.Prefix):])
  196. }
  197. func NewPrefixMapper(mapper IMapper, prefix string) PrefixMapper {
  198. return PrefixMapper{mapper, prefix}
  199. }
  200. // provide suffix table name support
  201. type SuffixMapper struct {
  202. Mapper IMapper
  203. Suffix string
  204. }
  205. func (mapper SuffixMapper) Obj2Table(name string) string {
  206. return mapper.Mapper.Obj2Table(name) + mapper.Suffix
  207. }
  208. func (mapper SuffixMapper) Table2Obj(name string) string {
  209. return mapper.Mapper.Table2Obj(name[:len(name)-len(mapper.Suffix)])
  210. }
  211. func NewSuffixMapper(mapper IMapper, suffix string) SuffixMapper {
  212. return SuffixMapper{mapper, suffix}
  213. }