pandoc_latex_tip._app.InfoCommand.handle()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 30
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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