# Path: doctor/errors.py
# class SchemaError(DoctorError):
#     """Raised for errors in a schema."""
#     pass
# 
# class SchemaValidationError(DoctorError):
#     """Raised for errors when validating things against a schema."""
# 
#     def __init__(self, message, errors=None):
#         super(SchemaValidationError, self).__init__(message)
#         self.errors = errors
# 
# class TypeSystemError(DoctorError):
#     """An error that represents an invalid value for a type.
# 
#     This is borrowed from apistar:
#     https://github.com/encode/apistar/blob/
#     50dd15f0878f0a7c50ce829a72adb276782bcb78/apistar/exceptions.py#L4-L15
# 
#     :param detail: Detail about the error.
#     :param cls: The class type that was being instantiated.
#     :param code: The error code.
#     :param errors: A dict containing all validation errors during the request.
#         The key is the param name and the value is the error message.
#     """
#     def __init__(self,
#                  detail: Union[str, dict] = None,
#                  cls: type = None,
#                  code: str = None,
#                  errors: dict = None) -> None:
# 
#         if cls is not None and code is not None:
#             cls_errors = getattr(cls, 'errors')
#             detail = cls_errors[code].format(**cls.__dict__)
# 
#         self.detail = detail
#         if errors and len(errors) == 1:
#             param = list(errors.keys())[0]
#             msg = list(errors.values())[0]
#             detail = '{} - {}'.format(param, msg)
#         super().__init__(detail, errors=errors)
# 
# Path: doctor/parsers.py
# def parse_value(value, allowed_types, name='value'):
#     """Parse a value into one of a number of types.
# 
#     This function is used to coerce untyped HTTP parameter strings into an
#     appropriate type. It tries to coerce the value into each of the allowed
#     types, and uses the first that evaluates properly.
# 
#     Because this is coercing a string into multiple, potentially ambiguous,
#     types, it tests things in the order of least ambiguous to most ambiguous:
# 
#     - The "null" type is checked first. If allowed, and the value is blank
#       (""), None will be returned.
#     - The "boolean" type is checked next. Values of "true" (case insensitive)
#       are True, and values of "false" are False.
#     - Numeric types are checked next -- first "integer", then "number".
#     - The "array" type is checked next. A value is only considered a valid
#       array if it begins with a "[" and can be parsed as JSON.
#     - The "object" type is checked next. A value is only considered a valid
#       object if it begins with a "{" and can be parsed as JSON.
#     - The "string" type is checked last, since any value is a valid string.
#       Unicode strings are encoded as UTF-8.
# 
#     :param str value: Parameter value. Example: "1"
#     :param list allowed_types: Types that should be attempted. Example:
#         ["integer", "null"]
#     :param str name: Parameter name. If not specified, "value" is used.
#         Example: "campaign_id"
#     :returns: a tuple of a type string and coerced value
#     :raises: ParseError if the value cannot be coerced to any of the types
#     """
#     if not isinstance(value, str):
#         raise ValueError('value for %r must be a string' % name)
#     if isinstance(allowed_types, str):
#         allowed_types = [allowed_types]
# 
#     # Note that the order of these type considerations is important. Because we
#     # have an untyped value that may be one of any given number of types, we
#     # need a consistent order of evaluation in cases when there is ambiguity
#     # between types.
# 
#     if 'null' in allowed_types and value == '':
#         return 'null', None
# 
#     # For all of these types, we'll pass the value to the function and it will
#     # raise a TypeError or ValueError or return None if it can't be parsed as
#     # the given type.
# 
#     for allowed_type, parser in _parser_funcs:
#         if allowed_type in allowed_types:
#             try:
#                 parsed_value = parser(value)
#                 if parsed_value is not None:
#                     return allowed_type, parsed_value
#             except (TypeError, ValueError):
#                 # Ignore any errors, and continue trying other types
#                 pass
# 
#     raise ParseError('%s must be a valid type (%s)' %
#                      (name, ', '.join(allowed_types)))


import math
import re
import typing
import isodate
import rfc3987
from datetime import datetime
from typing import Any
from doctor.errors import SchemaError, SchemaValidationError, TypeSystemError
from doctor.parsers import parse_value
    from doctor.resource import ResourceSchema
    #: types aren't defined are allowed in the list.
    additional_items = False  # type: bool
    #: The minimum number of items allowed in the list.
    min_items = 0  # type: typing.Optional[int]
    #: The maxiimum number of items allowed in the list.
    max_items = None  # type: typing.Optional[int]
    #: If `True` items in the array should be unique from one another.
    unique_items = False  # type: bool

    def __init__(self, *args, **kwargs):
        if self.nullable and args[0] is None:
            return

        if args and isinstance(args[0], (str, bytes)):
            raise TypeSystemError(cls=self.__class__, code='type')

        try:
            value = list(*args, **kwargs)
        except TypeError:
            raise TypeSystemError(cls=self.__class__, code='type') from None

        if isinstance(self.items, list) and len(self.items) > 1:
            if len(value) < len(self.items):
                raise TypeSystemError(cls=self.__class__, code='min_items')
            elif len(value) > len(self.items) and not self.additional_items:
                raise TypeSystemError(cls=self.__class__, code='max_items')

        if len(value) < self.min_items:
            raise TypeSystemError(cls=self.__class__, code='min_items')
        elif self.max_items is not None and len(value) > self.max_items:

