aboutsummaryrefslogtreecommitdiff
path: root/tools/cru-py/cru/util/_func.py
blob: d9f80444fe117984441333fe43db05ea7d61f7b9 (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
120
121
122
123
124
125
126
from collections.abc import Callable
from typing import TypeVar

from ._cru import CRU
from ._const import CRU_PLACEHOLDER

T = TypeVar("T")

_PLACEHOLDER = CRU_PLACEHOLDER

class CruRawFunctions:
    @staticmethod
    def none(*_v, **_kwargs) -> None:
        return None

    @staticmethod
    def true(*_v, **_kwargs) -> True:
        return True

    @staticmethod
    def false(*_v, **_kwargs) -> False:
        return False

    @staticmethod
    def identity(v: T) -> T:
        return v

    @staticmethod
    def only_you(v: T, *_v, **_kwargs) -> T:
        return v

    @staticmethod
    def equal(a, b) -> bool:
        return a == b

    @staticmethod
    def not_equal(a, b) -> bool:
        return a != b

    @staticmethod
    def not_(v):
        return not v


class CruFunctionMeta:
    @staticmethod
    def bind(func: Callable, *bind_args, **bind_kwargs) -> Callable:
        def bound_func(*args, **kwargs):
            popped = 0
            real_args = []
            for arg in bind_args:
                if arg is _PLACEHOLDER:
                    real_args.append(args[popped])
                    popped += 1
                else:
                    real_args.append(arg)
            real_args.extend(args[popped:])
            return func(*real_args, **(bind_kwargs | kwargs))

        return bound_func

    @staticmethod
    def chain(*funcs: Callable) -> Callable:
        if len(funcs) == 0:
            raise ValueError("At least one function is required!")

        final_func = funcs[0]
        for func in funcs[1:]:
            func_copy = func

            def chained_func(*args, **kwargs):
                results = final_func(*args, **kwargs)
                results = results if isinstance(results, tuple) else (results,)
                return func_copy(*results)

            final_func = chained_func

        return final_func


# Advanced Function Wrapper
class CruFunction:
    def __init__(self, f: Callable):
        self._f = f

    @property
    def f(self) -> Callable:
        return self._f

    @property
    def func(self) -> Callable:
        return self.f

    def bind(self, *bind_args, **bind_kwargs) -> "CruFunction":
        self._f = CruFunctionMeta.bind(self._f, *bind_args, **bind_kwargs)
        return self

    def chain(self, *funcs: Callable) -> "CruFunction":
        self._f = CruFunctionMeta.chain(self._f, *funcs)
        return self

    def __call__(self, *args, **kwargs):
        return self._f(*args, **kwargs)

    @staticmethod
    def make_chain(base_func: Callable, *funcs: Callable) -> "CruFunction":
        return CruFunction(base_func).chain(*funcs)


class CruWrappedFunctions:
    none = CruFunction(CruRawFunctions.none)
    true = CruFunction(CruRawFunctions.true)
    false = CruFunction(CruRawFunctions.false)
    identity = CruFunction(CruRawFunctions.identity)
    only_you = CruFunction(CruRawFunctions.only_you)
    equal = CruFunction(CruRawFunctions.equal)
    not_equal = CruFunction(CruRawFunctions.not_equal)
    not_ = CruFunction(CruRawFunctions.not_)


class CruFunctionGenerators:
    @staticmethod
    def make_isinstance_of_types(*types: type) -> Callable:
        return CruFunction(lambda v: type(v) in types)

CRU.add_objects(CruRawFunctions, CruFunctionMeta, CruFunction, CruWrappedFunctions, CruFunctionGenerators)