217 lines
4.0 KiB
Go
217 lines
4.0 KiB
Go
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// buildScheme builds a 'basic' scheme
|
|
// from a pattern string
|
|
func buildScheme(ss []string) (Scheme, error) {
|
|
|
|
/* (1) Build scheme */
|
|
sch := make(Scheme, 0, maxMatch)
|
|
|
|
for _, s := range ss {
|
|
|
|
/* (2) ignore empty */
|
|
if len(s) == 0 {
|
|
continue
|
|
}
|
|
|
|
m := new(matcher)
|
|
|
|
switch s {
|
|
|
|
/* (3) Card: 0, N */
|
|
case "**":
|
|
m.req = false
|
|
m.mul = true
|
|
sch = append(sch, m)
|
|
|
|
/* (4) Card: 1, N */
|
|
case "..":
|
|
m.req = true
|
|
m.mul = true
|
|
sch = append(sch, m)
|
|
|
|
/* (5) Card: 0, 1 */
|
|
case "*":
|
|
m.req = false
|
|
m.mul = false
|
|
sch = append(sch, m)
|
|
|
|
/* (6) Card: 1 */
|
|
case ".":
|
|
m.req = true
|
|
m.mul = false
|
|
sch = append(sch, m)
|
|
|
|
/* (7) Card: 1, literal string */
|
|
default:
|
|
m.req = true
|
|
m.mul = false
|
|
m.pat = fmt.Sprintf("/%s", s)
|
|
sch = append(sch, m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sch, nil
|
|
}
|
|
|
|
// optimise optimised the scheme for further parsing
|
|
func (s Scheme) optimise() (Scheme, error) {
|
|
|
|
/* (1) Nothing to do if only 1 element */
|
|
if len(s) <= 1 {
|
|
return s, nil
|
|
}
|
|
|
|
/* (2) Init reshifted scheme */
|
|
rshift := make(Scheme, 0, maxMatch)
|
|
rshift = append(rshift, s[0])
|
|
|
|
/* (2) Iterate over matchers */
|
|
for p, i, l := 0, 1, len(s); i < l; i++ {
|
|
|
|
pre, cur := s[p], s[i]
|
|
|
|
/* Merge: 2 following literals */
|
|
if len(pre.pat) > 0 && len(cur.pat) > 0 {
|
|
|
|
// merge strings into previous
|
|
pre.pat = fmt.Sprintf("%s%s", pre.pat, cur.pat)
|
|
|
|
// delete current
|
|
s[i] = nil
|
|
|
|
}
|
|
|
|
// increment previous (only if current is not nul)
|
|
if s[i] != nil {
|
|
rshift = append(rshift, s[i])
|
|
p = i
|
|
}
|
|
|
|
}
|
|
|
|
return rshift, nil
|
|
|
|
}
|
|
|
|
// matchString checks the STRING matchers from an URI
|
|
// it returns a boolean : false when not matching, true eitherway
|
|
// it returns a cleared uri, without STRING data
|
|
func (s Scheme) matchString(uri string) (string, bool) {
|
|
|
|
/* (1) Initialise variables */
|
|
clr := uri // contains cleared input string
|
|
minOff := 0 // minimum offset
|
|
|
|
/* (2) Iterate over strings */
|
|
for _, m := range s {
|
|
|
|
ls := len(m.pat)
|
|
|
|
// {1} If not STRING matcher -> ignore //
|
|
if ls == 0 {
|
|
continue
|
|
}
|
|
|
|
// {2} Get offset in URI (else -1) //
|
|
off := strings.Index(clr, m.pat)
|
|
if off < 0 {
|
|
return "", false
|
|
}
|
|
|
|
// {3} Fail on invalid offset range //
|
|
if off < minOff {
|
|
return "", false
|
|
}
|
|
|
|
// {4} Check for trailing '/' //
|
|
hasSlash := 0
|
|
if off+ls < len(clr) && clr[off+ls] == '/' {
|
|
hasSlash = 1
|
|
}
|
|
|
|
// {5} Remove the current string (+trailing slash) from the URI //
|
|
beg, end := clr[:off], clr[off+ls+hasSlash:]
|
|
clr = fmt.Sprintf("%s\a/%s", beg, end) // separate matches by '\a' character
|
|
|
|
// {6} Update offset range //
|
|
minOff = len(beg) + 2 - 1 // +2 slash separators
|
|
// -1 because strings begin with 1 slash already
|
|
|
|
}
|
|
|
|
/* (3) If exists, remove trailing '/' */
|
|
if clr[len(clr)-1] == '/' {
|
|
clr = clr[:len(clr)-1]
|
|
}
|
|
|
|
/* (4) If exists, remove trailing '\a' */
|
|
if clr[len(clr)-1] == '\a' {
|
|
clr = clr[:len(clr)-1]
|
|
}
|
|
|
|
return clr, true
|
|
|
|
}
|
|
|
|
// matchWildcards check the WILCARDS (non-string) matchers from
|
|
// a cleared URI. it returns if the string matches
|
|
// + it sets the matchers buffers for later extraction
|
|
func (s Scheme) matchWildcards(clear string) bool {
|
|
|
|
/* (1) Extract wildcards (ref) */
|
|
wildcards := make(Scheme, 0, maxMatch)
|
|
|
|
for _, m := range s {
|
|
if len(m.pat) == 0 {
|
|
m.buf = nil // flush buffers
|
|
wildcards = append(wildcards, m)
|
|
}
|
|
}
|
|
|
|
/* (2) If no wildcards -> match */
|
|
if len(wildcards) == 0 {
|
|
return true
|
|
}
|
|
|
|
/* (3) Break uri by '\a' characters */
|
|
matches := strings.Split(clear, "\a")[1:]
|
|
|
|
/* (4) Iterate over matches */
|
|
for n, match := range matches {
|
|
|
|
// {1} If no more matcher //
|
|
if n >= len(wildcards) {
|
|
return false
|
|
}
|
|
|
|
// {2} Split by '/' //
|
|
data := strings.Split(match, "/")[1:] // from index 1 because it begins with '/'
|
|
|
|
// {3} If required and missing //
|
|
if wildcards[n].req && len(data) < 1 {
|
|
return false
|
|
}
|
|
|
|
// {4} If not multi but got multi //
|
|
if !wildcards[n].mul && len(data) > 1 {
|
|
return false
|
|
}
|
|
|
|
// {5} Store data into matcher //
|
|
wildcards[n].buf = data
|
|
|
|
}
|
|
|
|
/* (5) Match */
|
|
return true
|
|
|
|
}
|