# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_cast.ipynb.

# %% auto 0
__all__ = ['cast', 'caster', 'trycast']

# %% ../nbs/02_cast.ipynb 6
from functools import wraps

# %% ../nbs/02_cast.ipynb 8
from typing import Any, Union, Optional, Callable, TypeGuard

# %% ../nbs/02_cast.ipynb 10
#| export


# %% ../nbs/02_cast.ipynb 12
from chck import isnone, notnone

# %% ../nbs/02_cast.ipynb 14
from .atyp import O, P, T

# %% ../nbs/02_cast.ipynb 16
def cast(x, dtype: T, retnone: bool = False) -> Union[T, Any, None]:
    '''Tries to cast `x` to `dtype`. If `retnone` is `True`, returns `None` if the cast fails, otherwise returns `x`.'''
    try: return dtype(x)
    except: return None if retnone else x


def caster(dtype: T, retnone: bool = False):
    def decorator(func):
        @wraps(func)
        def wrapper(x, retnone: bool = retnone) -> Union[T, Any, None]:
            return cast(x, dtype, retnone)
        return wrapper
    return decorator

# %% ../nbs/02_cast.ipynb 18
def trycast(
    x: O, *fns: Callable[P, T], 
    retnone: bool = False, 
    guardfn: Optional[Callable[P, TypeGuard[T]]] = None,
) -> Union[T, Any, None]:
    '''Attempts to cast `x` using provided functions `fns`, returning `None` 
    or the original `x` if unsuccessful depending on `retnone`.
    
    Parameters
    ----------
    x : O
        The object to attempt to cast.
        
    *fns : Callable[P, T]
        Functions to attempt casting with.
        
    retnone : bool
        Whether to return `None` if casting fails.
        
    guardfn : Optional[Callable[P, TypeGuard[T]]]
        An optional guard function to check if `x` is already of the desired type.
    
    Returns
    -------
    Union[T, Any, None]
        The casted object, original object, or `None` if casting fails and `retnone` is True.
    '''
    
    # Short-circuit if `x` is already of the desired type
    if callable(guardfn) and guardfn(x): return x
    for fn in fns:
        try: 
            ret = fn(x)
            if notnone(ret): return ret
            if not retnone and isnone(ret): continue
            break
        except: ...
    return None if retnone else x
