github.com/systematiccaos/gorm@v1.22.6/schema/naming.go (about)

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