Skip to content

Commit

Permalink
Add support for double-quotes in filter path queries
Browse files Browse the repository at this point in the history
  • Loading branch information
beevik committed Aug 29, 2023
1 parent 8a4dcda commit 8da548e
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 31 deletions.
8 changes: 4 additions & 4 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,10 +299,10 @@ func indentLF(n int, source string) string {
}
}

// nextIndex returns the index of the next occurrence of sep in s,
// starting from offset. It returns -1 if the sep string is not found.
func nextIndex(s, sep string, offset int) int {
switch i := strings.Index(s[offset:], sep); i {
// nextIndex returns the index of the next occurrence of byte ch in s,
// starting from offset. It returns -1 if the byte is not found.
func nextIndex(s string, ch byte, offset int) int {
switch i := strings.IndexByte(s[offset:], ch); i {
case -1:
return -1
default:
Expand Down
63 changes: 36 additions & 27 deletions path.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,12 +242,17 @@ func splitPath(path string) []string {
var pieces []string
start := 0
inquote := false
var quote byte
for i := 0; i+1 <= len(path); i++ {
if path[i] == '\'' {
inquote = !inquote
} else if path[i] == '/' && !inquote {
pieces = append(pieces, path[start:i])
start = i + 1
if !inquote {
if path[i] == '\'' || path[i] == '"' {
inquote, quote = true, path[i]
} else if path[i] == '/' {
pieces = append(pieces, path[start:i])
start = i + 1
}
} else if path[i] == quote {
inquote = false
}
}
return append(pieces, path[start:])
Expand Down Expand Up @@ -302,30 +307,34 @@ func (c *compiler) parseFilter(path string) filter {
return nil
}

// Filter contains [@attr='val'], [fn()='val'], or [tag='val']?
eqindex := strings.Index(path, "='")
if eqindex >= 0 {
rindex := nextIndex(path, "'", eqindex+2)
if rindex != len(path)-1 {
c.err = ErrPath("path has mismatched filter quotes.")
return nil
}

key := path[:eqindex]
value := path[eqindex+2 : rindex]
// Filter contains [@attr='val'], [@attr="val"], [fn()='val'],
// [fn()="val"], [tag='val'] or [tag="val"]?
eqindex := strings.IndexByte(path, '=')
if eqindex >= 0 && eqindex+1 < len(path) {
quote := path[eqindex+1]
if quote == '\'' || quote == '"' {
rindex := nextIndex(path, quote, eqindex+2)
if rindex != len(path)-1 {
c.err = ErrPath("path has mismatched filter quotes.")
return nil
}

switch {
case key[0] == '@':
return newFilterAttrVal(key[1:], value)
case strings.HasSuffix(key, "()"):
name := key[:len(key)-2]
if fn, ok := fnTable[name]; ok {
return newFilterFuncVal(fn, value)
key := path[:eqindex]
value := path[eqindex+2 : rindex]

switch {
case key[0] == '@':
return newFilterAttrVal(key[1:], value)
case strings.HasSuffix(key, "()"):
name := key[:len(key)-2]
if fn, ok := fnTable[name]; ok {
return newFilterFuncVal(fn, value)
}
c.err = ErrPath("path has unknown function " + name)
return nil
default:
return newFilterChildText(key, value)
}
c.err = ErrPath("path has unknown function " + name)
return nil
default:
return newFilterChildText(key, value)
}
}

Expand Down
3 changes: 3 additions & 0 deletions path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ var tests = []test{
{"./bookstore/book[@category='WEB']/title", []string{"XQuery Kick Start", "Learning XML"}},
{"./bookstore/book[@path='/books/xml']/title", []string{"Learning XML"}},
{"./bookstore/book[@category='COOKING']/title[@lang='en']", "Everyday Italian"},
{`./bookstore/book[@category="COOKING"]/title[@lang="en"]`, "Everyday Italian"},
{"./bookstore/book/title[@lang='en'][@sku='150']", "Harry Potter"},
{"./bookstore/book/title[@lang='fr']", nil},
{"//p:price[@p:tax='1.99']", []string{"29.99"}},
Expand All @@ -144,6 +145,8 @@ var tests = []test{
{"./bookstore/book[]", errorResult("etree: path contains an empty filter expression.")},
{"./bookstore/book[@category='WEB'", errorResult("etree: path has invalid filter [brackets].")},
{"./bookstore/book[@category='WEB]", errorResult("etree: path has mismatched filter quotes.")},
{`./bookstore/book[@category='WEB"]`, errorResult("etree: path has mismatched filter quotes.")},
{`./bookstore/book[@category="WEB']`, errorResult("etree: path has mismatched filter quotes.")},
{"./bookstore/book[author]a", errorResult("etree: path has invalid filter [brackets].")},
{"/][", errorResult("etree: path has invalid filter [brackets].")},
}
Expand Down

0 comments on commit 8da548e

Please sign in to comment.