# Path: string_utils/errors.py
# class InvalidInputError(TypeError):
#     """
#     Custom error raised when received object is not a string as expected.
#     """
# 
#     def __init__(self, input_data: Any):
#         """
#         :param input_data: Any received object
#         """
#         type_name = type(input_data).__name__
#         msg = 'Expected "str", received "{}"'.format(type_name)
#         super().__init__(msg)
# 
# Path: string_utils/validation.py
# def is_snake_case(input_string: Any, separator: str = '_') -> bool:
#     """
#     Checks if a string is formatted as "snake case".
# 
#     A string is considered snake case when:
# 
#     - it's composed only by lowercase/uppercase letters and digits
#     - it contains at least one underscore (or provided separator)
#     - it does not start with a number
# 
#     *Examples:*
# 
#     >>> is_snake_case('foo_bar_baz') # returns true
#     >>> is_snake_case('foo') # returns false
# 
#     :param input_string: String to test.
#     :type input_string: str
#     :param separator: String to use as separator.
#     :type separator: str
#     :return: True for a snake case string, false otherwise.
#     """
#     if is_full_string(input_string):
#         re_map = {
#             '_': SNAKE_CASE_TEST_RE,
#             '-': SNAKE_CASE_TEST_DASH_RE
#         }
#         re_template = r'([a-z]+\d*{sign}[a-z\d{sign}]*|{sign}+[a-z\d]+[a-z\d{sign}]*)'
#         r = re_map.get(
#             separator,
#             re.compile(re_template.format(sign=re.escape(separator)), re.IGNORECASE)
#         )
# 
#         return r.match(input_string) is not None
# 
#     return False
# 
# def is_full_string(input_string: Any) -> bool:
#     """
#     Check if a string is not empty (it must contains at least one non space character).
# 
#     *Examples:*
# 
#     >>> is_full_string(None) # returns false
#     >>> is_full_string('') # returns false
#     >>> is_full_string(' ') # returns false
#     >>> is_full_string('hello') # returns true
# 
#     :param input_string: String to check.
#     :type input_string: str
#     :return: True if not empty, false otherwise.
#     """
#     return is_string(input_string) and input_string.strip() != ''
# 
# def is_camel_case(input_string: Any) -> bool:
#     """
#     Checks if a string is formatted as camel case.
# 
#     A string is considered camel case when:
# 
#     - it's composed only by letters ([a-zA-Z]) and optionally numbers ([0-9])
#     - it contains both lowercase and uppercase letters
#     - it does not start with a number
# 
#     *Examples:*
# 
#     >>> is_camel_case('MyString') # returns true
#     >>> is_camel_case('mystring') # returns false
# 
#     :param input_string: String to test.
#     :type input_string: str
#     :return: True for a camel case string, false otherwise.
#     """
#     return is_full_string(input_string) and CAMEL_CASE_TEST_RE.match(input_string) is not None
# 
# def is_integer(input_string: str) -> bool:
#     """
#     Checks whether the given string represents an integer or not.
# 
#     An integer may be signed or unsigned or use a "scientific notation".
# 
#     *Examples:*
# 
#     >>> is_integer('42') # returns true
#     >>> is_integer('42.0') # returns false
# 
#     :param input_string: String to check
#     :type input_string: str
#     :return: True if integer, false otherwise
#     """
#     return is_number(input_string) and '.' not in input_string
# 
# def is_string(obj: Any) -> bool:
#     """
#     Checks if an object is a string.
# 
#     *Example:*
# 
#     >>> is_string('foo') # returns true
#     >>> is_string(b'foo') # returns false
# 
#     :param obj: Object to test.
#     :return: True if string, false otherwise.
#     """
#     return isinstance(obj, str)


