from collections.abc import Callable
from functools import singledispatch
from typing import overload


@overload
def slice_string(s: str, start: int, end: int, /) -> str: ...


@overload
def slice_string(s: str, start: int, /) -> str: ...


@overload
def slice_string(start: int, end: int, /) -> Callable[[str], str]: ...


@overload
def slice_string(start: int, /) -> Callable[[str], str]: ...


@singledispatch
def slice_string(string: str, start: int, end: int | None = None, /) -> str:
    """
    Given a string and start and end index returns a string from the start index to the end index.

    Alias to string[start:end].

    Start index is inclusive, end index is exclusive.

    End is optional if not provided returns the string from start to the end of the string given.

    Parameters
    ----------
    string : str
        Input string (positional-only).
    start : int
        Start index (positional-only).
    end : int, optional
        End index (positional-only).

    Returns
    -------
    str
        String from the start index to the end index.

    See Also
    --------
    slice

    Examples
    --------
    Data first:
    >>> R.slice_string('abcdefghijkl', 1)
    'bcdefghijkl'
    >>> R.slice_string('abcdefghijkl', 4, 7)
    'efg'

    Data last:
    >>> R.slice_string(1)('abcdefghijkl')
    'bcdefghijkl'
    >>> R.slice_string(4, 7)('abcdefghijkl')
    'efg'

    """
    if end is None:
        return string[start:]
    return string[start:end]


@slice_string.register  # pyright: ignore[reportFunctionMemberAccess]
def _(start: int, end: int | None = None, /) -> Callable[[str], str]:
    return lambda s: slice_string(s, start, end)  # pyright: ignore[reportArgumentType]
