github.com/influx6/npkg@v0.8.8/npattrn/pattern.go (about) 1 // Package npattrn provides a simple regexp npattrn matching library majorly 2 // for constructing URL matchers. 3 // Patterns in this package follow the follow approach in declaring custom match 4 // segments. 5 // 6 // npattrn: /name/{id:[/\d+/]}/log/{date:[/\w+\W+/]} 7 // npattrn: /name/:id 8 // 9 // 10 package npattrn 11 12 import ( 13 "regexp" 14 "strings" 15 ) 16 17 // Params defines a map of stringed keys and values. 18 type Params map[string]string 19 20 // Matchable defines an interface for matchers. 21 type Matchable interface { 22 IsParam() bool 23 HasHash() bool 24 Segment() string 25 Validate(string) bool 26 } 27 28 // Matchers defines a list of machers for validating patterns with. 29 type Matchers []Matchable 30 31 // URIMatcher defines an interface for a URI matcher. 32 type URIMatcher interface { 33 Validate(string) (Params, string, bool) 34 Pattern() string 35 Priority() int 36 } 37 38 // matchProvider provides a class array-path matcher 39 type matchProvider struct { 40 pattern string 41 matchers Matchers 42 endless bool 43 priority int 44 } 45 46 // New returns a new instance of a URIMatcher. 47 func New(pattern string) URIMatcher { 48 pattern = addSlash(pattern) 49 50 pm := SegmentList(pattern) 51 52 m := matchProvider{ 53 priority: CheckPriority(pattern), 54 pattern: pattern, 55 matchers: pm, 56 endless: IsEndless(pattern), 57 } 58 59 return &m 60 } 61 62 // Priority returns the priority status of this giving npattrn. 63 func (m *matchProvider) Priority() int { 64 return m.priority 65 } 66 67 // Pattern returns the npattrn string for this matcher. 68 func (m *matchProvider) Pattern() string { 69 return m.pattern 70 } 71 72 // Validate returns true/false if the giving string matches the npattrn, returning 73 // a map of parameters match against segments of the npattrn. 74 func (m *matchProvider) Validate(path string) (Params, string, bool) { 75 path = addSlash(path) 76 var hashedSrc = stripAndCleanButHash(strings.Replace(path, "#", "/#", 1)) 77 src := splitPattern(hashedSrc) 78 79 srclen := len(src) 80 total := len(m.matchers) 81 82 if !m.endless && total != srclen { 83 return nil, "", false 84 } 85 86 if m.endless && total > srclen { 87 return nil, "", false 88 } 89 90 var state bool 91 92 param := make(Params) 93 94 var lastIndex int 95 var doneHash bool 96 97 for index, v := range m.matchers { 98 lastIndex = index 99 100 if v.HasHash() { 101 doneHash = true 102 } 103 104 if v.Validate(src[index]) { 105 106 if v.IsParam() { 107 param[v.Segment()] = src[index] 108 } 109 110 state = true 111 continue 112 } else { 113 state = false 114 break 115 } 116 } 117 118 if lastIndex+1 == srclen { 119 return param, "", state 120 } 121 122 remPath := strings.Join(src[lastIndex+1:], "/") 123 if doneHash || !strings.Contains(hashedSrc, "#") { 124 return param, addSlash(remPath), state 125 } 126 return param, addSlash(remPath), state 127 } 128 129 //============================================================================== 130 131 // SegmentList returns list of SegmentMatcher which implements the Matchable 132 // interface, with each made of each segment of the npattrn. 133 func SegmentList(pattern string) Matchers { 134 pattern = stripAndCleanButHash(pattern) 135 136 var set Matchers 137 138 if hashIndex := strings.Index(pattern, "#"); hashIndex != -1 { 139 if hashIndex == 0 { 140 pattern = strings.Join([]string{"/", pattern}, "") 141 } else { 142 last := pattern[hashIndex-1 : hashIndex] 143 if string(last[0]) != "/" { 144 splits := strings.Split(pattern, "#") 145 pattern = strings.Join([]string{splits[0], "/#", splits[1]}, "") 146 } 147 } 148 } 149 150 for _, val := range splitPattern(pattern) { 151 set = append(set, Segment(val)) 152 } 153 154 return set 155 } 156 157 //============================================================================== 158 159 // SegmentMatcher defines a single piece of npattrn to be matched against. 160 type SegmentMatcher struct { 161 *regexp.Regexp 162 original string 163 param bool 164 hashed bool 165 } 166 167 // Segment returns a Matchable for a specific part of a npattrn eg. :name, age, 168 // {id:[\\d+]}. 169 func Segment(segment string) Matchable { 170 if segment == "*" { 171 segment = "/*" 172 } 173 174 hashed := strings.HasPrefix(segment, "#") 175 if hashed { 176 segment = segment[1:] 177 } 178 179 id, rx, b := YankSpecial(segment) 180 mrk := regexp.MustCompile(rx) 181 182 sm := SegmentMatcher{ 183 Regexp: mrk, 184 original: id, 185 param: b, 186 hashed: hashed, 187 } 188 189 return &sm 190 } 191 192 // HasHash returns true/false if this segment hash the hash. 193 func (s *SegmentMatcher) HasHash() bool { 194 return s.hashed 195 } 196 197 // IsParam returns true/false if the segment is also a paramter. 198 func (s *SegmentMatcher) IsParam() bool { 199 return s.param 200 } 201 202 // Segment returns the original string that makes up this segment matcher. 203 func (s *SegmentMatcher) Segment() string { 204 return s.original 205 } 206 207 // Validate validates the value against the matcher. 208 func (s *SegmentMatcher) Validate(m string) bool { 209 return s.MatchString(m) 210 } 211 212 //==============================================================================