2021-02-12 07:50:50 +02:00
|
|
|
// Package grate opens tabular data files (such as spreadsheets and delimited plaintext files)
|
|
|
|
// and allows programmatic access to the data contents in a consistent interface.
|
|
|
|
package grate
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2021-02-12 17:44:23 +02:00
|
|
|
"log"
|
2021-02-12 07:59:01 +02:00
|
|
|
"sort"
|
2021-02-12 07:50:50 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Source represents a set of data collections.
|
|
|
|
type Source interface {
|
|
|
|
// List the individual data tables within this source.
|
|
|
|
List() ([]string, error)
|
|
|
|
|
|
|
|
// Get a Collection from the source by name.
|
|
|
|
Get(name string) (Collection, error)
|
|
|
|
}
|
|
|
|
|
2021-02-12 17:44:23 +02:00
|
|
|
// Collection represents an iterable collection of records.
|
|
|
|
type Collection interface {
|
|
|
|
// Next advances to the next record of content.
|
|
|
|
// It MUST be called prior to any Scan().
|
|
|
|
Next() bool
|
|
|
|
|
|
|
|
// Strings extracts values from the current record into a list of strings.
|
|
|
|
Strings() []string
|
|
|
|
|
|
|
|
// Scan extracts values from the current record into the provided arguments
|
|
|
|
// Arguments must be pointers to one of 5 supported types:
|
|
|
|
// bool, int, float64, string, or time.Time
|
|
|
|
// If invalid, returns ErrInvalidScanType
|
|
|
|
Scan(args ...interface{}) error
|
|
|
|
|
|
|
|
// IsEmpty returns true if there are no data values.
|
|
|
|
IsEmpty() bool
|
|
|
|
|
|
|
|
// Err returns the last error that occured.
|
|
|
|
Err() error
|
|
|
|
}
|
|
|
|
|
2021-02-12 07:50:50 +02:00
|
|
|
// OpenFunc defines a Source's instantiation function.
|
|
|
|
// It should return ErrNotInFormat immediately if filename is not of the correct file type.
|
|
|
|
type OpenFunc func(filename string) (Source, error)
|
|
|
|
|
|
|
|
// Open a tabular data file and return a Source for accessing it's contents.
|
|
|
|
func Open(filename string) (Source, error) {
|
|
|
|
for _, o := range srcTable {
|
2021-02-12 07:59:01 +02:00
|
|
|
src, err := o.op(filename)
|
2021-02-12 07:50:50 +02:00
|
|
|
if err == nil {
|
|
|
|
return src, nil
|
|
|
|
}
|
2021-02-12 17:44:23 +02:00
|
|
|
if !errors.Is(err, ErrNotInFormat) {
|
2021-02-12 07:50:50 +02:00
|
|
|
return nil, err
|
|
|
|
}
|
2021-02-12 17:44:23 +02:00
|
|
|
if Debug {
|
|
|
|
log.Println(" ", filename, "is not in", o.name, "format")
|
|
|
|
}
|
2021-02-12 07:50:50 +02:00
|
|
|
}
|
2021-02-12 17:44:23 +02:00
|
|
|
return nil, ErrUnknownFormat
|
2021-02-12 07:50:50 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 07:59:01 +02:00
|
|
|
type srcOpenTab struct {
|
|
|
|
name string
|
|
|
|
pri int
|
|
|
|
op OpenFunc
|
|
|
|
}
|
|
|
|
|
|
|
|
var srcTable = make([]*srcOpenTab, 0, 20)
|
2021-02-12 07:50:50 +02:00
|
|
|
|
|
|
|
// Register the named source as a grate datasource implementation.
|
2021-02-12 07:59:01 +02:00
|
|
|
func Register(name string, priority int, opener OpenFunc) error {
|
|
|
|
srcTable = append(srcTable, &srcOpenTab{name: name, pri: priority, op: opener})
|
|
|
|
sort.Slice(srcTable, func(i, j int) bool {
|
|
|
|
return srcTable[i].pri < srcTable[j].pri
|
|
|
|
})
|
2021-02-12 07:50:50 +02:00
|
|
|
return nil
|
|
|
|
}
|