|
| 1 | +"""Factory functions for creating encoders and decoders based on type.""" |
| 2 | + |
| 3 | +from inspect import isclass |
| 4 | +from typing import Any, Type, TypeVar, Union, cast, get_origin |
| 5 | + |
| 6 | +from pydantic import BaseModel |
| 7 | +from tacoq.core.encoding.json_dict import JsonDictDecoder, JsonDictEncoder |
| 8 | +from tacoq.core.encoding.models import Decoder, Encoder, EncodingError |
| 9 | +from tacoq.core.encoding.passthrough import PassthroughDecoder, PassthroughEncoder |
| 10 | +from tacoq.core.encoding.pydantic import PydanticDecoder, PydanticEncoder |
| 11 | +from tacoq.core.encoding.string import StringDecoder, StringEncoder |
| 12 | + |
| 13 | +SUPPORTED_DATA_TYPES = (bytes, str, dict, list, BaseModel) |
| 14 | + |
| 15 | +T = TypeVar("T", bound=Union[bytes, str, dict[str, Any], list[Any], BaseModel]) |
| 16 | + |
| 17 | + |
| 18 | +def create_encoder(data_type: Type[T]) -> Encoder[T]: |
| 19 | + """Creates an appropriate encoder based on the input type. |
| 20 | +
|
| 21 | + ### Arguments: |
| 22 | + - data_type: The type to create an encoder for |
| 23 | +
|
| 24 | + ### Returns: |
| 25 | + An encoder instance appropriate for the given type |
| 26 | +
|
| 27 | + ### Raises: |
| 28 | + - EncodingError: If no suitable encoder is found for the type |
| 29 | + """ |
| 30 | + |
| 31 | + if data_type is bytes: |
| 32 | + return cast(Encoder[T], PassthroughEncoder()) |
| 33 | + elif data_type is str: |
| 34 | + return cast(Encoder[T], StringEncoder()) |
| 35 | + elif data_type is dict or get_origin(data_type) is dict: |
| 36 | + return cast(Encoder[T], JsonDictEncoder()) |
| 37 | + elif isclass(data_type) and issubclass(data_type, BaseModel): |
| 38 | + return cast(Encoder[T], PydanticEncoder()) |
| 39 | + else: |
| 40 | + raise EncodingError( |
| 41 | + f"No encoder found for type {data_type}. Available encoder types: {SUPPORTED_DATA_TYPES}. If yours isn't one of these, perhaps you should implement your own encoder?" |
| 42 | + ) |
| 43 | + |
| 44 | + |
| 45 | +def create_decoder(data_type: Type[T]) -> Decoder[T]: |
| 46 | + """Creates an appropriate decoder based on the input type. |
| 47 | +
|
| 48 | + ### Arguments: |
| 49 | + - data_type: The type to create a decoder for |
| 50 | +
|
| 51 | + ### Returns: |
| 52 | + A decoder instance appropriate for the given type |
| 53 | +
|
| 54 | + ### Raises: |
| 55 | + - EncodingError: If no suitable decoder is found for the type |
| 56 | + """ |
| 57 | + |
| 58 | + if data_type is bytes: |
| 59 | + return cast(Decoder[T], PassthroughDecoder()) |
| 60 | + elif data_type is str: |
| 61 | + return cast(Decoder[T], StringDecoder()) |
| 62 | + elif data_type is dict or get_origin(data_type) is dict: |
| 63 | + return cast(Decoder[T], JsonDictDecoder()) |
| 64 | + elif issubclass(data_type, BaseModel): |
| 65 | + return cast(Decoder[T], PydanticDecoder(model=data_type)) |
| 66 | + else: |
| 67 | + raise EncodingError( |
| 68 | + f"No decoder found for type {data_type}. Available decoder types: {SUPPORTED_DATA_TYPES}. If yours isn't one of these, perhaps you should implement your own decoder?" |
| 69 | + ) |
0 commit comments