github.com/astrogo/cfitsio@v0.1.0/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