github.com/ydb-platform/ydb-go-sdk/v3@v3.57.0/internal/query/scanner/struct.go (about) 1 package scanner 2 3 import ( 4 "fmt" 5 "reflect" 6 "strings" 7 8 "github.com/ydb-platform/ydb-go-sdk/v3/internal/value" 9 "github.com/ydb-platform/ydb-go-sdk/v3/internal/xerrors" 10 ) 11 12 type scanStructSettings struct { 13 TagName string 14 AllowMissingColumnsFromSelect bool 15 AllowMissingFieldsInStruct bool 16 } 17 18 type StructScanner struct { 19 data *data 20 } 21 22 func Struct(data *data) StructScanner { 23 return StructScanner{ 24 data: data, 25 } 26 } 27 28 func fieldName(f reflect.StructField, tagName string) string { //nolint:gocritic 29 if name, has := f.Tag.Lookup(tagName); has { 30 return name 31 } 32 33 return f.Name 34 } 35 36 func (s StructScanner) ScanStruct(dst interface{}, opts ...ScanStructOption) error { 37 settings := scanStructSettings{ 38 TagName: "sql", 39 AllowMissingColumnsFromSelect: false, 40 AllowMissingFieldsInStruct: false, 41 } 42 for _, opt := range opts { 43 opt.applyScanStructOption(&settings) 44 } 45 ptr := reflect.ValueOf(dst) 46 if ptr.Kind() != reflect.Pointer { 47 return xerrors.WithStackTrace(fmt.Errorf("%w: '%s'", errDstTypeIsNotAPointer, ptr.Kind().String())) 48 } 49 if ptr.Elem().Kind() != reflect.Struct { 50 return xerrors.WithStackTrace(fmt.Errorf("%w: '%s'", errDstTypeIsNotAPointerToStruct, ptr.Elem().Kind().String())) 51 } 52 tt := ptr.Elem().Type() 53 missingColumns := make([]string, 0, len(s.data.columns)) 54 existingFields := make(map[string]struct{}, tt.NumField()) 55 for i := 0; i < tt.NumField(); i++ { 56 name := fieldName(tt.Field(i), settings.TagName) 57 v, err := s.data.seekByName(name) 58 if err != nil { 59 missingColumns = append(missingColumns, name) 60 } else { 61 if err = value.CastTo(v, ptr.Elem().Field(i).Addr().Interface()); err != nil { 62 return xerrors.WithStackTrace(err) 63 } 64 existingFields[name] = struct{}{} 65 } 66 } 67 68 if !settings.AllowMissingColumnsFromSelect && len(missingColumns) > 0 { 69 return xerrors.WithStackTrace( 70 fmt.Errorf("%w: '%v'", errColumnsNotFoundInRow, strings.Join(missingColumns, "','")), 71 ) 72 } 73 74 if !settings.AllowMissingFieldsInStruct { 75 missingFields := make([]string, 0, tt.NumField()) 76 for _, c := range s.data.columns { 77 if _, has := existingFields[c.GetName()]; !has { 78 missingFields = append(missingFields, c.GetName()) 79 } 80 } 81 if len(missingFields) > 0 { 82 return xerrors.WithStackTrace( 83 fmt.Errorf("%w: '%v'", errFieldsNotFoundInStruct, strings.Join(missingFields, "','")), 84 ) 85 } 86 } 87 88 return nil 89 }