Skip to content
This repository was archived by the owner on Jan 31, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 98 additions & 0 deletions tests/test_predicate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import pytest

from tibia.predicate import all_, any_, not_, when, when_or, where
from tibia.value import Value


@pytest.mark.parametrize(
("value", "target"),
[
("", False),
("abc", False),
("abc123", True),
],
)
def test_all(value: str, target: bool) -> None:
pfn = all_(
lambda s: "a" in s,
lambda s: "1" in s,
)

assert pfn(value) == target


@pytest.mark.parametrize(
("value", "target"),
[
("", False),
("abc", True),
("abc123", True),
],
)
def test_any(value: str, target: bool) -> None:
pfn = any_(
lambda s: "a" in s,
lambda s: "1" in s,
)

assert pfn(value) == target


@pytest.mark.parametrize(
("value", "target", "target_not"),
[
("", False, True),
("abc", True, False),
("123", False, True),
],
)
def test_not(value: str, target: bool, target_not: bool) -> None:
not_pnf = not_(str.__contains__, "abc")

assert ("abc" in value) == target
assert not_pnf(value) == target_not


@pytest.mark.parametrize(
("value", "target"),
[
("", False),
("abc", True),
("abc123", True),
],
)
def test_where(value: str, target: bool) -> None:
pfn = (
where(str.__contains__, "a")
.and_(str.__contains__, "b")
.or_(str.__contains__, "c12")
.unwrap()
)

assert pfn(value) == target


@pytest.mark.parametrize(
("value", "target"),
[
(0, 0),
(1, 2),
],
)
def test_when(value: int, target: int) -> None:
result = Value(value).map(when, lambda i: i > 0, lambda i: i + 1).unwrap()

assert result == target


@pytest.mark.parametrize(
("value", "target"),
[
(0, "no"),
(1, "1"),
],
)
def test_when_or(value: int, target: str) -> None:
result = Value(value).map(when_or, lambda i: i > 0, "no", str).unwrap()

assert result == target
5 changes: 5 additions & 0 deletions tibia/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from .future_maybe import FutureMaybe
from .future_result import FutureResult
from .maybe import Empty, Maybe, Some
from .predicate import all_, any_, not_, where
from .result import Err, Ok, Result
from .value import Value

Expand All @@ -12,6 +13,10 @@
"Empty",
"Maybe",
"Some",
"all_",
"any_",
"not_",
"where",
"Err",
"Ok",
"Result",
Expand Down
74 changes: 74 additions & 0 deletions tibia/predicate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from __future__ import annotations

from typing import Callable, Concatenate


def all_[T](*pfns: Callable[[T], bool]) -> Callable[[T], bool]:
return lambda t: all(fn(t) for fn in pfns)


def any_[T](*pfns: Callable[[T], bool]) -> Callable[[T], bool]:
return lambda t: any(fn(t) for fn in pfns)


def not_[T, **P](
pfn: Callable[Concatenate[T, P], bool],
*args: P.args,
**kwargs: P.kwargs,
) -> Callable[[T], bool]:
return lambda t: not pfn(t, *args, **kwargs)


class where[T, **P]:
pfn: Callable[[T], bool]

def __init__(
self,
pfn: Callable[Concatenate[T, P], bool],
*args: P.args,
**kwargs: P.kwargs,
):
self.pfn = lambda t: pfn(t, *args, **kwargs)

def __call__(self, value: T) -> bool:
return self.pfn(value)

def or_[**P_](
self,
pfn: Callable[Concatenate[T, P_], bool],
*args: P_.args,
**kwargs: P_.kwargs,
) -> where[T]:
return where(lambda t: self.pfn(t) or pfn(t, *args, **kwargs))

def and_[**P_](
self,
pfn: Callable[Concatenate[T, P_], bool],
*args: P_.args,
**kwargs: P_.kwargs,
) -> where[T]:
return where(lambda t: self.pfn(t) and pfn(t, *args, **kwargs))

def unwrap(self) -> Callable[[T], bool]:
return self


def when[T, **P](
value: T,
pfn: Callable[[T], bool],
fn: Callable[Concatenate[T, P], T],
*args: P.args,
**kwargs: P.kwargs,
) -> T:
return fn(value, *args, **kwargs) if pfn(value) else value


def when_or[T, **P, R](
value: T,
pfn: Callable[[T], bool],
other: R,
fn: Callable[Concatenate[T, P], R],
*args: P.args,
**kwargs: P.kwargs,
) -> R:
return fn(value, *args, **kwargs) if pfn(value) else other