1
0
mirror of https://github.com/pbnjay/grate.git synced 2026-05-16 17:16:40 +02:00

more consistent error handling

This commit is contained in:
Jeremy Jay
2021-02-12 10:44:23 -05:00
parent f25b853fdf
commit 26e4b46e83
11 changed files with 102 additions and 43 deletions
+44
View File
@@ -0,0 +1,44 @@
package grate
import "errors"
var (
// configure at build time by adding go build arguments:
// -ldflags="-X github.com/pbnjay/grate.loglevel=debug"
loglevel string = "warn"
// Debug should be set to true to expose detailed logging.
Debug bool = (loglevel == "debug")
)
// ErrInvalidScanType is returned by Scan for invalid arguments.
var ErrInvalidScanType = errors.New("grate: Scan only supports *bool, *int, *float64, *string, *time.Time arguments")
// ErrNotInFormat is used to auto-detect file types using the defined OpenFunc
// It is returned by OpenFunc when the code does not detect correct file formats.
var ErrNotInFormat = errors.New("grate: file is not in this format")
// ErrUnknownFormat is used when grate does not know how to open a file format.
var ErrUnknownFormat = errors.New("grate: file format is not known/supported")
type errx struct {
errs []error
}
func (e errx) Error() string {
return e.errs[0].Error()
}
func (e errx) Unwrap() error {
if len(e.errs) > 1 {
return e.errs[1]
}
return nil
}
// WrapErr wraps a set of errors.
func WrapErr(e ...error) error {
if len(e) == 1 {
return e[0]
}
return errx{errs: e}
}
+3
View File
@@ -0,0 +1,3 @@
module github.com/pbnjay/grate
go 1.16
+28 -27
View File
@@ -4,6 +4,7 @@ package grate
import (
"errors"
"log"
"sort"
)
@@ -16,14 +17,32 @@ type Source interface {
Get(name string) (Collection, error)
}
// 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
}
// 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)
// ErrNotInFormat is used to auto-detect file types using the defined OpenFunc
// It is returned by OpenFunc when the code does not detect correct file formats.
var ErrNotInFormat = errors.New("grate: file is not in this format")
// Open a tabular data file and return a Source for accessing it's contents.
func Open(filename string) (Source, error) {
for _, o := range srcTable {
@@ -31,11 +50,14 @@ func Open(filename string) (Source, error) {
if err == nil {
return src, nil
}
if err != ErrNotInFormat {
if !errors.Is(err, ErrNotInFormat) {
return nil, err
}
if Debug {
log.Println(" ", filename, "is not in", o.name, "format")
}
}
return nil, errors.New("grate: file format is not known/supported")
return nil, ErrUnknownFormat
}
type srcOpenTab struct {
@@ -54,24 +76,3 @@ func Register(name string, priority int, opener OpenFunc) error {
})
return nil
}
// 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
Scan(args ...interface{}) error
// IsEmpty returns true if there are no data values.
IsEmpty() bool
// Err returns the last error that occured.
Err() error
}
+11
View File
@@ -33,6 +33,17 @@ func OpenCSV(filename string) (grate.Source, error) {
total++
t.rows = append(t.rows, rec)
}
if err != nil {
switch perr := err.(type) {
case *csv.ParseError:
return nil, grate.WrapErr(perr, grate.ErrNotInFormat)
}
if total < 10 {
// probably? not in this format
return nil, grate.WrapErr(err, grate.ErrNotInFormat)
}
return nil, err
}
// kinda arbitrary metrics for detecting CSV
looksGood := 0
+2 -2
View File
@@ -67,9 +67,9 @@ func (t *simpleFile) Scan(args ...interface{}) error {
case *string:
*v = row[i]
case *time.Time:
return errors.New("grate/simple: time.Time not supported, you must parse string manually")
return errors.New("grate/simple: time.Time not supported, you must parse date strings manually")
default:
return errors.New("grate/simple: scan destination must be one of: *bool, *int, *float64, *string, or *time.Time")
return grate.ErrInvalidScanType
}
if err != nil {
return err
+4
View File
@@ -32,6 +32,10 @@ func OpenTSV(filename string) (grate.Source, error) {
total++
t.rows = append(t.rows, r)
}
if s.Err() != nil {
// this can only be read errors, not format
return nil, s.Err()
}
// kinda arbitrary metrics for detecting TSV
looksGood := 0
+3 -1
View File
@@ -4,6 +4,8 @@ import (
"fmt"
"io"
"os"
"github.com/pbnjay/grate"
)
// Document represents a Compound File Binary Format document.
@@ -24,7 +26,7 @@ func Open(filename string) (Document, error) {
}
err = d.load(f)
if err != nil {
return nil, err
return nil, grate.WrapErr(err, grate.ErrNotInFormat)
}
return d, nil
}
+1 -4
View File
@@ -503,15 +503,12 @@ func (s *WorkSheet) Scan(args ...interface{}) error {
case *time.Time:
*v = currow.cols[i].(time.Time)
default:
return ErrInvalidType
return grate.ErrInvalidScanType
}
}
return nil
}
// ErrInvalidType is returned by Scan for invalid arguments.
var ErrInvalidType = errors.New("xls: Scan only supports *bool, *int, *float64, *string, *time.Time arguments")
var berrLookup = map[byte]string{
0x00: "#NULL!",
0x07: "#DIV/0!",
+2 -2
View File
@@ -51,7 +51,7 @@ func (b *WorkBook) IsProtected() bool {
func Open(filename string) (grate.Source, error) {
doc, err := cfb.Open(filename)
if err != nil {
return nil, grate.ErrNotInFormat //err
return nil, err
}
b := &WorkBook{
@@ -64,7 +64,7 @@ func Open(filename string) (grate.Source, error) {
rdr, err := doc.Open("Workbook")
if err != nil {
return nil, err
return nil, grate.WrapErr(err, grate.ErrNotInFormat)
}
err = b.loadFromStream(rdr)
return b, err
+1 -4
View File
@@ -252,7 +252,7 @@ func (s *Sheet) Scan(args ...interface{}) error {
case *time.Time:
*v = currow.cols[i].(time.Time)
default:
return ErrInvalidType
return grate.ErrInvalidScanType
}
}
return nil
@@ -266,6 +266,3 @@ func (s *Sheet) IsEmpty() bool {
func (s *Sheet) Err() error {
return s.err
}
// ErrInvalidType is returned by Scan for invalid arguments.
var ErrInvalidType = errors.New("xlsx: Scan only supports *bool, *int, *float64, *string, *time.Time arguments")
+3 -3
View File
@@ -40,7 +40,7 @@ func Open(filename string) (grate.Source, error) {
}
z, err := zip.NewReader(f, info.Size())
if err != nil {
return nil, grate.ErrNotInFormat //err
return nil, grate.WrapErr(err, grate.ErrNotInFormat)
}
d := &Document{
filename: filename,
@@ -52,12 +52,12 @@ func Open(filename string) (grate.Source, error) {
// parse the primary relationships
dec, c, err := d.openXML("_rels/.rels")
if err != nil {
return nil, err
return nil, grate.WrapErr(err, grate.ErrNotInFormat)
}
err = d.parseRels(dec, "")
c.Close()
if err != nil {
return nil, err
return nil, grate.WrapErr(err, grate.ErrNotInFormat)
}
if d.primaryDoc == "" {
return nil, errors.New("xlsx: invalid document")