aboutsummaryrefslogtreecommitdiffstats
path: root/routing.h
blob: a150a4e7e8472db02829d9d165b2e587eac5ed78 (plain) (blame)
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
#pragma once

#include <cstdint>
#include <utility>
#include <string>
#include <tuple>

#include "utility.h"

namespace flask
{
    namespace black_magic
    {
        constexpr bool is_equ_n(const_str a, int ai, const_str b, int bi, int n)
        {
            return 
                ai + n > a.size() || bi + n > b.size() 
                    ? false :
                n == 0 
                    ? true : 
                a[ai] != b[bi] 
                    ? false : 
                is_equ_n(a,ai+1,b,bi+1,n-1);
        }

        constexpr bool is_int(const_str s, int i)
        {
            return is_equ_n(s, i, "<int>", 0, 5);
        }

        constexpr bool is_float(const_str s, int i)
        {
            return is_equ_n(s, i, "<float>", 0, 7) ||
                is_equ_n(s, i, "<double>", 0, 8);
        }

        constexpr bool is_str(const_str s, int i)
        {
            return is_equ_n(s, i, "<str>", 0, 5);
        }

        constexpr bool is_path(const_str s, int i)
        {
            return is_equ_n(s, i, "<path>", 0, 6);
        }

        template <typename ...Args>
        struct Caller
        {
            template <typename F>
            void operator()(F f, Args... args)
            {
                f(args...);
            }
        };


        template <int N, typename ... Args> struct S;
        template <int N, typename Arg, typename ... Args> struct S<N, Arg, Args...> {
            static_assert(N <= 4+1, "too many routing arguments (maximum 5)");
            template <typename T>
            using push = typename std::conditional<(N>4), S<N, Arg, Args...>, S<N+1, Arg, Args..., T>>::type;
            using pop = S<N-1, Args...>;
        };
        template <> struct S<0>
        {
            template <typename T>
            using push = S<1, T>;
        };

        template <typename F, typename Set>
        struct CallHelper;
        template <typename F, int N, typename ...Args>
        struct CallHelper<F, S<N, Args...>>
        {
            template <typename F1, typename ...Args1, typename = 
                decltype(std::declval<F1>()(std::declval<Args1>()...))
                >
            static char __test(int);

            template <typename ...>
            static int __test(...);

            static constexpr bool value = sizeof(__test<F, Args...>(0)) == sizeof(char);
        };

        static_assert(CallHelper<void(), S<0>>::value, "");
        static_assert(CallHelper<void(int), S<1, int>>::value, "");
        static_assert(!CallHelper<void(int), S<0>>::value, "");
        static_assert(!CallHelper<void(int), S<2, int, int>>::value, "");

        template <typename F, 
            typename Set = S<0>>
        constexpr bool validate_helper(const_str rule, unsigned i=0)
        {
            return 
                i == rule.size() 
                    ? CallHelper<F, Set>::value :
                is_int(rule, i)
                    ? validate_helper<F, typename Set::template push<int>>(rule, find_closing_tag(rule, i+1)+1) :
                is_float(rule, i)
                    ? validate_helper<F, typename Set::template push<double>>(rule, find_closing_tag(rule, i+1)+1) :
                is_str(rule, i)
                    ? validate_helper<F, typename Set::template push<std::string>>(rule, find_closing_tag(rule, i+1)+1) :
                is_path(rule, i)
                    ? validate_helper<F, typename Set::template push<std::string>>(rule, find_closing_tag(rule, i+1)+1) :
                validate_helper<F, Set>(rule, i+1)
            ;
        }

        static_assert(validate_helper<void()>("/"),"");
        static_assert(validate_helper<void(int)>("/<int>"),"");
        static_assert(!validate_helper<void()>("/<int>"),"");
    }

    class Router
    {
    public:
        constexpr Router(black_magic::const_str rule) : rule(rule)
        {
        }

        template <typename F>
        constexpr bool validate() const
        {
            return black_magic::validate_helper<F>(rule);
        }
    private:
        black_magic::const_str rule;
    };
}