2018-04-26 11:03:44 +00:00
|
|
|
package parser
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Build builds an URI scheme from a pattern string
|
|
|
|
func Build(s string) (*Scheme, error){
|
|
|
|
|
|
|
|
/* (1) Manage '/' at the start */
|
|
|
|
if len(s) < 1 || s[0] != '/' {
|
|
|
|
return nil, fmt.Errorf("URI must begin with '/'")
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (2) Split by '/' */
|
|
|
|
parts := strings.Split(s, "/")
|
|
|
|
|
|
|
|
/* (3) Max exceeded */
|
|
|
|
if len(parts)-2 > maxMatch {
|
|
|
|
for i, p := range parts {
|
|
|
|
fmt.Printf("%d: '%s'\n", i, p);
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("URI must not exceed %d slash-separated components, got %d", maxMatch, len(parts))
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (4) Build for each part */
|
|
|
|
sch, err := buildScheme(parts)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-04-26 13:01:18 +00:00
|
|
|
/* (5) Optimise structure */
|
2018-04-26 11:03:44 +00:00
|
|
|
opti, err := sch.optimise()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &opti, nil
|
|
|
|
|
2018-04-26 13:01:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Match returns if the given URI is matched by the scheme
|
|
|
|
func (s Scheme) Match(str string) bool {
|
|
|
|
|
|
|
|
/* (1) Nothing -> match all */
|
|
|
|
if len(s) == 0 { return true }
|
|
|
|
|
|
|
|
/* (2) Init variables */
|
|
|
|
wc := make(Scheme, 0, maxMatch) // conrains wildcards
|
|
|
|
clr := str // contains cleared input string
|
|
|
|
minOff := 0 // minimum offset
|
|
|
|
|
|
|
|
/* (3) Iterate over strings */
|
|
|
|
for _, m := range s {
|
|
|
|
|
|
|
|
ls := len(m.pat)
|
|
|
|
|
|
|
|
// if not string -> ignore
|
|
|
|
if ls == 0 {
|
|
|
|
wc = append(wc, m)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
// get offset if contained, else -1
|
|
|
|
off := strings.Index(clr, m.pat)
|
|
|
|
if off < 0 { return false }
|
|
|
|
|
|
|
|
// if invalid offset range -> fail
|
|
|
|
if off < minOff { return false }
|
|
|
|
|
|
|
|
// check if trailing '/'
|
|
|
|
slash := 0
|
|
|
|
if off+ls < len(clr) && clr[off+ls] == '/' {
|
|
|
|
slash = 1
|
|
|
|
}
|
|
|
|
|
|
|
|
// exclude this string (+trailing slash if set)
|
|
|
|
// fmt.Printf("remove: '%s'\n", clr[off:off+ls+slash])
|
|
|
|
beg, end := clr[:off], clr[off+ls+slash:]
|
|
|
|
clr = fmt.Sprintf("%s//%s", beg, end)
|
|
|
|
// fmt.Printf("clr: '%s'\n", clr)
|
|
|
|
|
|
|
|
// update offset range
|
|
|
|
minOff = len(beg) + 2 - 1 // +2 slash separators
|
|
|
|
// -1 because strings begin with 1 slash already
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (4) If no wildcard -> match */
|
|
|
|
if len(wc) == 0 {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// flush wildcard buffers
|
|
|
|
for _, w := range wc { w.buf = nil }
|
|
|
|
|
|
|
|
/* (5) Break the clear string apart */
|
|
|
|
matches := strings.Split(clr, "//")[1:]
|
|
|
|
|
|
|
|
// fail if missing matches
|
|
|
|
if len(matches) < len(wc) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
nwc := 0
|
|
|
|
for _, m := range matches {
|
|
|
|
|
|
|
|
// ignore empty
|
|
|
|
if len(m) == 0 { continue }
|
|
|
|
|
|
|
|
// no matcher for this match
|
|
|
|
if nwc >= len(wc){ return false }
|
|
|
|
|
|
|
|
// split by '/'
|
|
|
|
parts := strings.Split(m, "/")
|
|
|
|
|
|
|
|
// if required and missing -> does not match
|
|
|
|
if wc[nwc].req && len(parts) < 1 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// if not multi but multi -> does not match
|
|
|
|
if !wc[nwc].mul && len(parts) > 1 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// store into wilcard matcher
|
|
|
|
wc[nwc].buf = parts
|
|
|
|
|
|
|
|
// increment wildcard count
|
|
|
|
nwc++
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// GetMatch returns the indexed match (excluding string matchers)
|
|
|
|
func (s Scheme) GetMatch(n uint8) ([]string, error) {
|
|
|
|
|
|
|
|
/* (1) Index out of range */
|
|
|
|
if n > uint8(len(s)) {
|
|
|
|
return nil, fmt.Errorf("Index out of range")
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (2) Iterate to find index (exclude strings) */
|
|
|
|
ni := -1
|
|
|
|
for i, l := 0, len(s) ; i < l ; i++ {
|
|
|
|
|
|
|
|
// ignore strings
|
|
|
|
if len(s[i].pat) > 0 { continue }
|
|
|
|
|
|
|
|
// increment match counter : ni
|
|
|
|
ni++
|
|
|
|
|
|
|
|
// if expected index -> return matches
|
|
|
|
if uint8(ni) == n {
|
|
|
|
return s[i].buf, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* (3) If nothing found -> return empty set */
|
|
|
|
return nil, fmt.Errorf("Index out of range (max: %d)", ni)
|
|
|
|
|
2018-04-26 11:03:44 +00:00
|
|
|
}
|