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());
    }
}