keithasaurus / koda-validate

Typesafe, Composable Validation
MIT License
112 stars 0 forks source link

Is it possible to provide an error message for a custom predicate and still use to_serializable_errs? #43

Closed ianjosephwilson closed 2 months ago

ianjosephwilson commented 4 months ago

I was trying out this library and I don't see a clear way to provide an error message for this custom predicate.

from typing import Annotated
from koda_validate import DataclassValidator, DateValidator, Predicate
from datetime import date, datetime, timedelta
from dataclasses import dataclass
from koda_validate.serialization import to_serializable_errs

def get_today() -> date:
    return datetime.utcnow().date()

class GreaterThan(Predicate[date]):
    def __init__(self, limit: callable) -> None:
        self.limit = limit

    def __call__(self, val: date) -> bool:
        return val > self.limit()

@dataclass
class Event:
    title: str
    start_date: Annotated[date, DateValidator(GreaterThan(get_today))]

event_validator = DataclassValidator(Event)

days_in_two_weeks = 14

# After today: OK
result = event_validator({
    'title': 'Your own birthday party',
    'start_date': (datetime.utcnow().date() + timedelta(days=days_in_two_weeks)).isoformat(),
})

assert isinstance(result.val.start_date, date)

# Before today: NOT OK
result = event_validator({
    'title': 'Your own birthday party',
    'start_date': (datetime.utcnow().date() - timedelta(days=days_in_two_weeks)).isoformat(),
})

assert not result.is_valid

print(to_serializable_errs(result))
keithasaurus commented 3 months ago

@ianjosephwilson There are a few ways you can do this, but first thing in my mind is something like:

def my_serializable_errs(invalid: Invalid) -> Serializable:
     match invalid.err_type:
           case PredicateErrs(preds):
                  return [
                         "whatever message" if isinstance(p, GreaterThan) else pred_to_err_message(p)
                         for p in preds
                  ]
           case _:
                  return to_serializable_errs(invalid, my_serializable_errs)

As noted in its docstring to_serializable_errs is really meant more as an example implementation than anything. koda_validate attempts to be agnostic to error output -- whether it's serializable, text, or some other format. The reason for this is that generating error messages / data tends to vary widely between different applications -- different formats, languages (or multiple languages), etc.