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 } /* (5) Optimise structure */ opti, err := sch.optimise() if err != nil { return nil, err } return &opti, nil } // 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) }