-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtextreader.go
More file actions
106 lines (96 loc) · 3.41 KB
/
Copy pathtextreader.go
File metadata and controls
106 lines (96 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package structtable
import (
"reflect"
"github.com/domonda/go-errs"
"github.com/domonda/go-types/strfmt"
)
// TextReader implements the Reader interface for reading tabular data from
// a 2D slice of strings into struct instances.
//
// This reader is useful when you have pre-parsed tabular data as strings
// and want to populate struct instances with the data. It supports column
// mapping and custom scanning configuration.
type TextReader struct {
rows [][]string
columnMapping map[int]string
columnTitleTag string
scanConfig *strfmt.ScanConfig
}
// NewTextReader creates a new TextReader instance.
//
// This constructor initializes a TextReader with the provided data and configuration.
// The columnMapping maps column indices to struct field names, and the columnTitleTag
// specifies which struct tag to use for field name resolution.
//
// Parameters:
// - rows: The 2D slice of strings containing the tabular data
// - columnMapping: Map from column index to struct field name
// - columnTitleTag: The struct tag to use for field name resolution
// - scanConfig: Optional scanning configuration (uses default if nil)
//
// Returns:
// - A new TextReader instance ready for use
func NewTextReader(rows [][]string, columnMapping map[int]string, columnTitleTag string, scanConfig ...*strfmt.ScanConfig) *TextReader {
tr := &TextReader{
rows: rows,
columnMapping: columnMapping,
columnTitleTag: columnTitleTag,
scanConfig: strfmt.DefaultScanConfig,
}
if len(scanConfig) > 0 && scanConfig[0] != nil {
tr.scanConfig = scanConfig[0]
}
return tr
}
// NumRows returns the total number of rows available for reading.
//
// This method implements the Reader interface and returns the count
// of rows in the underlying data.
func (tr *TextReader) NumRows() int {
return len(tr.rows)
}
// ReadRow populates a struct instance with data from the specified row.
//
// This method implements the Reader interface and populates the destStruct
// with data from the row at the given index. It uses the columnMapping to
// determine which columns correspond to which struct fields, and uses the
// columnTitleTag to resolve field names from struct tags.
//
// Parameters:
// - index: The row index to read (0-based)
// - destStruct: The reflect.Value of the struct to populate
//
// Returns:
// - err: Any error that occurred during reading or field population
func (tr *TextReader) ReadRow(index int, destStruct reflect.Value) error {
if index < 0 || index >= len(tr.rows) {
return errs.Errorf("row index %d out of range [0..%d)", index, len(tr.rows))
}
row := tr.rows[index]
for col, name := range tr.columnMapping {
if col < 0 || col >= len(row) {
return errs.Errorf("row %d column index %d out of range [0..%d)", index, col, len(row))
}
// Find struct field with name
var destVal reflect.Value
for i := 0; i < destStruct.NumField(); i++ {
fieldType := destStruct.Type().Field(i)
fieldName := fieldType.Name
if tag := fieldType.Tag.Get(tr.columnTitleTag); tag != "" {
fieldName = tag
}
if fieldName == name {
destVal = destStruct.Field(i)
break
}
}
if !destVal.IsValid() {
return errs.Errorf("no struct field %q found in %s using tag %q", name, destStruct.Type(), tr.columnTitleTag)
}
err := strfmt.Scan(destVal, row[col], tr.scanConfig)
if err != nil {
return errs.Errorf("error reading row %d, column %d: %w", index, col, err)
}
}
return nil
}