# Path: doctor/errors.py
# class SchemaError(DoctorError):
#     """Raised for errors in a schema."""
#     pass
# 
# class SchemaValidationError(DoctorError):
#     """Raised for errors when validating things against a schema."""
# 
#     def __init__(self, message, errors=None):
#         super(SchemaValidationError, self).__init__(message)
#         self.errors = errors
# 
# class TypeSystemError(DoctorError):
#     """An error that represents an invalid value for a type.
# 
#     This is borrowed from apistar:
#     https://github.com/encode/apistar/blob/
#     50dd15f0878f0a7c50ce829a72adb276782bcb78/apistar/exceptions.py#L4-L15
# 
#     :param detail: Detail about the error.
#     :param cls: The class type that was being instantiated.
#     :param code: The error code.
#     :param errors: A dict containing all validation errors during the request.
#         The key is the param name and the value is the error message.
#     """
#     def __init__(self,
#                  detail: Union[str, dict] = None,
#                  cls: type = None,
#                  code: str = None,
#                  errors: dict = None) -> None:
# 
#         if cls is not None and code is not None:
#             cls_errors = getattr(cls, 'errors')
#             detail = cls_errors[code].format(**cls.__dict__)
# 
#         self.detail = detail
#         if errors and len(errors) == 1:
#             param = list(errors.keys())[0]
#             msg = list(errors.values())[0]
#             detail = '{} - {}'.format(param, msg)
#         super().__init__(detail, errors=errors)
# 
# Path: doctor/parsers.py
# def parse_value(value, allowed_types, name='value'):
#     """Parse a value into one of a number of types.
# 
#     This function is used to coerce untyped HTTP parameter strings into an
#     appropriate type. It tries to coerce the value into each of the allowed
#     types, and uses the first that evaluates properly.
# 
#     Because this is coercing a string into multiple, potentially ambiguous,
#     types, it tests things in the order of least ambiguous to most ambiguous:
# 
#     - The "null" type is checked first. If allowed, and the value is blank
#       (""), None will be returned.
#     - The "boolean" type is checked next. Values of "true" (case insensitive)
#       are True, and values of "false" are False.
#     - Numeric types are checked next -- first "integer", then "number".
#     - The "array" type is checked next. A value is only considered a valid
#       array if it begins with a "[" and can be parsed as JSON.
#     - The "object" type is checked next. A value is only considered a valid
#       object if it begins with a "{" and can be parsed as JSON.
#     - The "string" type is checked last, since any value is a valid string.
#       Unicode strings are encoded as UTF-8.
# 
#     :param str value: Parameter value. Example: "1"
#     :param list allowed_types: Types that should be attempted. Example:
#         ["integer", "null"]
#     :param str name: Parameter name. If not specified, "value" is used.
#         Example: "campaign_id"
#     :returns: a tuple of a type string and coerced value
#     :raises: ParseError if the value cannot be coerced to any of the types
#     """
#     if not isinstance(value, str):
#         raise ValueError('value for %r must be a string' % name)
#     if isinstance(allowed_types, str):
#         allowed_types = [allowed_types]
# 
#     # Note that the order of these type considerations is important. Because we
#     # have an untyped value that may be one of any given number of types, we
#     # need a consistent order of evaluation in cases when there is ambiguity
#     # between types.
# 
#     if 'null' in allowed_types and value == '':
#         return 'null', None
# 
#     # For all of these types, we'll pass the value to the function and it will
#     # raise a TypeError or ValueError or return None if it can't be parsed as
#     # the given type.
# 
#     for allowed_type, parser in _parser_funcs:
#         if allowed_type in allowed_types:
#             try:
#                 parsed_value = parser(value)
#                 if parsed_value is not None:
#                     return allowed_type, parsed_value
#             except (TypeError, ValueError):
#                 # Ignore any errors, and continue trying other types
#                 pass
# 
#     raise ParseError('%s must be a valid type (%s)' %
#                      (name, ', '.join(allowed_types)))


# Path: doctor/types.py
import math
import re
import typing
import isodate
import rfc3987
from datetime import datetime
from typing import Any
from doctor.errors import SchemaError, SchemaValidationError, TypeSystemError
from doctor.parsers import parse_value
    from doctor.resource import ResourceSchema

    #: types aren't defined are allowed in the list.
    additional_items = False  # type: bool
    #: The minimum number of items allowed in the list.
    min_items = 0  # type: typing.Optional[int]
    #: The maxiimum number of items allowed in the list.
    max_items = None  # type: typing.Optional[int]
    #: If `True` items in the array should be unique from one another.
    unique_items = False  # type: bool

    def __init__(self, *args, **kwargs):
        if self.nullable and args[0] is None:
            return

        if args and isinstance(args[0], (str, bytes)):
            raise TypeSystemError(cls=self.__class__, code='type')

        try:
            value = list(*args, **kwargs)
        except TypeError:
            raise TypeSystemError(cls=self.__class__, code='type') from None

        if isinstance(self.items, list) and len(self.items) > 1:
            if len(value) < len(self.items):
                raise TypeSystemError(cls=self.__class__, code='min_items')
            elif len(value) > len(self.items) and not self.additional_items:
                raise TypeSystemError(cls=self.__class__, code='max_items')

        if len(value) < self.min_items:
            raise TypeSystemError(cls=self.__class__, code='min_items')
        elif self.max_items is not None and len(value) > self.max_items:

            raise TypeSystemError(cls=self.__class__, code='max_items')