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 }