github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/sqlx/scanner/struct.go (about) 1 package scanner 2 3 import ( 4 "context" 5 "database/sql" 6 "reflect" 7 "strings" 8 9 "github.com/pkg/errors" 10 11 "github.com/machinefi/w3bstream/pkg/depends/kit/sqlx/builder" 12 "github.com/machinefi/w3bstream/pkg/depends/kit/sqlx/scanner/nullable" 13 "github.com/machinefi/w3bstream/pkg/depends/x/reflectx" 14 ) 15 16 type RowScanner interface { 17 Scan(...interface{}) error 18 } 19 20 type WithColumnReceivers interface { 21 ColumnReceivers() map[string]interface{} 22 } 23 24 func ScanRows(ctx context.Context, rows *sql.Rows, v interface{}) error { 25 t := reflect.TypeOf(v) 26 27 if t.Kind() != reflect.Ptr { 28 return errors.Errorf("scan target must be a ptr") 29 } 30 31 if scanner, ok := v.(sql.Scanner); ok { 32 return rows.Scan(scanner) 33 } 34 35 t = reflectx.DeRef(t) 36 37 switch t.Kind() { 38 default: 39 return rows.Scan(nullable.NewNullIgnoreScanner(v)) 40 case reflect.Struct: 41 cols, err := rows.Columns() 42 if err != nil { 43 return err 44 } 45 colc := len(cols) 46 if colc < 1 { 47 return nil 48 } 49 dst := make([]interface{}, colc) 50 holder := EmptyScanner(0) 51 52 if with, ok := v.(WithColumnReceivers); ok { 53 receivers := with.ColumnReceivers() 54 for i, name := range cols { 55 if c, ok := receivers[strings.ToLower(name)]; ok { 56 dst[i] = nullable.NewNullIgnoreScanner(c) 57 } else { 58 dst[i] = &holder 59 } 60 } 61 return rows.Scan(dst...) 62 } 63 64 indexes := map[string]int{} 65 for i, name := range cols { 66 indexes[strings.ToLower(name)] = i 67 dst[i] = &holder 68 } 69 70 builder.ForEachFieldValue(ctx, v, func(fv *builder.FieldValue) { 71 if fv.TableName != "" { 72 name := fv.TableName + "__" + fv.Field.Name 73 if i, ok := indexes[name]; ok && i > -1 { 74 dst[i] = nullable.NewNullIgnoreScanner(fv.Value.Addr().Interface()) 75 } 76 } 77 if i, ok := indexes[fv.Field.Name]; ok && i > -1 { 78 dst[i] = nullable.NewNullIgnoreScanner(fv.Value.Addr().Interface()) 79 } 80 }) 81 82 return rows.Scan(dst...) 83 } 84 } 85 86 type EmptyScanner int 87 88 func (s *EmptyScanner) Scan(_ interface{}) error { return nil }