Passed
Push — develop ( 084267...277b17 )
by Christophe
01:09
created

pandoc_latex_tip._app.IconsListCommand.handle()   B

Complexity

Conditions 5

Size

Total Lines 29
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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