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
use std::fmt::Debug;
use std::marker::PhantomData;
use std::sync::Arc;

use tide::Route;

/// [Variadic-argument][] route versioning is implemented via this struct for [`From<T>`][] with Single-argument, Tuple, and Vec types.
///
/// This allows [`preroll::main!`][crate::main!] and [`preroll::test_utils::create_client`][crate::test_utils::create_client] to version routes automatically based on their position in the arguments list.
///
/// Examples of what can be provided to any function that accepts `impl Into<VariadicRoutes>`, assuming all arguments are `Fn(Route<'r, Arc<State>>)`:
/// - A single routes function argument.
///     - Will become `/api/v1`.
///     - E.g. `routes_v1` or `routes_setup`.
/// - A [Tuple][] of 1 to 4 routes functions.
///     - Will become `/api/v1` through `/api/v4`.
///     - E.g. `(routes_v1, routes_v2, routes_v3)`
/// - A [Vec][] of boxed routes functions.
///     - If you need this you may want to reconsider your architecture!
///     - Will become `/api/v{N}` where N is the index + 1.
///     - E.g. `vec![Box::new(routes_v1), Box::new(routes_v2)]`
///
/// [`From<T>`]: https://doc.rust-lang.org/std/convert/trait.From.html
/// [Tuple]: https://doc.rust-lang.org/std/primitive.tuple.html
/// [Variadic-argument]: https://en.wikipedia.org/wiki/Variadic_function
/// [Vec]: https://doc.rust-lang.org/std/vec/struct.Vec.html
#[allow(clippy::type_complexity)]
#[allow(missing_debug_implementations)]
pub struct VariadicRoutes<State>
where
    State: Send + Sync + 'static,
{
    _phantom_state: PhantomData<*const State>,
    pub routes: Vec<Box<dyn for<'r> Fn(Route<'r, Arc<State>>)>>,
}

impl<State, RoutesFn> From<RoutesFn> for VariadicRoutes<State>
where
    State: Send + Sync + 'static,
    RoutesFn: for<'r> Fn(Route<'r, Arc<State>>) + 'static,
{
    fn from(routes: RoutesFn) -> Self {
        VariadicRoutes {
            _phantom_state: PhantomData,
            routes: vec![Box::new(routes)],
        }
    }
}

// For completeness only
impl<State, RoutesFn> From<(RoutesFn,)> for VariadicRoutes<State>
where
    State: Send + Sync + 'static,
    RoutesFn: for<'r> Fn(Route<'r, Arc<State>>) + Debug + 'static,
{
    fn from(routes: (RoutesFn,)) -> Self {
        VariadicRoutes {
            _phantom_state: PhantomData,
            routes: vec![Box::new(routes.0)],
        }
    }
}

impl<State, RoutesFn1, RoutesFn2> From<(RoutesFn1, RoutesFn2)> for VariadicRoutes<State>
where
    State: Send + Sync + 'static,
    RoutesFn1: for<'r> Fn(Route<'r, Arc<State>>) + 'static,
    RoutesFn2: for<'r> Fn(Route<'r, Arc<State>>) + 'static,
{
    fn from(routes: (RoutesFn1, RoutesFn2)) -> Self {
        VariadicRoutes {
            _phantom_state: PhantomData,
            routes: vec![Box::new(routes.0), Box::new(routes.1)],
        }
    }
}

impl<State, RoutesFn1, RoutesFn2, RoutesFn3> From<(RoutesFn1, RoutesFn2, RoutesFn3)>
    for VariadicRoutes<State>
where
    State: Send + Sync + 'static,
    RoutesFn1: for<'r> Fn(Route<'r, Arc<State>>) + 'static,
    RoutesFn2: for<'r> Fn(Route<'r, Arc<State>>) + 'static,
    RoutesFn3: for<'r> Fn(Route<'r, Arc<State>>) + 'static,
{
    fn from(routes: (RoutesFn1, RoutesFn2, RoutesFn3)) -> Self {
        VariadicRoutes {
            _phantom_state: PhantomData,
            routes: vec![Box::new(routes.0), Box::new(routes.1), Box::new(routes.2)],
        }
    }
}

impl<State, RoutesFn1, RoutesFn2, RoutesFn3, RoutesFn4>
    From<(RoutesFn1, RoutesFn2, RoutesFn3, RoutesFn4)> for VariadicRoutes<State>
where
    State: Send + Sync + 'static,
    RoutesFn1: for<'r> Fn(Route<'r, Arc<State>>) + 'static,
    RoutesFn2: for<'r> Fn(Route<'r, Arc<State>>) + 'static,
    RoutesFn3: for<'r> Fn(Route<'r, Arc<State>>) + 'static,
    RoutesFn4: for<'r> Fn(Route<'r, Arc<State>>) + 'static,
{
    fn from(routes: (RoutesFn1, RoutesFn2, RoutesFn3, RoutesFn4)) -> Self {
        VariadicRoutes {
            _phantom_state: PhantomData,
            routes: vec![
                Box::new(routes.0),
                Box::new(routes.1),
                Box::new(routes.2),
                Box::new(routes.3),
            ],
        }
    }
}

// If you have api versioning beyond ... I don't know... 5?? you probably should reconsider your architecture!!
#[allow(clippy::type_complexity)]
impl<State> From<Vec<Box<dyn for<'r> Fn(Route<'r, Arc<State>>)>>> for VariadicRoutes<State>
where
    State: Send + Sync + 'static,
{
    fn from(routes: Vec<Box<dyn for<'r> Fn(Route<'r, Arc<State>>)>>) -> Self {
        VariadicRoutes {
            _phantom_state: PhantomData,
            routes,
        }
    }
}