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:
@@ -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}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user