import base64
import random
import unicodedata
import zlib
from typing import Union
from uuid import uuid4
from ._regex import *
from .errors import InvalidInputError
from .validation import is_snake_case, is_full_string, is_camel_case, is_integer, is_string
    :return: Shuffled string
    """
    if not is_string(input_string):
        raise InvalidInputError(input_string)

    # turn the string into a list of chars
    chars = list(input_string)

    # shuffle the list
    random.shuffle(chars)

    # convert the shuffled list back to string
    return ''.join(chars)


def strip_html(input_string: str, keep_tag_content: bool = False) -> str:
    """
    Remove html code contained into the given string.

    *Examples:*

    >>> strip_html('test: <a href="foo/bar">click here</a>') # returns 'test: '
    >>> strip_html('test: <a href="foo/bar">click here</a>', keep_tag_content=True) # returns 'test: click here'

    :param input_string: String to manipulate.
    :type input_string: str
    :param keep_tag_content: True to preserve tag content, False to remove tag and its content too (default).
    :type keep_tag_content: bool
    :return: String with html removed.
    """

# Path: string_utils/errors.py
# class InvalidInputError(TypeError):
#     """
#     Custom error raised when received object is not a string as expected.
#     """
# 
#     def __init__(self, input_data: Any):
#         """
#         :param input_data: Any received object
#         """
#         type_name = type(input_data).__name__
#         msg = 'Expected "str", received "{}"'.format(type_name)
#         super().__init__(msg)
# 
# Path: string_utils/validation.py
# def is_snake_case(input_string: Any, separator: str = '_') -> bool:
#     """
#     Checks if a string is formatted as "snake case".
# 
#     A string is considered snake case when:
# 
#     - it's composed only by lowercase/uppercase letters and digits
#     - it contains at least one underscore (or provided separator)
#     - it does not start with a number
# 
#     *Examples:*
# 
#     >>> is_snake_case('foo_bar_baz') # returns true
#     >>> is_snake_case('foo') # returns false
# 
#     :param input_string: String to test.
#     :type input_string: str
#     :param separator: String to use as separator.
#     :type separator: str
#     :return: True for a snake case string, false otherwise.
#     """
#     if is_full_string(input_string):
#         re_map = {
#             '_': SNAKE_CASE_TEST_RE,
#             '-': SNAKE_CASE_TEST_DASH_RE
#         }
#         re_template = r'([a-z]+\d*{sign}[a-z\d{sign}]*|{sign}+[a-z\d]+[a-z\d{sign}]*)'
#         r = re_map.get(
#             separator,
#             re.compile(re_template.format(sign=re.escape(separator)), re.IGNORECASE)
#         )
# 
#         return r.match(input_string) is not None
# 
#     return False
# 
# def is_full_string(input_string: Any) -> bool:
#     """
#     Check if a string is not empty (it must contains at least one non space character).
# 
#     *Examples:*
# 
#     >>> is_full_string(None) # returns false
#     >>> is_full_string('') # returns false
#     >>> is_full_string(' ') # returns false
#     >>> is_full_string('hello') # returns true
# 
#     :param input_string: String to check.
#     :type input_string: str
#     :return: True if not empty, false otherwise.
#     """
#     return is_string(input_string) and input_string.strip() != ''
# 
# def is_camel_case(input_string: Any) -> bool:
#     """
#     Checks if a string is formatted as camel case.
# 
#     A string is considered camel case when:
# 
#     - it's composed only by letters ([a-zA-Z]) and optionally numbers ([0-9])
#     - it contains both lowercase and uppercase letters
#     - it does not start with a number
# 
#     *Examples:*
# 
#     >>> is_camel_case('MyString') # returns true
#     >>> is_camel_case('mystring') # returns false
# 
#     :param input_string: String to test.
#     :type input_string: str
#     :return: True for a camel case string, false otherwise.
#     """
#     return is_full_string(input_string) and CAMEL_CASE_TEST_RE.match(input_string) is not None
# 
# def is_integer(input_string: str) -> bool:
#     """
#     Checks whether the given string represents an integer or not.
# 
#     An integer may be signed or unsigned or use a "scientific notation".
# 
#     *Examples:*
# 
#     >>> is_integer('42') # returns true
#     >>> is_integer('42.0') # returns false
# 
#     :param input_string: String to check
#     :type input_string: str
#     :return: True if integer, false otherwise
#     """
#     return is_number(input_string) and '.' not in input_string
# 
# def is_string(obj: Any) -> bool:
#     """
#     Checks if an object is a string.
# 
#     *Example:*
# 
#     >>> is_string('foo') # returns true
#     >>> is_string(b'foo') # returns false
# 
#     :param obj: Object to test.
#     :return: True if string, false otherwise.
#     """
#     return isinstance(obj, str)


# Path: string_utils/manipulation.py
import base64
import random
import unicodedata
import zlib
from typing import Union
from uuid import uuid4
from ._regex import *
from .errors import InvalidInputError
from .validation import is_snake_case, is_full_string, is_camel_case, is_integer, is_string

    :return: Shuffled string
    """
    if not is_string(input_string):
        raise InvalidInputError(input_string)

    # turn the string into a list of chars
    chars = list(input_string)

    # shuffle the list
    random.shuffle(chars)

    # convert the shuffled list back to string
    return ''.join(chars)


def strip_html(input_string: str, keep_tag_content: bool = False) -> str:
    """
    Remove html code contained into the given string.

    *Examples:*

    >>> strip_html('test: <a href="foo/bar">click here</a>') # returns 'test: '
    >>> strip_html('test: <a href="foo/bar">click here</a>', keep_tag_content=True) # returns 'test: click here'

    :param input_string: String to manipulate.
    :type input_string: str
    :param keep_tag_content: True to preserve tag content, False to remove tag and its content too (default).
    :type keep_tag_content: bool
    :return: String with html removed.
    """

    if not is_string(input_string):