"""Custom converters for :mod:`attrs`."""
import datetime
from typing import Any, Callable, Optional, TypeVar, Union

from netsgiro import AssignmentType, AvtaleGiroRegistrationType, ServiceCode, TransactionType

T = TypeVar('T')


def value_or_none(converter: Callable[[Any], T]) -> Callable[[Any], Optional[T]]:
    """Make converter that returns value or ``None``.

    ``converter`` is called to further convert non-``None`` values.

    This converter is identical to :func:`attr.converters.optional` in attrs >=
    17.1.0.
    """
    return lambda value: None if value is None else converter(value)


def truthy_or_none(converter: Callable[[Any], T]) -> Callable[[Any], Optional[T]]:
    """Make converter that returns a truthy value or ``None``.

    ``converter`` is called to further convert non-``None`` values.
    """
    return lambda value: converter(value) if value else None


def stripped_spaces_around(converter: Callable[[Any], T]) -> Callable[[Any], Optional[T]]:
    """Make converter that strips leading and trailing spaces.

    ``converter`` is called to further convert non-``None`` values.
    """
    return lambda value: None if value is None else converter(value.strip())


def stripped_newlines(converter: Callable[[Any], T]) -> Callable[[Any], Optional[T]]:
    """Make converter that returns a string stripped of newlines or ``None``.

    ``converter`` is called to further convert non-``None`` values.
    """
    return lambda value: converter(value.replace('\r', '').replace('\n', ''))


def fixed_len_str(length: int, converter: Callable[[Any], T]) -> Callable[[Any], Optional[T]]:
    """Make converter that pads a string to the given ``length`` or ``None``.

    ``converter`` is called to further convert non-``None`` values.
    """
    return lambda value: converter('{value:{length}}'.format(value=value, length=length))


def to_service_code(value: Union[ServiceCode, int, str]) -> ServiceCode:
    return ServiceCode(int(value))


def to_assignment_type(value: Union[AssignmentType, int, str]) -> AssignmentType:
    return AssignmentType(int(value))


def to_transaction_type(value: Union[TransactionType, int, str]) -> TransactionType:
    return TransactionType(int(value))


def to_avtalegiro_registration_type(
    value: Union[AvtaleGiroRegistrationType, int, str]
) -> AvtaleGiroRegistrationType:
    return AvtaleGiroRegistrationType(int(value))


def to_date(value: Union[datetime.date, str, None]) -> Optional[datetime.date]:
    if isinstance(value, datetime.date):
        return value
    if value is None or value == '000000':
        return None
    return datetime.datetime.strptime(value, '%d%m%y').date()


def to_bool(value: Union[bool, str]) -> bool:
    if isinstance(value, bool):
        return value
    if value == 'J':
        return True
    elif value == 'N':
        return False
    else:
        raise ValueError(f"Expected 'J' or 'N', got {value!r}")


to_safe_str_or_none = value_or_none(stripped_newlines(stripped_spaces_around(truthy_or_none(str))))

@@ -1857,24 +1857,142 @@
 =length))%0A%0A%0A
+to_safe_str_or_none: Callable = value_or_none(%0A    stripped_newlines(stripped_spaces_around(truthy_or_none(str)))%0A)%0A%0A%0A
 def to_servi
@@ -2052,16 +2052,56 @@
 ceCode:%0A
+    %22%22%22Convert input to ServiceCode.%22%22%22%0A
     retu
@@ -2207,24 +2207,67 @@
 gnmentType:%0A
+    %22%22%22Convert input to AssignmentType.%22%22%22%0A
     return A
@@ -2371,32 +2371,76 @@
 ransactionType:%0A
+    %22%22%22Convert input to TransactionType.%22%22%22%0A
     return Trans
@@ -2585,24 +2585,79 @@
 rationType:%0A
+    %22%22%22Convert input to AvtaleGiroRegistrationType.%22%22%22%0A
     return A
@@ -2743,14 +2743,8 @@
  str
-, None
 %5D) -
@@ -2770,16 +2770,57 @@
 .date%5D:%0A
+    %22%22%22Convert input to date or None.%22%22%22%0A
     if i
@@ -3046,16 +3046,49 @@
 %3E bool:%0A
+    %22%22%22Convert input to bool.%22%22%22%0A
     if i
@@ -3295,106 +3295,4 @@
 %7D%22)%0A
-%0A%0Ato_safe_str_or_none = value_or_none(stripped_newlines(stripped_spaces_around(truthy_or_none(str))))%0A

Add docstrings to converters