github.com/lenartj/cfitsio@v0.0.0-20210325092924-ef692a403eb8/rows.go (about)

     1  package cfitsio
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  )
     7  
     8  // Rows is the result of a query on a FITS Table.
     9  // Its cursors starts before the first row of the result set.
    10  // Use Next to advance through the rows:
    11  //
    12  //  rows, err := table.Read(0, -1)
    13  //  ...
    14  //  for rows.Next() {
    15  //      var id int
    16  //      var x float64
    17  //      err = rows.Scan(&id, &x)
    18  //      ...
    19  //  }
    20  //  err = rows.Err() // get any error encountered during iteration
    21  //  ...
    22  //
    23  type Rows struct {
    24  	table  *Table
    25  	cols   []int // list of (active) column indices
    26  	i      int64 // number of rows iterated over
    27  	n      int64 // number of rows this iterator iters over
    28  	inc    int64 // number of rows to increment by at each iteration
    29  	cur    int64 // current row index
    30  	closed bool
    31  	err    error // last error
    32  
    33  	// cache of type -> slice of (struct-field-index,col-index)
    34  	// used by scanStruct
    35  	icols map[reflect.Type][][2]int
    36  }
    37  
    38  // Err returns the error, if any, that was encountered during iteration.
    39  // Err may be called after an explicit or implicit Close.
    40  func (rows *Rows) Err() error {
    41  	return rows.err
    42  }
    43  
    44  // Close closes the Rows, preventing further enumeration.
    45  // Close is idempotent and does not affect the result of Err.
    46  func (rows *Rows) Close() error {
    47  	if rows.closed {
    48  		return nil
    49  	}
    50  	rows.closed = true
    51  	rows.table = nil
    52  	return nil
    53  }
    54  
    55  // Scan copies the columns in the current row into the values pointed at by
    56  // dest.
    57  func (rows *Rows) Scan(args ...interface{}) error {
    58  	var err error
    59  	defer func() {
    60  		rows.err = err
    61  	}()
    62  
    63  	switch len(args) {
    64  	case 0:
    65  		// special case: read everything into the cols.
    66  		return rows.scanAll()
    67  	case 1:
    68  		// maybe special case: map? struct?
    69  		rt := reflect.TypeOf(args[0]).Elem()
    70  		switch rt.Kind() {
    71  		case reflect.Map:
    72  			return rows.scanMap(*args[0].(*map[string]interface{}))
    73  		case reflect.Struct:
    74  			return rows.scanStruct(args[0])
    75  		}
    76  	}
    77  
    78  	return rows.scan(args...)
    79  }
    80  
    81  func (rows *Rows) scan(args ...interface{}) error {
    82  	var err error
    83  	if len(args) != len(rows.cols) {
    84  		return fmt.Errorf(
    85  			"cfitsio.Rows.Scan: invalid number of arguments (got %d. expected %d)",
    86  			len(args),
    87  			len(rows.cols),
    88  		)
    89  	}
    90  	for i, icol := range rows.cols {
    91  		// if rows.table.cols[icol].Name == "int8s" {
    92  		// 	rv := reflect.ValueOf(args[i]).Elem().Interface()
    93  		// 	fmt.Printf(">>>[%v]: [%v](%T) [%v](%T)\n", rows.cur,
    94  		// 		rows.table.cols[icol].Value, rows.table.cols[icol].Value,
    95  		// 		rv, rv)
    96  		// }
    97  		err = rows.table.cols[icol].read(rows.table.f, icol, rows.cur, args[i])
    98  		if err != nil {
    99  			return err
   100  		}
   101  		// if rows.table.cols[icol].Name == "int8s" {
   102  		// 	rv := reflect.ValueOf(args[i]).Elem().Interface()
   103  		// 	fmt.Printf("<<<[%v]: [%v] [%v]\n", rows.cur, rows.table.cols[icol].Value, rv)
   104  		// }
   105  	}
   106  	return err
   107  }
   108  
   109  func (rows *Rows) scanAll() error {
   110  	var err error
   111  	for _, icol := range rows.cols {
   112  		col := &rows.table.cols[icol]
   113  		err = col.read(rows.table.f, icol, rows.cur, &col.Value)
   114  		if err != nil {
   115  			return err
   116  		}
   117  	}
   118  	return err
   119  }
   120  
   121  func (rows *Rows) scanMap(data map[string]interface{}) error {
   122  	var err error
   123  
   124  	icols := make([]int, 0, len(data))
   125  	switch len(data) {
   126  	case 0:
   127  		icols = make([]int, len(rows.cols))
   128  		for i := range rows.cols {
   129  			icols[i] = i
   130  		}
   131  	default:
   132  		for k := range data {
   133  			icol := rows.table.Index(k)
   134  			if icol >= 0 {
   135  				icols = append(icols, icol)
   136  			}
   137  		}
   138  	}
   139  
   140  	for _, icol := range icols {
   141  		col := rows.table.Col(icol)
   142  		err = col.read(rows.table.f, icol, rows.cur, &col.Value)
   143  		if err != nil {
   144  			return err
   145  		}
   146  		data[col.Name] = col.Value
   147  	}
   148  
   149  	return err
   150  }
   151  
   152  func (rows *Rows) scanStruct(data interface{}) error {
   153  	var err error
   154  
   155  	rt := reflect.TypeOf(data).Elem()
   156  	rv := reflect.ValueOf(data).Elem()
   157  	if _, ok := rows.icols[rt]; !ok {
   158  		icols := make([][2]int, 0, rt.NumField())
   159  		for i := 0; i < rt.NumField(); i++ {
   160  			f := rt.Field(i)
   161  			n := f.Tag.Get("fits")
   162  			if n == "" {
   163  				n = f.Name
   164  			}
   165  			icol := rows.table.Index(n)
   166  			if icol >= 0 {
   167  				icols = append(icols, [2]int{i, icol})
   168  			}
   169  		}
   170  		rows.icols[rt] = icols
   171  	}
   172  	icols := rows.icols[rt]
   173  	for _, icol := range icols {
   174  		col := &rows.table.cols[icol[1]]
   175  		value := rv.Field(icol[0]).Addr().Interface()
   176  		err = col.read(rows.table.f, icol[1], rows.cur, value)
   177  		if err != nil {
   178  			return err
   179  		}
   180  		col.Value = rv.Field(icol[0]).Interface()
   181  	}
   182  	return err
   183  }
   184  
   185  // Next prepares the next result row for reading with the Scan method.
   186  // It returns true on success, false if there is no next result row.
   187  // Every call to Scan, even the first one, must be preceded by a call to Next.
   188  func (rows *Rows) Next() bool {
   189  	if rows.closed {
   190  		return false
   191  	}
   192  	next := rows.i < rows.n
   193  	rows.cur += rows.inc
   194  	rows.i += rows.inc
   195  	if !next {
   196  		rows.err = rows.Close()
   197  	}
   198  	return next
   199  }
   200  
   201  // EOF