aboutsummaryrefslogtreecommitdiff
path: root/fingerd/utils.py
blob: 4f3ff104424d479e829f88ff0797fa071c2394f0 (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
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
#!/usr/bin/env python3
# *****************************************************************************
# Copyright (C) 2021 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
# This file is part of the fingerd Python 3.x module, which is MIT-licensed.
# *****************************************************************************
""" Utilities for the fingerd module. """

import logging as _logging
import re as _re

from datetime import timedelta as _td
from typing import Optional as _Optional, Union as _Union

__all__ = [
    'access_logger', 'error_logger', 'format_delta', 'logger', 'parse_delta',
]

__delta0_re = _re.compile(r'(-?)(([0-9]+[a-z]+)+)')
__delta1_re = _re.compile(r'([0-9]+)([a-z]+)')

logger = _logging.getLogger('fingerd')
access_logger = _logging.getLogger('fingerd.access')
error_logger = _logging.getLogger('fingerd.error')


def parse_delta(raw: str) -> _td:
    """ Parse a delta string as found in the configuration files. """

    try:
        delta = _td()

        sign, elements, _ = __delta0_re.fullmatch(raw).groups()
        sign = (1, -1)[len(sign)]
        for res in __delta1_re.finditer(elements):
            num, typ = res.groups()
            num = int(num)

            if typ == 'w':
                delta += _td(weeks=sign * num)
            elif typ in 'jd':
                delta += _td(days=sign * num)
            elif typ == 'h':
                delta += _td(hours=sign * num)
            elif typ == 'm':
                delta += _td(minutes=sign * num)
            elif typ == 's':
                delta += _td(seconds=sign * num)
            else:
                raise Exception

        return delta
    except Exception:
        return None


def format_delta(td: _td) -> str:
    """ Create a delta string. """

    sls = zip(
        (_td(days=7), _td(days=1), _td(seconds=3600), _td(seconds=60)),
        'wdhm',
    )

    if td >= _td():
        d = ''

        for span, letter in sls:
            n = td // span
            if n:
                d += f'{n}{letter}'
                td -= span * n

        s = td.seconds
        if not d or s:
            d += f'{s}s'
    else:
        d = '-'

        for span, letter in sls:
            n = -td // span
            if n:
                d += f'{n}{letter}'
                td += span * n

        s = (-td).seconds
        if s:
            d += f'{s}s'

    return d


def make_delta(
    value: _Optional[_Union[str, int, float, _td]],
    allow_none: bool = False,
) -> _td:
    """ Make a delta from a raw value. """

    if value is None:
        if not allow_none:
            raise ValueError('must not be None')

    if isinstance(value, _td):
        return value

    try:
        value = int(value)
    except (TypeError, ValueError):
        if isinstance(value, str):
            new_value = parse_delta(value)
            if new_value is not None:
                return new_value

            raise ValueError(f'invalid time delta: {value!r}')

        raise TypeError(f'unknown type {type(value).__name__}')
    else:
        return _td(seconds=value)

# End of file.