Passed
Push — develop ( 2629f6...c6ca7c )
by Christophe
01:15
created

pandoc_latex_tip._app.IconsAddCommand.handle()   F

Complexity

Conditions 14

Size

Total Lines 82
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 59
dl 0
loc 82
rs 3.6
c 0
b 0
f 0
cc 14
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like pandoc_latex_tip._app.IconsAddCommand.handle() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
"""
2
App module.
3
"""
4
5
import pathlib
6
import shutil
7
import sys
8
from importlib.metadata import version
9
10
from cleo.application import Application
11
from cleo.commands.command import Command
12
from cleo.helpers import argument, option
13
14
import yaml
15
16
17
from ._main import get_core_icons, main
18
19
name_arg = argument(
20
    "name",
21
    description="Collection name",
22
)
23
file_arg = argument(
24
    "file",
25
    description="File name",
26
)
27
css_opt = option(
28
    "CSS",
29
    description="CSS filename from the collection",
30
    flag=False,
31
)
32
ttf_opt = option(
33
    "TTF",
34
    description="TTF filename from the collection",
35
    flag=False,
36
)
37
prefix_opt = option(
38
    "prefix",
39
    short_name="p",
40
    description="Icon prefix used to replace the common prefix found in the css file",
41
    flag=False,
42
)
43
44
45
class CollectionsAddCommand(Command):
46
    """
47
    CollectionsAddCommand.
48
    """
49
50
    name = "collections add"
51
    description = "Add a file to a collection"
52
    arguments = [name_arg, file_arg]
53
    help = (  # noqa: A003, VNE003
54
        "A collection is a space used to store all the CSS and TTF files "
55
        "related to one or more sets of icons."
56
    )
57
58
    def handle(self) -> int:
59
        """
60
        Handle collections add command.
61
62
        Returns
63
        -------
64
        int
65
            status code
66
        """
67
        if self.argument("name") == "fontawesome":
68
            self.line("<error>You cannot modify core collection</error>")
69
            return 1
70
71
        try:
72
            dir_path = pathlib.Path(
73
                sys.prefix, "share", "pandoc_latex_tip", self.argument("name")
74
            )
75
            if not dir_path.exists():
76
                dir_path.mkdir(parents=True)
77
            file_path = pathlib.Path(self.argument("file"))
78
            dest_path = pathlib.Path(dir_path, file_path.parts[-1])
79
            shutil.copy(file_path, dest_path)
80
81
            self.line(
82
                f"Add file '{self.argument('file')}' to "
83
                f"collection '{self.argument('name')}'"
84
            )
85
86
        except PermissionError as error:
87
            self.line(f"<error>{error}</error>")
88
            return 1
89
        return 0
90
91
92
class CollectionsDeleteCommand(Command):
93
    """
94
    CollectionDeleteCommand.
95
    """
96
97
    name = "collections delete"
98
    description = "Delete a collection"
99
    arguments = [name_arg]
100
    help = (  # noqa: A003,VNE003
101
        "Deleting a collection will erase the folder containing its files. "
102
        "The operation cannot proceed if the collection is used by a set of icons."
103
    )
104
105
    def handle(self) -> int:
106
        """
107
        Handle collections delete command.
108
109
        Returns
110
        -------
111
        int
112
            status code
113
        """
114
        if self.argument("name") == "fontawesome":
115
            self.line("<error>You cannot modify core collection</error>")
116
            return 1
117
        try:
118
            dir_path = pathlib.Path(
119
                sys.prefix, "share", "pandoc_latex_tip", self.argument("name")
120
            )
121
            config_path = pathlib.Path(
122
                sys.prefix,
123
                "share",
124
                "pandoc_latex_tip",
125
                "config.yml",
126
            )
127
            try:
128
                if config_path.exists():
129
                    with config_path.open(encoding="utf-8") as stream:
130
                        icons = yaml.safe_load(stream)
131
                        for definition in icons:
132
                            if definition["collection"] == self.argument("name"):
133
                                self.line(
134
                                    f"<error>Collection '{self.argument('name')}' "
135
                                    f"is in use</error>"
136
                                )
137
                                return 1
138
            except PermissionError as error:
139
                self.line(f"<error>{error}</error>")
140
                return 1
141
142
            if dir_path.exists():
143
                shutil.rmtree(dir_path)
144
                self.line(f"Delete collection '{self.argument('name')}'")
145
            else:
146
                self.line(
147
                    f"<error>Collection '{self.argument('name')}' "
148
                    f"does not exist</error>"
149
                )
150
                return 1
151
        except PermissionError as error:
