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
//! Defines a hierarchial `Tree` with subtrees of `Node`.

use crate::helpers::http::PercentDecoded;
use crate::router::route::Route;
use crate::router::tree::node::Node;
use crate::router::tree::segment::{SegmentMapping, SegmentType};
use hyper::Body;
use log::trace;

pub mod node;
pub mod regex;
pub mod segment;

/// A hierarchical structure that provides a root `Node` and subtrees of linked nodes
/// that represent valid `Request` paths.
///
/// The `Tree` is created by the `gotham::router::builder` API and used internally by the `Router`
/// to determine the valid `Route` instances for a request path before dispatch.
pub struct Tree {
    root: Node,
}

impl Tree {
    /// Creates a new `Tree` and root `Node`.
    pub fn new() -> Self {
        trace!(" creating new tree");
        Tree {
            root: Node::new("/", SegmentType::Static),
        }
    }

    /// Adds a direct child to the root of the `Tree`.
    pub fn add_child(&mut self, child: Node) {
        self.root.add_child(child);
    }

    /// Adds a `Route` be evaluated by the `Router` when the root of the `Tree` is requested.
    pub fn add_route(&mut self, route: Box<dyn Route<ResBody = Body> + Send + Sync>) {
        self.root.add_route(route);
    }

    /// Borrow the root `NodeBuilder` as mutable.
    pub fn borrow_root_mut(&mut self) -> &mut Node {
        &mut self.root
    }

    /// 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, segment_type: SegmentType) -> bool {
        self.root.has_child(segment, segment_type)
    }

    /// Attempt to acquire a path from the `Tree` which matches the `Request` path and is routable.
    pub(crate) fn traverse<'a>(
        &'a self,
        req_path_segments: &'a [PercentDecoded],
    ) -> Option<(&Node, SegmentMapping<'a>, usize)> {
        trace!(" starting tree traversal");
        self.root.match_node(req_path_segments)
    }
}

#[cfg(test)]
mod tests {
    use hyper::{Method, Response, StatusCode};

    use crate::extractor::{NoopPathExtractor, NoopQueryStringExtractor};
    use crate::helpers::http::request::path::RequestPathSegments;
    use crate::helpers::http::response::create_empty_response;
    use crate::pipeline::{finalize_pipeline_set, new_pipeline_set};
    use crate::router::route::dispatch::DispatcherImpl;
    use crate::router::route::matcher::MethodOnlyRouteMatcher;
    use crate::router::route::{Delegation, Extractors, RouteImpl};
    use crate::state::State;

    use super::*;

    fn handler(state: State) -> (State, Response<Body>) {
        let res = create_empty_response(&state, StatusCode::OK);
        (state, res)
    }

    #[test]
    fn tree_traversal_tests() {
        let pipeline_set = finalize_pipeline_set(new_pipeline_set());
        let mut tree = Tree::new();

        let mut activate_node_builder = Node::new("activate", SegmentType::Static);

        let mut thing_node_builder = Node::new("thing", SegmentType::Dynamic);
        let thing_route = {
            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.add_child(activate_node_builder);

        let request_path_segments = RequestPathSegments::new("/%61ctiv%61te/workflow5");
        match tree.traverse(request_path_segments.segments().as_slice()) {
            Some((node, params, processed)) => {
                assert!(node.is_routable());
                assert_eq!(processed, 2);
                assert_eq!(
                    params.get("thing").unwrap().last().unwrap().as_ref(),
                    "workflow5"
                );
            }
            None => panic!(),
        }

        assert!(tree
            .traverse(&[PercentDecoded::new("/").unwrap()])
            .is_none());
        assert!(tree
            .traverse(&[PercentDecoded::new("/activate").unwrap()])
            .is_none());
    }
}