@@ -133,6 +133,177 @@ impl<T> Match<T> {
133
133
}
134
134
}
135
135
136
+ /// A Router for defining matching rules (``routes``) for paths to a destination (``handler``).
137
+ /// One or more routes are added to the router and then paths can be recognized and a match
138
+ /// returned. Routes can contain parameters that are then returned as part of the match.
139
+ ///
140
+ /// # Example
141
+ ///
142
+ /// ```
143
+ /// use route_recognizer::Router;
144
+ ///
145
+ /// #[derive(PartialEq)]
146
+ /// enum FooBarBaz {
147
+ /// FOO,
148
+ /// BAR,
149
+ /// BAZ,
150
+ /// };
151
+ ///
152
+ /// let mut router = Router::new();
153
+ /// router.add("/foo", FooBarBaz::FOO);
154
+ /// router.add("/foo/:bar", FooBarBaz::BAR);
155
+ /// router.add("/foo/:bar/*baz", FooBarBaz::BAZ);
156
+ ///
157
+ /// let m = router.recognize("/foo").unwrap();
158
+ /// if *m.handler == FooBarBaz::FOO {
159
+ /// println!("do some foo");
160
+ /// }
161
+ ///
162
+ /// let m = router.recognize("/foo/123").unwrap();
163
+ /// if *m.handler == FooBarBaz::BAR {
164
+ /// println!("Got a bar of {}", m.params["bar"]);
165
+ /// }
166
+ ///
167
+ /// let m = router.recognize("/foo/123/abc/def").unwrap();
168
+ /// if *m.handler == FooBarBaz::BAZ {
169
+ /// println!("Got a bar of {} and a baz of {}", m.params["bar"], m.params["baz"]);
170
+ /// }
171
+ /// ```
172
+ ///
173
+ ///
174
+ /// # Route types
175
+ ///
176
+ /// A ``route`` consists of one or more segments, separated by a ``/``, to be matched against the ``path`` to be
177
+ /// recognized. There are three types of segments - *static*, *dynamic*, *star*:
178
+ ///
179
+ /// 1. *static* - a specific string to match
180
+ ///
181
+ /// ```
182
+ /// use route_recognizer::Router;
183
+ /// let mut router = Router::new();
184
+ /// router.add("/foo", "foo".to_string());
185
+ /// router.add("/foo/bar", "foobar".to_string());
186
+ ///
187
+ /// let m = router.recognize("/foo").unwrap();
188
+ /// assert_eq!(*m.handler, "foo"); // foo is matched
189
+ ///
190
+ /// let m = router.recognize("/foo/bar").unwrap();
191
+ /// assert_eq!(*m.handler, "foobar"); // foobar is matched
192
+ ///
193
+ /// let m = router.recognize("/foo/bar/baz");
194
+ /// assert!(m.is_err()); // No match is found
195
+ /// ```
196
+ ///
197
+ /// 2. *dynamic* - a single segment is matched. Dynamic segments start with a ``:`` and can
198
+ /// be named to be retrieved as a parameter.
199
+ ///
200
+ /// ```
201
+ /// use route_recognizer::Router;
202
+ /// let mut router = Router::new();
203
+ /// router.add("/foo/:bar", "foobar".to_string());
204
+ /// router.add("/foo/:bar/baz", "foobarbaz".to_string());
205
+ ///
206
+ /// let m = router.recognize("/foo");
207
+ /// assert!(m.is_err()); // No match is found
208
+ ///
209
+ /// let m = router.recognize("/foo/bar").unwrap();
210
+ /// assert_eq!(*m.handler, "foobar"); // foobar is matched
211
+ /// assert_eq!(m.params["bar"], "bar"); // parameter 'bar' is set to 'bar'
212
+ ///
213
+ /// let m = router.recognize("/foo/123").unwrap();
214
+ /// assert_eq!(*m.handler, "foobar"); // foobar is matched
215
+ /// assert_eq!(m.params["bar"], "123"); // parameter 'bar' is set to '123'
216
+ ///```
217
+ ///
218
+ /// 3. *star* - matches one or more segments until the end of the path or another
219
+ /// defined segment is reached.
220
+ ///
221
+ /// ```
222
+ /// use route_recognizer::Router;
223
+ /// let mut router = Router::new();
224
+ /// router.add("/foo/*bar", "foobar".to_string());
225
+ /// router.add("/foo/*bar/baz", "foobarbaz".to_string());
226
+ ///
227
+ /// let m = router.recognize("/foo");
228
+ /// assert!(m.is_err()); // No match is found
229
+ ///
230
+ /// let m = router.recognize("/foo/123").unwrap();
231
+ /// assert_eq!(*m.handler, "foobar"); // foobar is matched
232
+ /// assert_eq!(m.params["bar"], "123"); // parameter 'bar' is set to '123'
233
+ ///
234
+ /// let m = router.recognize("/foo/123/abc/def").unwrap();
235
+ /// assert_eq!(*m.handler, "foobar"); // foobar is matched
236
+ /// assert_eq!(m.params["bar"], "123/abc/def"); // parameter 'bar' is set to '123/abc/def'
237
+ ///
238
+ /// let m = router.recognize("/foo/123/abc/baz").unwrap();
239
+ /// assert_eq!(*m.handler, "foobarbaz"); // foobar is matched
240
+ /// assert_eq!(m.params["bar"], "123/abc"); // parameter 'bar' is set to '123/abc'
241
+ ///```
242
+ ///
243
+ /// # Unnamed parameters
244
+ ///
245
+ /// Parameters do not need to have a name, but can be indicated just by the leading ``:``
246
+ /// or ``*``. If a name is not defined then the parameter is not captured in the ``params``
247
+ /// field of the match.
248
+ ///
249
+ /// For example:
250
+ ///
251
+ /// ```
252
+ /// use route_recognizer::Router;
253
+ /// let mut router = Router::new();
254
+ /// router.add("/foo/*", "foo".to_string());
255
+ /// router.add("/bar/:/baz", "barbaz".to_string());
256
+ ///
257
+ /// let m = router.recognize("/foo/123").unwrap();
258
+ /// assert_eq!(*m.handler, "foo"); // foo is matched
259
+ /// assert_eq!(m.params.iter().next(), None); // but no parameters are found
260
+ ///
261
+ /// let m = router.recognize("/bar/123/baz").unwrap();
262
+ /// assert_eq!(*m.handler, "barbaz"); // barbaz is matched
263
+ /// assert_eq!(m.params.iter().next(), None); // but no parameters are found
264
+ /// ```
265
+ ///
266
+ /// # Routing precedence
267
+ ///
268
+ /// Routes can be a combination of all three types and the most specific match will be
269
+ /// the result of the precedence of the types where *static* takes precedence over
270
+ /// *dynamic*, which in turn takes precedence over *star* segments. For example, if you
271
+ /// have the following three routes:
272
+ ///
273
+ /// ```
274
+ /// use route_recognizer::Router;
275
+ /// let mut router = Router::new();
276
+ /// router.add("/foo", "foo".to_string());
277
+ /// router.add("/:bar", "bar".to_string());
278
+ /// router.add("/*baz", "baz".to_string());
279
+ ///
280
+ /// let m = router.recognize("/foo").unwrap();
281
+ /// assert_eq!(*m.handler, "foo"); // foo is matched as it is a static match
282
+ ///
283
+ /// let m = router.recognize("/123").unwrap();
284
+ /// assert_eq!(*m.handler, "bar"); // bar is matched as it is a single segment match,
285
+ /// // whereas baz is a star match
286
+ /// ```
287
+ ///
288
+ /// The precedence rules also apply within a route itself. So if you have a mix of types
289
+ /// the static and dynamic parts will take precedence over star rules. For example:
290
+ ///
291
+ /// ```
292
+ /// use route_recognizer::Router;
293
+ /// let mut router = Router::new();
294
+ /// router.add("/foo/*bar/baz/:bay", "foobarbazbay".to_string());
295
+ ///
296
+ /// let m = router.recognize("/foo/123/abc/def/baz/xyz").unwrap();
297
+ /// assert_eq!(m.params["bar"], "123/abc/def");
298
+ /// assert_eq!(m.params["bay"], "xyz");
299
+ ///
300
+ /// // note that the match will take the right most match when
301
+ /// // a star segment is define, so in a path that contains multiple
302
+ /// // baz segments it will match on the last one
303
+ /// let m = router.recognize("/foo/123/baz/abc/def/baz/xyz").unwrap();
304
+ /// assert_eq!(m.params["bar"], "123/baz/abc/def");
305
+ /// assert_eq!(m.params["bay"], "xyz");
306
+ /// ```
136
307
#[ derive( Clone ) ]
137
308
pub struct Router < T > {
138
309
nfa : NFA < Metadata > ,
@@ -147,7 +318,49 @@ impl<T> Router<T> {
147
318
}
148
319
}
149
320
150
- pub fn add ( & mut self , mut route : & str , dest : T ) -> Result < ( ) , String > {
321
+ /// add a route to the router.
322
+ ///
323
+ /// # Examples
324
+ /// Basic usage:
325
+ /// ```
326
+ /// use route_recognizer::Router;
327
+ /// let mut router = Router::new();
328
+ /// router.add("/foo/*bar/baz/:bay", "foo".to_string());
329
+ /// ```
330
+ ///
331
+ /// # Panics
332
+ ///
333
+ /// If a duplicate name is detected in the route the function will panic to ensure that data
334
+ /// is not lost when a route is recognized. If the earlier parameter is not required an unamed
335
+ /// parameter (e.g. ``/a/:/:b`` or ``/a/*/:b``) can be used.
336
+ ///
337
+ /// If user defined data is being added as a route, consider using [`Router::add_check`] instead.
338
+ ///
339
+ /// [`Router::add_check`]: struct.Router.html#method.add_check
340
+ ///
341
+ pub fn add ( & mut self , route : & str , dest : T ) {
342
+ self . add_check ( route, dest) . unwrap ( ) ;
343
+ }
344
+
345
+ /// add a route to the router returning a result indicating success or failure.
346
+ ///
347
+ /// # Examples
348
+ /// Basic usage:
349
+ /// ```
350
+ /// use route_recognizer::Router;
351
+ /// let mut router = Router::new();
352
+ /// router.add_check("/foo/*bar/baz/:bay", "foo".to_string()).expect("Failed to add route.");
353
+ /// ```
354
+ ///
355
+ /// If duplicate parameter names are defined in the route then an ``Error`` is returned:
356
+ /// ```
357
+ /// let mut router = route_recognizer::Router::new();
358
+ /// let result = router.add_check("/foo/:bar/abc/:bar", "foobarabcbar".to_string());
359
+ /// assert!(result.is_err());
360
+ /// assert_eq!("Duplicate name 'bar' in route foo/:bar/abc/:bar", result.err().unwrap());
361
+ /// ```
362
+ ///
363
+ pub fn add_check ( & mut self , mut route : & str , dest : T ) -> Result < ( ) , String > {
151
364
if !route. is_empty ( ) && route. as_bytes ( ) [ 0 ] == b'/' {
152
365
route = & route[ 1 ..] ;
153
366
}
0 commit comments