152
            self.line(f"<error>{error}</error>")
153
            return 1
154
        return 0
155
156
157
class CollectionsListCommand(Command):
158
    """
159
    CollectionsListCommand.
160
    """
161
162
    name = "collections"
163
    description = "List the collections"
164
165
    def handle(self) -> int:
166
        """
167
        Handle collections command.
168
169
        Returns
170
        -------
171
        int
172
            status code
173
        """
174
        dir_path = pathlib.Path(sys.prefix, "share", "pandoc_latex_tip")
175
        for folder in dir_path.iterdir():
176
            if folder.parts[-1] == "fontawesome":
177
                self.line("<info>fontawesome *</info>")
178
            elif folder.is_dir():
179
                self.line(folder.parts[-1])
180
        return 0
181
182
183
class CollectionsInfoCommand(Command):
184
    """
185
    CollectionsInfoCommand.
186
    """
187
188
    name = "collections info"
189
    description = "Display a collection"
190
    arguments = [name_arg]
191
    help = (  # noqa: A003, VNE003
192
        "Displaying a collection allows listing all the "
193
        "CSS and TTF files it contains."
194
    )
195
196
    def handle(self) -> int:
197
        """
198
        Handle collections info command.
199
200
        Returns
201
        -------
202
        int
203
            status code
204
        """
205
        dir_path = pathlib.Path(
206
            sys.prefix,
207
            "share",
208
            "pandoc_latex_tip",
209
            self.argument("name"),
210
        )
211
        if dir_path.exists():
212
            for filename in dir_path.iterdir():
213
                self.line(filename.parts[-1])
214
        else:
215
            self.line(
216
                f"<error>Collection '{self.argument('name')}' "
217
                f"does not exist</error>"
218
            )
219
            return 1
220
        return 0
221
222
223
class IconsAddCommand(Command):
224
    """
225
    IconsAddCommand.
226
    """
227
228
    name = "icons add"
229
    description = "Add a set of icons from a collection"
230
    arguments = [name_arg]
231
    options = [css_opt, ttf_opt, prefix_opt]
232
    help = (  # noqa: A003, VNE003
233
        "A set of icons is created from a CSS file and a TTF file from a collection. "
234
        "The prefix ensures that the icons are unique. "
235
        "If set, it replaces the common prefix detected in the CSS file."
236
    )
237
238
    # pylint: disable=too-many-return-statements, too-many-branches
239
    def handle(self) -> int:
240
        """
241
        Handle icons add command.
242
243
        Returns
244
        -------
245
        int
246
            status code
247
        """
248
        if self.argument("name") == "fontawesome":
249
            self.line("<error>You cannot modify core collection</error>")
250
            return 1
251
        dir_path = pathlib.Path(
252
            sys.prefix,
253
            "share",
254
            "pandoc_latex_tip",
255
            self.argument("name"),
256
        )
257
        if dir_path.exists():
258
            if not self.option("CSS"):
259
                self.line("<error>CSS option is mandatory</error>")
260
                return 1
261
            css_file = pathlib.Path(dir_path, self.option("CSS"))
262
            if not css_file.exists():
263
                self.line(
264
                    f"<error>Collection '{self.argument('name')}' "
265
                    f"does not contain '{self.option('CSS')}'</error>"
266
                )
267
                return 1
268
            if not self.option("TTF"):
269
                self.line("<error>TTF option is mandatory</error>")
270
                return 1
271
            ttf_file = pathlib.Path(dir_path, self.option("TTF"))
272
            if not ttf_file.exists():
273
                self.line(
274
                    f"<error>Collection '{self.argument('name')}' "
275
                    f"does not contain '{self.option('TTF')}'</error>"
276
                )
277
                return 1
278
            if not self.option("prefix"):
279
                self.line("<error>prefix option is mandatory</error>")
280
                return 1
281
            config_path = pathlib.Path(
282
                sys.prefix,
283
                "share",
284
                "pandoc_latex_tip",
285
                "config.yml",
286
            )
287
            try:
288
                if config_path.exists():
289
                    with config_path.open(encoding="utf-8") as stream:
290
                        icons = yaml.safe_load(stream)
291
                        for definition in icons:
292
                            if definition["prefix"] == self.option("prefix"):
293
                                self.line(
294
                                    f"<error>Prefix '{self.option('prefix')}' "
295
                                    f"is already used</error>"
296
                                )
297
                                return 1
298
                else:
299
                    icons = []
300
                icons.append(
301
                    {
302
                        "collection": self.argument("name"),
303
                        "CSS": self.option("CSS"),
304
                        "TTF": self.option("TTF"),
305
                        "prefix": self.option("prefix"),
306
                    },
307
                )
