1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
//! Defines a hierarchial `Tree` with subtrees of `Node`. use std::collections::HashMap; use http::PercentDecoded; use router::route::Route; use router::tree::node::{Node, NodeBuilder, SegmentType}; pub mod node; /// A depth ordered `Vec` of `Node` instances that create a routable path through the `Tree` for the /// matched `Request` path. pub type Path<'a> = Vec<&'a Node>; /// Data which is returned from Tree traversal, mapping internal segment value to segment(s) /// which have been matched against the `Request` path. /// /// Data is Percent and UTF8 decoded. #[derive(Debug)] pub struct SegmentMapping<'a, 'b> { data: HashMap<&'a str, Vec<&'b PercentDecoded>>, } /// Number of segments from a `Request` path that are considered to have been processed /// by an `Router` traversing its `Tree`. type SegmentsProcessed = usize; impl<'a, 'b> SegmentMapping<'a, 'b> { /// Returns a reference for `Request` path segments mapped to the segment key. pub fn get(&self, key: &'a str) -> Option<&Vec<&'b PercentDecoded>> { self.data.get(key) } /// Determines if `Request` path segments are mapped to the segment key. pub fn contains_key(&self, key: &'a str) -> bool { self.data.contains_key(key) } /// Adds an empty value for a segment key, useful for segments that are considered /// optional and haven't been explicitly provided as part of a `Request` path pub fn add_unmapped_segment(&mut self, key: &'a str) { if !self.data.contains_key(key) { self.data.insert(key, Vec::new()); } } /// Number of segments from the Request path that have been mapped pub fn len(&self) -> usize { self.data.len() } } /// A hierarchical structure that provides a root `Node` and subtrees of linked nodes /// that represent valid `Request` paths. /// /// Allows the `Router` to supply a `Request` path and obtain `[0..n]` valid /// `Route` instances for that path for further evaluation. /// /// # Examples /// /// Desired tree: /// /// ```text /// / (Static Match) /// | -- activate (Static Match) /// | -- workflow (Dynamic Match, Routable) /// ``` /// /// Code: /// /// ```rust /// # extern crate gotham; /// # extern crate hyper; /// # /// # use hyper::{Request, Response, Method, StatusCode}; /// # /// # use gotham::http::response::create_response; /// # use gotham::router::route::{RouteImpl, Extractors, Delegation}; /// # use gotham::router::route::dispatch::{new_pipeline_set, finalize_pipeline_set, DispatcherImpl}; /// # use gotham::state::State; /// # use gotham::router::route::matcher::MethodOnlyRouteMatcher; /// # use gotham::router::tree::TreeBuilder; /// # use gotham::router::tree::node::NodeBuilder; /// # use gotham::router::tree::node::SegmentType; /// # use gotham::http::request::path::RequestPathSegments; /// # use gotham::router::request::path::NoopPathExtractor; /// # use gotham::router::request::query_string::NoopQueryStringExtractor; /// # use gotham::http::PercentDecoded; /// # /// # fn handler(state: State, _req: Request) -> (State, Response) { /// # let res = create_response(&state, StatusCode::Ok, None); /// # (state, res) /// # } /// # /// # fn main() { /// # let pipeline_set = finalize_pipeline_set(new_pipeline_set()); /// let mut tree_builder: TreeBuilder = TreeBuilder::new(); /// /// let mut activate_node_builder = NodeBuilder::new("activate", SegmentType::Static); /// /// let mut thing_node_builder = NodeBuilder::new(":thing", SegmentType::Dynamic); /// let thing_route = { /// // elided ... /// # let methods = vec![Method::Get]; /// # let matcher = MethodOnlyRouteMatcher::new(methods); /// # let dispatcher = Box::new(DispatcherImpl::new(|| Ok(handler), (), pipeline_set)); /// # let extractors: Extractors<NoopPathExtractor, NoopQueryStringExtractor> = Extractors::new(); /// # let route = RouteImpl::new(matcher, dispatcher, extractors, Delegation::Internal); /// # Box::new(route) /// }; /// thing_node_builder.add_route(thing_route); /// /// activate_node_builder.add_child(thing_node_builder); /// tree_builder.add_child(activate_node_builder); /// /// let tree = tree_builder.finalize(); /// /// match tree.traverse(RequestPathSegments::new("/%61ctiv%61te/workflow5").segments().as_slice()) { /// Some((path, leaf, segments_processed, segment_mapping)) => { /// assert!(path.last().unwrap().is_routable()); /// assert_eq!(path.last().unwrap().segment(), leaf.segment()); /// assert_eq!(segments_processed, 2); /// assert_eq!(segment_mapping.get(":thing").unwrap().last().unwrap().val(), "workflow5"); /// } /// None => panic!(), /// } /// /// // These paths are not routable but could be if 1 or more `Route` were added. /// assert!(tree.traverse(&[&PercentDecoded::new("/").unwrap()]).is_none()); /// assert!(tree.traverse(&[&PercentDecoded::new("/activate").unwrap()]).is_none()); /// # } /// ``` pub struct Tree { root: Node, } impl Tree { /// Borrow the root `Node` of the `Tree`. /// /// To be used in building a `Tree` structure only. pub fn borrow_root(&self) -> &Node { &self.root } /// Attempt to acquire a path from the `Tree` which matches the `Request` path and is routable. pub fn traverse<'r, 'n>( &'n self, req_path_segments: &'r [&PercentDecoded], ) -> Option<(Path<'n>, &Node, SegmentsProcessed, SegmentMapping<'n, 'r>)> { trace!(" starting tree traversal"); self.root.traverse(req_path_segments) } } /// Constructs a `Tree` which is sorted and immutable. pub struct TreeBuilder { root: NodeBuilder, } impl TreeBuilder { /// Creates a new `Tree` and root `Node`. pub fn new() -> Self { trace!(" creating new tree"); TreeBuilder { root: NodeBuilder::new("/", SegmentType::Static) } } /// Adds a direct child to the root of the `TreeBuilder`. pub fn add_child(&mut self, child: NodeBuilder) { self.root.add_child(child); } /// Determines if a child `Node` representing the exact segment provided /// exists at the root of the `Tree`. /// /// To be used in building a `Tree` structure only. pub fn has_child(&self, segment: &str) -> bool { self.root.has_child(segment) } /// Adds a `Route` be evaluated by the `Router` when the root of the /// `Tree` is requested. pub fn add_route(&mut self, route: Box<Route + Send + Sync>) { self.root.add_route(route); } /// Finalizes and sorts all internal data and creates a Tree for use with a `Router`. pub fn finalize(self) -> Tree { Tree { root: self.root.finalize() } } }