2017-08-20 14:21:42 -04:00
|
|
|
package goxpath
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/xml"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/ChrisTrenkamp/goxpath/internal/execxp"
|
2020-06-15 16:18:51 -04:00
|
|
|
"github.com/ChrisTrenkamp/goxpath/parser"
|
2017-08-20 14:21:42 -04:00
|
|
|
"github.com/ChrisTrenkamp/goxpath/tree"
|
|
|
|
)
|
|
|
|
|
|
|
|
//Opts defines namespace mappings and custom functions for XPath expressions.
|
|
|
|
type Opts struct {
|
|
|
|
NS map[string]string
|
|
|
|
Funcs map[xml.Name]tree.Wrap
|
|
|
|
Vars map[string]tree.Result
|
|
|
|
}
|
|
|
|
|
|
|
|
//FuncOpts is a function wrapper for Opts.
|
|
|
|
type FuncOpts func(*Opts)
|
|
|
|
|
|
|
|
//XPathExec is the XPath executor, compiled from an XPath string
|
|
|
|
type XPathExec struct {
|
|
|
|
n *parser.Node
|
|
|
|
}
|
|
|
|
|
|
|
|
//Parse parses the XPath expression, xp, returning an XPath executor.
|
|
|
|
func Parse(xp string) (XPathExec, error) {
|
|
|
|
n, err := parser.Parse(xp)
|
|
|
|
return XPathExec{n: n}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
//MustParse is like Parse, but panics instead of returning an error.
|
|
|
|
func MustParse(xp string) XPathExec {
|
|
|
|
ret, err := Parse(xp)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return ret
|
|
|
|
}
|
|
|
|
|
|
|
|
//Exec executes the XPath expression, xp, against the tree, t, with the
|
|
|
|
//namespace mappings, ns, and returns the result as a stringer.
|
|
|
|
func (xp XPathExec) Exec(t tree.Node, opts ...FuncOpts) (tree.Result, error) {
|
|
|
|
o := &Opts{
|
|
|
|
NS: make(map[string]string),
|
|
|
|
Funcs: make(map[xml.Name]tree.Wrap),
|
|
|
|
Vars: make(map[string]tree.Result),
|
|
|
|
}
|
|
|
|
for _, i := range opts {
|
|
|
|
i(o)
|
|
|
|
}
|
|
|
|
return execxp.Exec(xp.n, t, o.NS, o.Funcs, o.Vars)
|
|
|
|
}
|
|
|
|
|
|
|
|
//ExecBool is like Exec, except it will attempt to convert the result to its boolean value.
|
|
|
|
func (xp XPathExec) ExecBool(t tree.Node, opts ...FuncOpts) (bool, error) {
|
|
|
|
res, err := xp.Exec(t, opts...)
|
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
|
|
|
b, ok := res.(tree.IsBool)
|
|
|
|
if !ok {
|
|
|
|
return false, fmt.Errorf("Cannot convert result to a boolean")
|
|
|
|
}
|
|
|
|
|
|
|
|
return bool(b.Bool()), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//ExecNum is like Exec, except it will attempt to convert the result to its number value.
|
|
|
|
func (xp XPathExec) ExecNum(t tree.Node, opts ...FuncOpts) (float64, error) {
|
|
|
|
res, err := xp.Exec(t, opts...)
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
n, ok := res.(tree.IsNum)
|
|
|
|
if !ok {
|
|
|
|
return 0, fmt.Errorf("Cannot convert result to a number")
|
|
|
|
}
|
|
|
|
|
|
|
|
return float64(n.Num()), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//ExecNode is like Exec, except it will attempt to return the result as a node-set.
|
|
|
|
func (xp XPathExec) ExecNode(t tree.Node, opts ...FuncOpts) (tree.NodeSet, error) {
|
|
|
|
res, err := xp.Exec(t, opts...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
n, ok := res.(tree.NodeSet)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("Cannot convert result to a node-set")
|
|
|
|
}
|
|
|
|
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//MustExec is like Exec, but panics instead of returning an error.
|
|
|
|
func (xp XPathExec) MustExec(t tree.Node, opts ...FuncOpts) tree.Result {
|
|
|
|
res, err := xp.Exec(t, opts...)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
|
|
|
//ParseExec parses the XPath string, xpstr, and runs Exec.
|
|
|
|
func ParseExec(xpstr string, t tree.Node, opts ...FuncOpts) (tree.Result, error) {
|
|
|
|
xp, err := Parse(xpstr)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return xp.Exec(t, opts...)
|
|
|
|
}
|