1 //! # `matchit` 2 //! 3 //! [](https://docs.rs/matchit) 4 //! [](https://crates.io/crates/matchit) 5 //! [](https://crates.io/crates/matchit) 6 //! 7 //! A blazing fast URL router. 8 //! 9 //! ```rust 10 //! use matchit::Router; 11 //! 12 //! # fn main() -> Result<(), Box<dyn std::error::Error>> { 13 //! let mut router = Router::new(); 14 //! router.insert("/home", "Welcome!")?; 15 //! router.insert("/users/:id", "A User")?; 16 //! 17 //! let matched = router.at("/users/978")?; 18 //! assert_eq!(matched.params.get("id"), Some("978")); 19 //! assert_eq!(*matched.value, "A User"); 20 //! # Ok(()) 21 //! # } 22 //! ``` 23 //! 24 //! ## Parameters 25 //! 26 //! Along with static routes, the router also supports dynamic route segments. These can either be named or catch-all parameters: 27 //! 28 //! ### Named Parameters 29 //! 30 //! Named parameters like `/:id` match anything until the next `/` or the end of the path: 31 //! 32 //! ```rust 33 //! # use matchit::Router; 34 //! # fn main() -> Result<(), Box<dyn std::error::Error>> { 35 //! let mut m = Router::new(); 36 //! m.insert("/users/:id", true)?; 37 //! 38 //! assert_eq!(m.at("/users/1")?.params.get("id"), Some("1")); 39 //! assert_eq!(m.at("/users/23")?.params.get("id"), Some("23")); 40 //! assert!(m.at("/users").is_err()); 41 //! 42 //! # Ok(()) 43 //! # } 44 //! ``` 45 //! 46 //! ### Catch-all Parameters 47 //! 48 //! Catch-all parameters start with `*` and match everything after the `/`. They must always be at the **end** of the route: 49 //! 50 //! ```rust 51 //! # use matchit::Router; 52 //! # fn main() -> Result<(), Box<dyn std::error::Error>> { 53 //! let mut m = Router::new(); 54 //! m.insert("/*p", true)?; 55 //! 56 //! assert_eq!(m.at("/foo.js")?.params.get("p"), Some("foo.js")); 57 //! assert_eq!(m.at("/c/bar.css")?.params.get("p"), Some("c/bar.css")); 58 //! 59 //! // note that this would not match 60 //! assert!(m.at("/").is_err()); 61 //! 62 //! # Ok(()) 63 //! # } 64 //! ``` 65 //! 66 //! ## Routing Priority 67 //! 68 //! Static and dynamic route segments are allowed to overlap. If they do, static segments will be given higher priority: 69 //! 70 //! ```rust 71 //! # use matchit::Router; 72 //! # fn main() -> Result<(), Box<dyn std::error::Error>> { 73 //! let mut m = Router::new(); 74 //! m.insert("/", "Welcome!").unwrap() ; // priority: 1 75 //! m.insert("/about", "About Me").unwrap(); // priority: 1 76 //! m.insert("/*filepath", "...").unwrap(); // priority: 2 77 //! 78 //! # Ok(()) 79 //! # } 80 //! ``` 81 //! 82 //! ## How does it work? 83 //! 84 //! The router takes advantage of the fact that URL routes generally follow a hierarchical structure. Routes are stored them in a radix trie that makes heavy use of common prefixes: 85 //! 86 //! ```text 87 //! Priority Path Value 88 //! 9 \ 1 89 //! 3 ├s None 90 //! 2 |├earch\ 2 91 //! 1 |└upport\ 3 92 //! 2 ├blog\ 4 93 //! 1 | └:post None 94 //! 1 | └\ 5 95 //! 2 ├about-us\ 6 96 //! 1 | └team\ 7 97 //! 1 └contact\ 8 98 //! ``` 99 //! 100 //! This allows us to reduce the route search to a small number of branches. Child nodes on the same level of the tree are also prioritized 101 //! by the number of children with registered values, increasing the chance of choosing the correct branch of the first try. 102 #![deny(rust_2018_idioms, clippy::all)] 103 104 mod error; 105 mod params; 106 mod router; 107 mod tree; 108 109 pub use error::{InsertError, MatchError}; 110 pub use params::{Params, ParamsIter}; 111 pub use router::{Match, Router}; 112 113 #[cfg(doctest)] 114 mod test_readme { 115 macro_rules! doc_comment { 116 ($x:expr) => { 117 #[doc = $x] 118 extern "C" {} 119 }; 120 } 121 122 doc_comment!(include_str!("../README.md")); 123 } 124