summaryrefslogtreecommitdiff
path: root/day7/router.py
blob: c662592c63b50206a569c326357151c24e6d18cd (plain)
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
import re
from utils import NOT_FOUND, add_text_headers, HTTP_METHODS
import db

from backend import *


class Route:
    def __init__(self, callback, url_format, methods=None):
        self.methods = ['GET'] if methods is None else methods
        self.url_pattern = re.compile(url_format)
        self.callback = callback

    def check_route(self, url, method):
        match = self.url_pattern.fullmatch(url)
        return bool(match) and len(match.groups()) == self.url_pattern.groups and method in self.methods

    def invoke_callback(self, url, method, query):
        match = self.url_pattern.fullmatch(url)
        groups = match.groups()

        matched = match and len(groups) == self.url_pattern.groups and method in self.methods
        if not matched:
            return NOT_FOUND, NOT_FOUND

        return self.callback(query, *match.groups())


# url_format - регулярное выражение
def route(url_format, methods=None):
    if methods is None:
        methods = ['GET']

    def wrapper(func):
        def inner(url, query, *args, **kwargs):
            pattern = re.compile(url_format)
            match = re.fullmatch(pattern, url)

            if match is None or len(match.groups()) != pattern.groups:
                return NOT_FOUND, '404 NOT FOUND'

            return func(query, *match.groups(), *args, **kwargs)

        # Добавляем вызываемую функцию в дерево роутера.
        # Благодаря этому указывать паттерн url и метод нужно указывать
        # только в инициализаторе декоратора, а функция run сама разберется
        # при каких условиях нужно вызвать конкретную функцию
        _router_table.append(Route(func, url_format, methods))
        return inner

    return wrapper


def run(request):
    res = NOT_FOUND, NOT_FOUND

    for key, value in request['cookies'].items():
        db.set_cookie(key, value)

    method, url = request['method'], request['url']

    for route in _router_table:
        if route.check_route(url, method):
            res = route.invoke_callback(url, method, request['query'])
            break

    return add_text_headers(*res)


_router_table = [
    # Пока ничего лучше не придумал
    Route(fallback_wrong_method, '/.*', list(set(HTTP_METHODS) ^ {'GET', 'POST'})),

    Route(index_get, '/'),

    Route(divide_get, r'/div/(\d+)/to/(\d+)/?'),
    Route(divide_post, r'/div/?', ['POST']),

    Route(show_errors_get, r'/show_errors/(\d{1})/?'),
    Route(show_errors_post, r'/show_errors/?', ['POST']),

    Route(set_cookie_get, r'/set_cookie/=/(.*)/?'),
    Route(set_cookie_post, r'/set_cookie/=/?', ['POST']),

    Route(short_log_get, r'/short_log/(\d{1})/?'),
    Route(short_log_post, r'/short_log/?', ['POST']),
]