308
                with config_path.open(mode="w", encoding="utf-8") as stream:
309
                    stream.write(yaml.dump(icons, sort_keys=False))
310
            except PermissionError as error:
311
                self.line(f"<error>{error}</error>")
312
                return 1
313
314
        else:
315
            self.line(
316
                f"<error>Collection '{self.argument('name')}' "
317
                f"does not exist</error>"
318
            )
319
            return 1
320
        return 0
321
322
323
class IconsDeleteCommand(Command):
324
    """
325
    IconsDeleteCommand.
326
    """
327
328
    name = "icons delete"
329
    description = "Delete a set of icons"
330
    options = [prefix_opt]
331
332
    def handle(self) -> int:
333
        """
334
        Handle icons delete command.
335
336
        Returns
337
        -------
338
        int
339
            status code
340
        """
341
        if not self.option("prefix"):
342
            self.line("<error>prefix option is mandatory</error>")
343
            return 1
344
        if self.option("prefix") in ("fa-", "far-", "fab-"):
345
            self.line("<error>You cannot modify core icons</error>")
346
            return 1
347
        config_path = pathlib.Path(
348
            sys.prefix,
349
            "share",
350
            "pandoc_latex_tip",
351
            "config.yml",
352
        )
353
        try:
354
            if config_path.exists():
355
                with config_path.open(encoding="utf-8") as stream:
356
                    icons = yaml.safe_load(stream)
357
                keep = [
358
                    definition
359
                    for definition in icons
360
                    if definition["prefix"] != self.option("prefix")
361
                ]
362
                if keep != icons:
363
                    with config_path.open(mode="w", encoding="utf-8") as stream:
364
                        stream.write(yaml.dump(keep, sort_keys=False))
365
                else:
366
                    self.line("<error>Unexisting prefix</error>")
367
                    return 1
368
            else:
369
                self.line("<error>Unexisting config file</error>")
370
                return 1
371
372
        except PermissionError as error:
373
            self.line(f"<error>{error}</error>")
374
            return 1
375
        return 0
376
377
378
class IconsListCommand(Command):
379
    """
380
    IconsListCommand.
381
    """
382
383
    name = "icons"
384
    description = "List the set of icons"
385
386
    def handle(self) -> int:
387
        """
388
        Handle icons command.
389
390
        Returns
391
        -------
392
        int
393
            status code
394
        """
395
        icons = get_core_icons()
396
        config_path = pathlib.Path(
397
            sys.prefix,
398
            "share",
399
            "pandoc_latex_tip",
400
            "config.yml",
401
        )
402
        try:
403
            if config_path.exists():
404
                with config_path.open(encoding="utf-8") as stream:
405
                    icons.extend(yaml.safe_load(stream))
406
        except PermissionError as error:
407
            self.line(f"<error>{error}</error>")
408
            return 1
409
410
        self.line(yaml.dump(icons, sort_keys=False))
411
412
        return 0
413
414
415
class PandocLaTeXFilterCommand(Command):
416
    """
417
    PandocLaTeXFilterCommand.
418
    """
419
420
    name = "latex"
421
    description = "Run pandoc filter for LaTeX document"
422
423
    def handle(self) -> int:
424
        """
425
        Handle latex command.
426
427
        Returns
428
        -------
429
        int
430
            status code
431
        """
432
        main()
433
        return 0
434
435
436
class PandocBeamerFilterCommand(Command):
437
    """
438
    PandocBeamerFilterCommand.
439
    """
440
441
    name = "beamer"
442
    description = "Run pandoc filter for Beamer document"
443
444
    def handle(self) -> int:
445
        """
446
        Handle beamer command.
447
448
        Returns
449
        -------
450
        int
451
            status code
452
        """
453
        main()
454
        return 0
455
456
457
def app() -> None:
458
    """
459
    Create a cleo application.
460
    """
461
    application = Application(
462
        name="pandoc-latex-tip filter",
463
        version=version("pandoc-latex-tip"),
464
    )
465
    application.set_display_name("pandoc-latex-tip filter")
466
    application.add(CollectionsAddCommand())
467
    application.add(CollectionsDeleteCommand())
468
    application.add(CollectionsListCommand())
469
    application.add(CollectionsInfoCommand())
470
    application.add(IconsAddCommand())
471
    application.add(IconsDeleteCommand())
472
    application.add(IconsListCommand())
473
    application.add(PandocLaTeXFilterCommand())
474
    application.add(PandocBeamerFilterCommand())
475
    application.run()
476