-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcxpath.go
More file actions
134 lines (120 loc) · 2.94 KB
/
cxpath.go
File metadata and controls
134 lines (120 loc) · 2.94 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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
package cxpath
import (
"fmt"
"io"
"iter"
"os"
"github.com/speedata/goxpath"
)
// A Context stores the current state of the XPath parser.
type Context struct {
P *goxpath.Parser
Seq goxpath.Sequence
Error error
}
// NewFromFile returns a new context from a file name.
func NewFromFile(filename string) (*Context, error) {
r, err := os.Open(filename)
if err != nil {
return nil, err
}
defer r.Close()
return NewFromReader(r)
}
// NewFromReader returns a new context from a reader.
func NewFromReader(r io.Reader) (*Context, error) {
p, err := goxpath.NewParser(r)
if err != nil {
return nil, err
}
ctx := Context{
P: p,
}
return &ctx, nil
}
// String returns the string value of the current sequence.
func (ctx *Context) String() string {
return ctx.Seq.Stringvalue()
}
// Int returns the number value as an integer
func (ctx *Context) Int() int {
i, err := ctx.Seq.IntValue()
if err != nil {
ctx.Error = err
}
return i
}
// Bool returns the boolean value of the sequence.
func (ctx *Context) Bool() bool {
b, err := goxpath.BooleanValue(ctx.Seq)
if err != nil {
ctx.Error = err
}
return b
}
// SetNamespace sets a prefix/URI pair for XPath queries.
func (ctx *Context) SetNamespace(prefix, uri string) *Context {
ctx.P.Ctx.Namespaces[prefix] = uri
return ctx
}
// Root returns the top most element of the XML file.
func (ctx *Context) Root() *Context {
newContext := Context{
P: &goxpath.Parser{
Ctx: goxpath.CopyContext(ctx.P.Ctx),
},
}
newContext.Seq, newContext.Error = newContext.P.Ctx.Root()
return &newContext
}
// Each can be used as an iterator which returns a Context for each item in the
// resulting sequence.
func (ctx *Context) Each(eval string) iter.Seq[*Context] {
p := &goxpath.Parser{
Ctx: goxpath.CopyContext(ctx.P.Ctx),
}
seq, err := safeEvaluate(p, eval)
return func(yield func(*Context) bool) {
if err != nil {
errCtx := &Context{
P: p,
Error: err,
}
yield(errCtx)
return
}
for _, itm := range seq {
newContext := Context{
P: &goxpath.Parser{
Ctx: goxpath.CopyContext(ctx.P.Ctx),
},
Seq: goxpath.Sequence{itm},
}
newContext.P.Ctx.SetContextSequence(newContext.Seq)
if !yield(&newContext) {
return
}
}
}
}
// Eval executes the given XPath expression relative to the current context. It
// returns a new Context, so the old one is still available for further use.
func (ctx *Context) Eval(eval string) *Context {
newContext := Context{
P: &goxpath.Parser{
Ctx: goxpath.CopyContext(ctx.P.Ctx),
},
}
newContext.Seq, newContext.Error = safeEvaluate(newContext.P, eval)
return &newContext
}
// safeEvaluate wraps Parser.Evaluate and recovers from panics that goxpath may
// raise for invalid XPath expressions.
func safeEvaluate(p *goxpath.Parser, expr string) (seq goxpath.Sequence, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("xpath evaluation panic: %v", r)
}
}()
return p.Evaluate(expr)
}