Skip to content

Commit 388db7d

Browse files
committed
Initial commit
0 parents  commit 388db7d

File tree

8 files changed

+3928
-0
lines changed

8 files changed

+3928
-0
lines changed

.gitignore

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Compiled Object files, Static and Dynamic libs (Shared Objects)
2+
*.o
3+
*.a
4+
*.so
5+
6+
# Folders
7+
_obj
8+
_test
9+
10+
# IDE Folders
11+
.idea
12+
13+
*.exe
14+
*.test
15+
*.prof
16+
17+
*.swp
18+

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2019 Guoyao Wu (guoyao.me@gmail.com)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

README.md

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
# Path-to-RegExp
2+
3+
> Turn a path string such as `/user/:name` into a regular expression.
4+
5+
Thanks to [path-to-regexp](https://github.com/pillarjs/path-to-regexp).
6+
7+
## Usage
8+
9+
```go
10+
import pathToRegexp "github/soongo/path-to-regexp"
11+
12+
// pathToRegexp.PathToRegexp(path, keys, options) // keys and options can be nil
13+
// pathToRegexp.Parse(path, options) // options can be nil
14+
// pathToRegexp.Compile(path, options) // options can be nil
15+
```
16+
17+
- **path** A string, array or slice of strings, or a regular expression with type *github.com/dlclark/regexp2.Regexp.
18+
- **keys** An array to populate with keys found in the path.
19+
- key
20+
- **name** The name of the token (`string` for named or `number` for index)
21+
- **prefix** The prefix character for the segment (e.g. `/`)
22+
- **delimiter** The delimiter for the segment (same as prefix or default delimiter)
23+
- **optional** Indicates the token is optional (`boolean`)
24+
- **repeat** Indicates the token is repeated (`boolean`)
25+
- **pattern** The RegExp used to match this token (`string`)
26+
- **options**
27+
- **sensitive** When `true` the regexp will be case sensitive. (default: `false`)
28+
- **strict** When `true` the regexp allows an optional trailing delimiter to match. (default: `false`)
29+
- **end** When `true` the regexp will match to the end of the string. (default: `true`)
30+
- **start** When `true` the regexp will match from the beginning of the string. (default: `true`)
31+
- **delimiter** The default delimiter for segments. (default: `'/'`)
32+
- **endsWith** Optional character, or list of characters, to treat as "end" characters.
33+
- **whitelist** List of characters to consider delimiters when parsing. (default: `nil`, any character)
34+
35+
```go
36+
var keys []pathToRegexp.Key
37+
regexp, err := pathToRegexp.PathToRegexp("/foo/:bar", &keys, nil)
38+
// regexp: ^\/foo\/([^\/]+?)(?:\/)?$
39+
// keys: [{name:"bar", prefix:"/", delimiter:"/", optional:false, repeat:false, pattern:"[^\\/]+?"}}]
40+
```
41+
42+
**Please note:** The `Regexp` returned by `path-to-regexp` is intended for ordered data (e.g. pathnames, hostnames). It can not handle arbitrarily ordered data (e.g. query strings, URL fragments, JSON, etc).
43+
44+
### Parameters
45+
46+
The path argument is used to define parameters and populate the list of keys.
47+
48+
#### Named Parameters
49+
50+
Named parameters are defined by prefixing a colon to the parameter name (`:foo`). By default, the parameter will match until the next prefix (e.g. `[^/]+`).
51+
52+
```go
53+
regexp, err := pathToRegexp.PathToRegexp("/:foo/:bar", nil, nil)
54+
// keys: [
55+
// {name:"foo", prefix:"/", delimiter:"/", optional:false, repeat:false, pattern:"[^\\/]+?"},
56+
// {name:"bar", prefix:"/", delimiter:"/", optional:false, repeat:false, pattern:"[^\\/]+?"}
57+
// ]
58+
59+
match, err := regexp.FindStringMatch("/test/route")
60+
for _, g := range match.Groups() {
61+
fmt.Printf("%q ", g.String())
62+
}
63+
fmt.Printf("%d, %q\n", match.Index, match)
64+
//=> "/test/route" "test" "route" 0, "/test/route"
65+
```
66+
67+
**Please note:** Parameter names must use "word characters" (`[A-Za-z0-9_]`).
68+
69+
#### Parameter Modifiers
70+
71+
##### Optional
72+
73+
Parameters can be suffixed with a question mark (`?`) to make the parameter optional.
74+
75+
```go
76+
regexp, err := pathToRegexp.PathToRegexp("/:foo/:bar?", nil, nil)
77+
// keys: [
78+
// {name:"foo", prefix:"/", delimiter:"/", optional:false, repeat:false, pattern:"[^\\/]+?"},
79+
// {name:"bar", prefix:"/", delimiter:"/", optional:true, repeat:false, pattern:"[^\\/]+?"}
80+
// ]
81+
82+
match, err := regexp.FindStringMatch("/test")
83+
for _, g := range match.Groups() {
84+
fmt.Printf("%q ", g.String())
85+
}
86+
fmt.Printf("%d, %q\n", match.Index, match)
87+
//=> "/test" "test" "" 0, "/test"
88+
89+
match, err = regexp.FindStringMatch("/test/route")
90+
for _, g := range match.Groups() {
91+
fmt.Printf("%q ", g.String())
92+
}
93+
fmt.Printf("%d, %q\n", match.Index, match)
94+
//=> "/test/route" "test" "route" 0, "/test/route"
95+
```
96+
97+
**Tip:** The prefix is also optional, escape the prefix `\/` to make it required.
98+
99+
##### Zero or more
100+
101+
Parameters can be suffixed with an asterisk (`*`) to denote a zero or more parameter matches. The prefix is used for each match.
102+
103+
```go
104+
regexp, err := pathToRegexp.PathToRegexp("/:foo*", nil, nil)
105+
// keys: [{name:"foo", prefix:"/", delimiter:"/", optional:true, repeat:true, pattern:"[^\\/]+?"}]
106+
107+
match, err := regexp.FindStringMatch("/")
108+
for _, g := range match.Groups() {
109+
fmt.Printf("%q ", g.String())
110+
}
111+
fmt.Printf("%d, %q\n", match.Index, match)
112+
//=> "/" "" 0, "/"
113+
114+
match, err = regexp.FindStringMatch("/bar/baz")
115+
for _, g := range match.Groups() {
116+
fmt.Printf("%q ", g.String())
117+
}
118+
fmt.Printf("%d, %q\n", match.Index, match)
119+
//=> "/bar/baz" "bar/baz" 0, "/bar/baz"
120+
```
121+
122+
##### One or more
123+
124+
Parameters can be suffixed with a plus sign (`+`) to denote a one or more parameter matches. The prefix is used for each match.
125+
126+
```go
127+
regexp, err := pathToRegexp.PathToRegexp("/:foo+", nil, nil)
128+
// keys: [{name:"foo", prefix:"/", delimiter:"/", optional:false, repeat:true, pattern:"[^\\/]+?"}]
129+
130+
match, err := regexp.FindStringMatch("/")
131+
fmt.Println(match)
132+
//=> nil
133+
134+
match, err = regexp.FindStringMatch("/bar/baz")
135+
for _, g := range match.Groups() {
136+
fmt.Printf("%q ", g.String())
137+
}
138+
fmt.Printf("%d, %q\n", match.Index, match)
139+
//=> "/bar/baz" "bar/baz" 0, "/bar/baz"
140+
```
141+
142+
#### Unnamed Parameters
143+
144+
It is possible to write an unnamed parameter that only consists of a matching group. It works the same as a named parameter, except it will be numerically indexed.
145+
146+
```go
147+
regexp, err := pathToRegexp.PathToRegexp("/:foo/(.*)", nil, nil)
148+
// keys: [
149+
// {name:"foo", prefix:"/", delimiter:"/", optional:false, repeat:false, pattern:"[^\\/]+?"},
150+
// {name:0, prefix:"/", delimiter:"/", optional:false, repeat:false, pattern:".*"}
151+
// ]
152+
153+
match, err := regexp.FindStringMatch("/test/route")
154+
for _, g := range match.Groups() {
155+
fmt.Printf("%q ", g.String())
156+
}
157+
fmt.Printf("%d, %q\n", match.Index, match)
158+
//=> "/test/route" "test" "route" 0, "/test/route"
159+
```
160+
161+
#### Custom Matching Parameters
162+
163+
All parameters can have a custom regexp, which overrides the default match (`[^/]+`). For example, you can match digits or names in a path:
164+
165+
```go
166+
regexpNumbers, err := pathToRegexp.PathToRegexp("/icon-:foo(\\d+).png", nil, nil)
167+
// keys: {name:"foo", prefix:"-", delimiter:"-", optional:false, repeat:false, pattern:"\\d+"}
168+
169+
match, err := regexpNumbers.FindStringMatch("/icon-123.png")
170+
for _, g := range match.Groups() {
171+
fmt.Printf("%q ", g.String())
172+
}
173+
//=> "/icon-123.png" "123"
174+
175+
match, err = regexpNumbers.FindStringMatch("/icon-abc.png")
176+
fmt.Println(match)
177+
//=> nil
178+
179+
regexpWord, err := pathToRegexp.PathToRegexp("/(user|u)", nil, nil)
180+
// keys: {name:0, prefix:"/", delimiter:"/", optional:false, repeat:false, pattern:"user|u"}
181+
182+
match, err = regexpWord.FindStringMatch("/u")
183+
for _, g := range match.Groups() {
184+
fmt.Printf("%q ", g.String())
185+
}
186+
//=> "/u" "u"
187+
188+
match, err = regexpWord.FindStringMatch("/users")
189+
fmt.Println(match)
190+
//=> nil
191+
```
192+
193+
**Tip:** Backslashes need to be escaped with another backslash in Go strings.
194+
195+
### Parse
196+
197+
The parse function is exposed via `pathToRegexp.Parse`. This will return a slice of strings and keys.
198+
199+
```go
200+
tokens := pathToRegexp.Parse("/route/:foo/(.*)", nil)
201+
202+
fmt.Printf("%#v\n", tokens[0])
203+
//=> "/route"
204+
205+
fmt.Printf("%#v\n", tokens[1])
206+
//=> pathToRegexp.Key{name:"foo", prefix:"/", delimiter:"/", optional:false, repeat:false, pattern:"[^\\/]+?"}
207+
208+
fmt.Printf("%#v\n", tokens[2])
209+
//=> pathToRegexp.Key{name:0, prefix:"/", delimiter:"/", optional:false, repeat:false, pattern:".*"}
210+
```
211+
212+
**Note:** This method only works with strings.
213+
214+
### Compile ("Reverse" Path-To-RegExp)
215+
216+
Path-To-RegExp exposes a compile function for transforming a string into a valid path.
217+
218+
```go
219+
toPath, err := pathToRegexp.Compile("/user/:id", nil)
220+
221+
toPath(map[string]int{"id": 123}, nil) //=> "/user/123"
222+
toPath(map[string]string{"id": "café"}, nil) //=> "/user/caf%C3%A9"
223+
toPath(map[string]string{"id": "/"}, nil) //=> "/user/%2F"
224+
225+
toPath(map[string]string{"id": ":/"}, nil) //=> "/user/%3A%2F"
226+
toPath(map[string]string{"id": ":&"}, &Options{encode: func(value string, token interface{}) string {
227+
return value
228+
}}) //=> panic
229+
toPath(map[string]string{"id": ":&"}, nil) //=> "/user/%3A%26"
230+
toPath(map[string]string{"id": ":&"}, &Options{encode: func(value string, token interface{}) string {
231+
return value
232+
}}) //=> /user/:&
233+
234+
toPathRepeated, err := pathToRegexp.Compile("/:segment+", nil)
235+
236+
toPathRepeated(map[string]string{"segment": "foo"}, nil) //=> "/foo"
237+
toPathRepeated(map[string][]string{"segment": {"a", "b", "c"}}, nil) //=> "/a/b/c"
238+
239+
toPathRegexp, err := pathToRegexp.Compile("/user/:id(\\d+)", nil)
240+
241+
toPathRegexp(map[string]int{"id": 123}, nil) //=> "/user/123"
242+
toPathRegexp(map[string]string{"id": "123"}, nil) //=> "/user/123"
243+
toPathRegexp(map[string]string{"id": "abc"}, nil) //=> panic
244+
t1 := true
245+
toPathRegexp(map[string]string{"id": "abc"}, &Options{validate: &t1}) //=> panic
246+
```
247+
248+
**Note:** The generated function will panic on invalid input. It will do all necessary checks to ensure the generated path is valid. This method only works with strings.

go.mod

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
module github/soongo/path-to-regexp
2+
3+
go 1.13
4+
5+
require github.com/dlclark/regexp2 v1.2.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
2+
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=

0 commit comments

Comments
 (0)