1from typing import Any
2from typing import Dict
3from typing import List
4from typing import Optional
5from typing import Union
6
7from ..exceptions import NoSuchArgumentException
8from ..exceptions import NoSuchOptionException
9
10from .argument import Argument
11from .option import Option
12from .command_name import CommandName
13from .command_option import CommandOption
14
15
16class ArgsFormat(object):
17    """
18    The format used to parse a RawArgs instance.
19    """
20
21    def __init__(
22        self, elements=None, base_format=None
23    ):  # type: (Optional[Union[List[Any], ArgsFormatBuilder]], Optional[ArgsFormat])
24        from .args_format_builder import ArgsFormatBuilder
25
26        if elements is None:
27            elements = []
28
29        if isinstance(elements, ArgsFormatBuilder):
30            builder = elements
31        else:
32            builder = self._create_builder_for_elements(elements)
33
34        if base_format is None:
35            base_format = builder.base_format
36
37        self._base_format = base_format
38        self._command_names = builder.get_command_names(False)
39        self._command_options = {}
40        self._command_options_by_short_name = {}
41        self._arguments = builder.get_arguments(False)
42        self._options = builder.get_options(False)
43        self._options_by_short_name = {}
44        self._has_multi_valued_arg = builder.has_multi_valued_argument(False)
45        self._hash_optional_arg = builder.has_optional_argument(False)
46
47        for option in self._options.values():
48            if option.short_name:
49                self._options_by_short_name[option.short_name] = option
50
51        for command_option in builder.get_command_options():
52            self._command_options[command_option.long_name] = command_option
53
54            if command_option.short_name:
55                self._command_options_by_short_name[
56                    command_option.short_name
57                ] = command_option
58
59            for long_alias in command_option.long_aliases:
60                self._command_options[long_alias] = command_option
61
62            for short_alias in command_option.short_aliases:
63                self._command_options_by_short_name[short_alias] = command_option
64
65    @property
66    def base_format(self):  # type: () -> ArgsFormat
67        return self._base_format
68
69    def has_command_names(self, include_base=True):  # type: (bool) -> bool
70        if self._command_names:
71            return True
72
73        if include_base and self._base_format:
74            return self._base_format.has_command_names()
75
76        return False
77
78    def get_command_names(self, include_base=True):  # type: (bool) -> List[CommandName]
79        command_names = self._command_names
80
81        if include_base and self._base_format:
82            command_names = self._base_format.get_command_names() + command_names
83
84        return command_names
85
86    def has_command_option(self, name, include_base=True):  # type: (str, bool) -> bool
87        if name in self._command_options or name in self._command_options_by_short_name:
88            return True
89
90        if include_base and self._base_format:
91            return self._base_format.has_command_option(name)
92
93        return False
94
95    def has_command_options(self, include_base=True):  # type: (bool) -> bool
96        if self._command_options:
97            return True
98
99        if include_base and self._base_format:
100            return self._base_format.has_command_options()
101
102        return False
103
104    def get_command_option(
105        self, name, include_base=True
106    ):  # type: (str, bool) -> CommandOption
107        if name in self._command_options:
108            return self._command_options[name]
109
110        if name in self._command_options_by_short_name:
111            return self._command_options_by_short_name[name]
112
113        if include_base and self._base_format:
114            return self._base_format.get_command_option(name)
115
116        raise NoSuchOptionException(name)
117
118    def get_command_options(
119        self, include_base=True
120    ):  # type: (bool) -> List[CommandOption]
121        command_options = list(self._command_options.values())
122
123        if include_base and self._base_format:
124            command_options += self._base_format.get_command_options()
125
126        return command_options
127
128    def has_argument(
129        self, name, include_base=True
130    ):  # type: (Union[str, int], bool) -> bool
131        arguments = self.get_arguments(include_base)
132
133        if isinstance(name, int):
134            return name < len(arguments)
135
136        return name in arguments
137
138    def has_multi_valued_argument(self, include_base=True):  # type: (bool) -> bool
139        if self._has_multi_valued_arg:
140            return True
141
142        if include_base and self._base_format:
143            return self._base_format.has_multi_valued_argument()
144
145        return False
146
147    def has_optional_argument(self, include_base=True):  # type: (bool) -> bool
148        if self._hash_optional_arg:
149            return True
150
151        if include_base and self._base_format:
152            return self._base_format.has_optional_argument()
153
154        return False
155
156    def has_required_argument(self, include_base=True):  # type: (bool) -> bool
157        if not self._hash_optional_arg and self._arguments:
158            return True
159
160        if include_base and self._base_format:
161            return self._base_format.has_required_argument()
162
163        return False
164
165    def has_arguments(self, include_base=True):  # type: (bool) -> bool
166        if self._arguments:
167            return True
168
169        if include_base and self._base_format:
170            return self._base_format.has_arguments()
171
172        return False
173
174    def get_argument(
175        self, name, include_base=True
176    ):  # type: (Union[str, int], bool) -> Argument
177        if isinstance(name, int):
178            arguments = list(self.get_arguments(include_base).values())
179
180            if name >= len(arguments):
181                raise NoSuchArgumentException(name)
182        else:
183            arguments = self.get_arguments(include_base)
184
185            if name not in arguments:
186                raise NoSuchArgumentException(name)
187
188        return arguments[name]
189
190    def get_arguments(self, include_base=True):  # type: (bool) -> Dict[str, Argument]
191        arguments = self._arguments.copy()
192
193        if include_base and self._base_format:
194            base_arguments = self._base_format.get_arguments()
195            base_arguments.update(arguments)
196            arguments = base_arguments
197
198        return arguments
199
200    def has_option(self, name, include_base=True):  # type: (str, bool) -> bool
201        if name in self._options or name in self._options_by_short_name:
202            return True
203
204        if include_base and self._base_format:
205            return self._base_format.has_option(name)
206
207        return False
208
209    def has_options(self, include_base=True):  # type: (bool) -> bool
210        if self._options:
211            return True
212
213        if include_base and self._base_format:
214            return self._base_format.has_options()
215
216        return False
217
218    def get_option(self, name, include_base=True):  # type: (str, bool) -> Option
219        if name in self._options:
220            return self._options[name]
221
222        if name in self._options_by_short_name:
223            return self._options_by_short_name[name]
224
225        if include_base and self._base_format:
226            return self._base_format.get_option(name)
227
228        raise NoSuchOptionException(name)
229
230    def get_options(self, include_base=True):  # type: (bool) -> Dict[str, Option]
231        options = self._options.copy()
232
233        if include_base and self._base_format:
234            options.update(self._base_format.get_options())
235
236        return options
237
238    def _create_builder_for_elements(
239        self, elements, base_format=None
240    ):  # type: (List[Any], Optional[ArgsFormat]) -> ArgsFormatBuilder
241        from .args_format_builder import ArgsFormatBuilder
242
243        builder = ArgsFormatBuilder(base_format)
244
245        for element in elements:
246            if isinstance(element, CommandName):
247                builder.add_command_name(element)
248            elif isinstance(element, CommandOption):
249                builder.add_command_option(element)
250            elif isinstance(element, Option):
251                builder.add_option(element)
252            elif isinstance(element, Argument):
253                builder.add_argument(element)
254
255        return builder
256