package orm import ( "reflect" "sync" "github.com/go-openapi/inflect" ) type table struct { rt reflect.Type rv reflect.Value l *sync.RWMutex tFn, cFn MapperFn cols columns rels relations mapped bool } // getPrimaryKey tries to find primary key func (t *table) getPrimaryKey() *column { var pkey *column if c := t.hasTaggedColumn("primary"); c != nil { pkey = c } else if c := t.hasColumn("Id"); c != nil { pkey = c } return pkey } // hasTaggetColumn checks for a collumn tagget as func (t *table) hasTaggedColumn(ct string) *column { for _, col := range t.cols { if col.hasTag(ct) { return &col } } return nil } // hasColumn checks for a column by name func (t *table) hasColumn(c string) *column { for _, col := range t.cols { if col.ref == t.cFn(c) { return &col } } return nil } // addField from struct; both «relations» and «columns» // Communicates over a channel, to do relation mapping func (t *table) addField(f field, cbCh chan<- mapperCallback) { if f.hasTags("omit", "-") { return // Skip "omitted" and "dashed" fields } switch f.getType() { case Relation: // Make callback to itself, relations should be mapped after columns cbCh <- mapperCallback{ from: t.getStructName(), to: t.getStructName(), fn: func(self *table) { // Get Primary Key pkey := t.getPrimaryKey() // Or at least try to if pkey == nil { return } // Predict related table name rtbl := f.getFieldType() // And check for tag if tag, ok := f.getTag("table"); ok { rtbl = tag } // Predict column name cn := f.getFieldName() + "Id" // And check for tag if tag, ok := f.getTag("fkey"); ok { cn = tag } // Check if it contains reference itself if c := self.hasColumn(cn); c != nil { // Make a call to load related table into scope; // we need its addr and must be loaded from mapper cbCh <- mapperCallback{ from: self.getStructName(), to: rtbl, fn: func(tbl *table) { key := tbl.getPrimaryKey() self.rels.addRelation(belongsTo, relation{tbl, f, *key, *c}) }, } } else { // Or predict column name in related table cn = self.getStructName() + "Id" // Check for reference tag if tag, ok := f.getTag("ref"); ok { cn = tag } // Make a callback to the related table to check for relationg cbCh <- mapperCallback{ from: self.getStructName(), to: rtbl, fn: func(tbl *table) { // Check for relation on column mane if c := tbl.hasColumn(cn); c != nil { // Predict the relations is «hasOne» has := hasOne // Try to predict (or simply guess) with pluralization, if «hasMany» if inflect.Pluralize(f.getFieldName()) == f.getFieldName() { has = hasMany } // Override with tagging if specified if tag, ok := f.getTag("has"); ok { switch tag { case "many": has = hasMany case "one": has = hasOne } } self.rels.addRelation(has, relation{tbl, f, *c, *pkey}) } }, } } return }, } default: // Add column switch f.getKind() { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: fallthrough // Support all Uint types case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: fallthrough // Support all Int types case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: fallthrough // Support all Float and Complex types case reflect.String, reflect.Bool: // Support string and boolean // Map column name dbf := t.cFn(f.getFieldName()) // override with tagging if tag, ok := f.getTag("field"); ok { dbf = t.cFn(tag) } t.cols = append(t.cols, column{ f, dbf, }) } } } // getType returns the reflect.Type of the «table» func (t *table) getType() reflect.Type { return t.rt } // getValue returns the reflect.Value of the «table» func (t *table) getValue() reflect.Value { return t.rv } // isMapped returns true when columns is mapped // Not relations! They will be mapped in separate routines func (t *table) isMapped() bool { return t.mapped } // Lock read lock func (t *table) Lock() *table { t.l.RLock() return t } // Unlock read lock func (t *table) Unlock() { t.l.RUnlock() } // lock write lock func (t *table) lock() *table { t.l.Lock() return t } // unlock write lock func (t *table) unlock() { t.l.Unlock() } // getStructName returns the name of the struct func (t *table) getStructName() string { return t.getType().Name() } // getName returns the mapped table name // as identified in the database func (t *table) getName() string { return t.tFn(t.getType().Name()) } // tables is simply a collection of tables type tables map[string]*table