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 }