1from typing import Any, Dict, Optional
2
3from draftjs_exporter.constants import BLOCK_TYPES, ENTITY_TYPES, INLINE_STYLES
4from draftjs_exporter.error import ConfigException
5from draftjs_exporter.types import ConfigMap, Props, RenderableType
6
7# Internal equivalent of a ConfigMap.
8OptionsMap = Dict[str, "Options"]
9
10
11class Options(object):
12    """
13    Facilitates querying configuration from a config map.
14    """
15
16    __slots__ = ("type", "element", "props", "wrapper", "wrapper_props")
17
18    def __init__(
19        self,
20        type_: str,
21        element: RenderableType,
22        props: Optional[Props] = None,
23        wrapper: RenderableType = None,
24        wrapper_props: Optional[Props] = None,
25    ) -> None:
26        self.type = type_
27        self.element = element
28        self.props = props if props else {}
29        self.wrapper = wrapper
30        self.wrapper_props = wrapper_props
31
32    def __str__(self) -> str:
33        return f"<Options {self.type} {self.element} {self.props} {self.wrapper} {self.wrapper_props}>"
34
35    def __repr__(self) -> str:
36        return str(self)
37
38    def __eq__(self, other: Any) -> bool:
39        """
40        Equality used in test code only, not to be relied on for the exporter.
41    """
42        return str(self) == str(other)
43
44    def __ne__(self, other: Any) -> bool:
45        return not self == other
46
47    def __hash__(self) -> int:
48        return hash(str(self))
49
50    @staticmethod
51    def create(kind_map: ConfigMap, type_: str, fallback_key: str) -> "Options":
52        """
53        Create an Options object from any mapping.
54        """
55        if type_ not in kind_map:
56            if fallback_key not in kind_map:
57                raise ConfigException(
58                    f'"{type_}" is not in the config and has no fallback'
59                )
60
61            config = kind_map[fallback_key]
62        else:
63            config = kind_map[type_]
64
65        if isinstance(config, dict):
66            if "element" not in config:
67                raise ConfigException(f'"{type_}" does not define an element')
68
69            opts = Options(type_, **config)
70        else:
71            opts = Options(type_, config)
72
73        return opts
74
75    @staticmethod
76    def map(kind_map: ConfigMap, fallback_key: str) -> OptionsMap:
77        options = {}
78        for type_ in kind_map:
79            options[type_] = Options.create(kind_map, type_, fallback_key)
80
81        return options
82
83    @staticmethod
84    def map_blocks(block_map: ConfigMap) -> OptionsMap:
85        return Options.map(block_map, BLOCK_TYPES.FALLBACK)
86
87    @staticmethod
88    def map_styles(style_map: ConfigMap) -> OptionsMap:
89        return Options.map(style_map, INLINE_STYLES.FALLBACK)
90
91    @staticmethod
92    def map_entities(entity_map: ConfigMap) -> OptionsMap:
93        return Options.map(entity_map, ENTITY_TYPES.FALLBACK)
94
95    @staticmethod
96    def get(options: OptionsMap, type_: str, fallback_key: str) -> "Options":
97        try:
98            return options[type_]
99        except KeyError:
100            try:
101                return options[fallback_key]
102            except KeyError:
103                raise ConfigException(
104                    f'"{type_}" is not in the config and has no fallback'
105                )
106