-
Notifications
You must be signed in to change notification settings - Fork 0
/
multiparser.go
75 lines (64 loc) · 1.61 KB
/
multiparser.go
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
package multiparser
import (
"errors"
"fmt"
"reflect"
)
// Parser implements raw data to object deserialization.
type Parser interface {
Parse(from []byte, to interface{}) error
}
var (
ErrEmptyParsers = errors.New("no Parser passed, at least one required")
ErrInvalidObject = errors.New("object must be non-nil pointer")
)
type multiParser struct {
parsers []Parser
}
// New creates a new Parser which can be used to serialize data from different
// formats.
func New(parsers ...Parser) (Parser, error) {
if len(parsers) == 0 {
return nil, ErrEmptyParsers
}
return &multiParser{
parsers: parsers,
}, nil
}
func (p *multiParser) Parse(from []byte, to interface{}) error {
// Validate
if rv := reflect.ValueOf(to); to == nil || rv.Kind() != reflect.Pointer || rv.IsNil() {
return ErrInvalidObject
}
// Try every Parser, if any of them succeeds, that's our result.
// The assumption is that Parsing will fail fast.
var err error
for _, parser := range p.parsers {
fromPtr := reflect.New(reflect.TypeOf(to).Elem()).Interface()
parseErr := parser.Parse(from, fromPtr)
if parseErr == nil {
// Update result
reflect.ValueOf(to).Elem().Set(reflect.ValueOf(fromPtr).Elem())
return nil
} else {
err = errors.Join(err, &ParserError{
Parser: fmt.Sprintf("%T", parser),
Err: parseErr,
})
}
}
return &ParserError{
Parser: fmt.Sprintf("%T", p),
Err: err,
}
}
type ParserError struct {
Parser string
Err error
}
func (e *ParserError) Error() string {
return fmt.Sprintf("parsing failed for %s: %v", e.Parser, e.Err)
}
func (e *ParserError) Unwrap() error {
return e.Err
}