@@ -133,6 +133,177 @@ impl<T> Match<T> {
133133 }
134134}
135135
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+ /// ```
136307#[ derive( Clone ) ]
137308pub struct Router < T > {
138309 nfa : NFA < Metadata > ,
@@ -147,7 +318,49 @@ impl<T> Router<T> {
147318 }
148319 }
149320
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 > {
151364 if !route. is_empty ( ) && route. as_bytes ( ) [ 0 ] == b'/' {
152365 route = & route[ 1 ..] ;
153366 }
0 commit comments