package stardb import ( "context" "reflect" ) // ScanEachORMFunc is called for each mapped struct in streaming ORM mode. type ScanEachORMFunc func(target interface{}) error // ScanEachORM streams query rows and maps each row to target before invoking fn. // target must be a pointer to struct; it is reused for each row. func (s *StarDB) ScanEachORM(query string, target interface{}, fn ScanEachORMFunc, args ...interface{}) error { return s.ScanEachORMContext(nil, query, target, fn, args...) } // ScanEachORMContext streams query rows with context and maps each row to target before invoking fn. // target must be a pointer to struct; it is reused for each row. func (s *StarDB) ScanEachORMContext(ctx context.Context, query string, target interface{}, fn ScanEachORMFunc, args ...interface{}) error { if fn == nil { return ErrScanORMFuncNil } if err := validateScanORMTarget(target); err != nil { return err } return s.ScanEachContext(ctx, query, func(row *StarResult) error { if err := mapResultToStructTarget(row, target, s); err != nil { return err } return fn(target) }, args...) } // ScanEachORM streams transaction rows and maps each row to target before invoking fn. // target must be a pointer to struct; it is reused for each row. func (t *StarTx) ScanEachORM(query string, target interface{}, fn ScanEachORMFunc, args ...interface{}) error { return t.ScanEachORMContext(nil, query, target, fn, args...) } // ScanEachORMContext streams transaction rows with context and maps each row to target before invoking fn. // target must be a pointer to struct; it is reused for each row. func (t *StarTx) ScanEachORMContext(ctx context.Context, query string, target interface{}, fn ScanEachORMFunc, args ...interface{}) error { if fn == nil { return ErrScanORMFuncNil } if err := validateScanORMTarget(target); err != nil { return err } return t.ScanEachContext(ctx, query, func(row *StarResult) error { if err := mapResultToStructTarget(row, target, t.db); err != nil { return err } return fn(target) }, args...) } // ScanEachORM streams prepared statement rows and maps each row to target before invoking fn. // target must be a pointer to struct; it is reused for each row. func (s *StarStmt) ScanEachORM(target interface{}, fn ScanEachORMFunc, args ...interface{}) error { return s.ScanEachORMContext(nil, target, fn, args...) } // ScanEachORMContext streams prepared statement rows with context and maps each row to target before invoking fn. // target must be a pointer to struct; it is reused for each row. func (s *StarStmt) ScanEachORMContext(ctx context.Context, target interface{}, fn ScanEachORMFunc, args ...interface{}) error { if fn == nil { return ErrScanORMFuncNil } if err := validateScanORMTarget(target); err != nil { return err } return s.ScanEachContext(ctx, func(row *StarResult) error { if err := mapResultToStructTarget(row, target, s.db); err != nil { return err } return fn(target) }, args...) } func validateScanORMTarget(target interface{}) error { if target == nil { return ErrTargetNil } targetType := reflect.TypeOf(target) targetValue := reflect.ValueOf(target) if targetType.Kind() != reflect.Ptr { return ErrTargetNotPointer } if targetValue.IsNil() { return ErrTargetPointerNil } if targetValue.Elem().Kind() != reflect.Struct { return ErrTargetNotStruct } return nil } func mapResultToStructTarget(row *StarResult, target interface{}, db *StarDB) error { targetValue := reflect.ValueOf(target) targetValue.Elem().Set(reflect.Zero(targetValue.Elem().Type())) rowWrapper := &StarRows{ db: db, length: 1, columns: row.columns, columnsType: row.columnsType, columnIndex: row.columnIndex, data: [][]interface{}{row.result}, parsed: true, } return rowWrapper.setStructFieldsFromRow(target, "db", 0) }