Passed
Push — develop ( 4d71fc...084267 )
by Christophe
01:13
created

CollectionsInfoCommand.handle()   B

Complexity

Conditions 6

Size

Total Lines 40
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 22
dl 0
loc 40
rs 8.4186
c 0
b 0
f 0
cc 6
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.line(
128
            f"Add file <comment>'{self.argument('file')}'</> to "
129
            f"collection <comment>'{self.argument('name')}'</>"
130
        )
131
        return 0
132
133
134
class CollectionsDeleteCommand(Command):
135
    """
136
    CollectionDeleteCommand.
137
    """
138
139
    name = "collections delete"
140
    description = "Delete a collection"
141
    arguments = [name_arg]
142
    help = (  # noqa: A003,VNE003
143
        "Deleting a collection will erase the folder containing its files. "
144
        "The operation cannot proceed if the collection is used by a set of icons."
145
    )
146
147
    def handle(self) -> int:
148
        """
149
        Handle collections delete command.
150
151
        Returns
152
        -------
153
        int
154
            status code
155
156
        Raises
157
        ------
158
        ValueError
159
            If an error occurs.
160
        """
161
        name = self.argument("name")
162
        if name == "fontawesome":
163
            raise ValueError("You cannot modify core collection")
164
        dir_path = pathlib.Path(sys.prefix, "share", "pandoc_latex_tip", name)
165
        config_path = pathlib.Path(
166
            sys.prefix,
167
            "share",
168
            "pandoc_latex_tip",
169
            "config.yml",
170
        )
171
        if config_path.exists():
172
            with config_path.open(encoding="utf-8") as stream:
173
                icons = yaml.safe_load(stream)
174
                for definition in icons:
175
                    if definition["collection"] == name:
176
                        raise ValueError(f"Collection '{name}' is in use")
177
178
        if not dir_path.exists():
179
            raise ValueError(f"Collection '{name}' does not exist")
180
181
        shutil.rmtree(dir_path)
182
        self.line(f"Delete collection <comment>'{name}'</>")
183
        return 0
184
185
186
class CollectionsListCommand(Command):
187
    """
188
    CollectionsListCommand.
189
    """
190
191
    name = "collections"
192
    description = "List the collections"
193
194
    def handle(self) -> int:
195
        """
196
        Handle collections command.
197
198
        Returns
199
        -------
200
        int
201
            status code
202
        """
203
        dir_path = pathlib.Path(sys.prefix, "share", "pandoc_latex_tip")
204
        self.line("<b>Collections</>")
205
        for folder in dir_path.iterdir():
206
            if folder.parts[-1] == "fontawesome":
207
                self.line("<error>fontawesome</>")
208
            elif folder.is_dir():
209
                self.line(f"<comment>{folder.parts[-1]}</>")
210
        return 0
211
212
213
class CollectionsInfoCommand(Command):
214
    """
215
    CollectionsInfoCommand.
216
    """
217
218
    name = "collections info"
219
    description = "Display a collection"
220
    arguments = [name_arg]
221
    help = (  # noqa: A003, VNE003
222
        "Displaying a collection allows listing all the "
223
        "CSS and TTF files it contains."
224
    )
225
226
    def handle(self) -> int:
227
        """
228
        Handle collections info command.
229
230
        Returns
231
        -------
232
        int
233
            status code
234
235
        Raises
236
        ------
237
        ValueError
238
            If an error occurs.
239
        """
240
        name = self.argument("name")
241
        dir_path = pathlib.Path(
242
            sys.prefix,
243
            "share",
244
            "pandoc_latex_tip",
245
            name,
246
        )
247
        if not dir_path.exists():
248
            raise ValueError(f"Collection '{name}' does not exist")
249
250
        self.line("<b>Name</>")
251
        self.line(f"<comment>{name}</>")
252
253
        self.line("")
254
        self.line("<b>CSS files</>")
255
        for filename in dir_path.iterdir():
256
            if filename.suffix == ".css":
257
                self.line(f"<comment>{filename.parts[-1]}</>")
258
259
        self.line("")
260
        self.line("<b>TTF files</>")
261
        for filename in dir_path.iterdir():
262
            if filename.suffix == ".ttf":
263
                self.line(f"<comment>{filename.parts[-1]}</>")
264
265
        return 0
266
267
268
class IconsAddCommand(Command):
269
    """
270
    IconsAddCommand.
271
    """
272
273
    name = "icons add"
274
    description = "Add a set of icons from a collection"
275
    arguments = [name_arg]
276
    options = [css_opt, ttf_opt, prefix_opt]
277
    help = (  # noqa: A003, VNE003
278
        "A set of icons is created from a CSS file and a TTF file from a collection. "
279
        "The prefix ensures that the icons are unique. "
280
        "It replaces the common prefix detected in the CSS file."
281
    )
282
283
    # pylint: disable=too-many-return-statements, too-many-branches
284
    def handle(self) -> int:
285
        """
286
        Handle icons add command.
287
288
        Returns
289
        -------
290
        int
291
            status code
292
293
        Raises
294
        ------
295
        ValueError
296
            If an error occurs.
297
        """
298
        name = self.argument("name")
299
        if name == "fontawesome":
300
            raise ValueError("You cannot modify core collection")
301
        dir_path = pathlib.Path(
302
            sys.prefix,
303
            "share",
304
            "pandoc_latex_tip",
305
            self.argument("name"),
306
        )
307
        if dir_path.exists():
308
            if not self.option("CSS"):
309
                raise ValueError("CSS option is mandatory")
310
            css = self.option("CSS")
311
            css_file = pathlib.Path(dir_path, css)
312
            if not css_file.exists():
313
                raise ValueError(f"Collection '{name}' does not contain '{css}'")
314
            if not self.option("TTF"):
315
                raise ValueError("TTF option is mandatory")
316
            ttf = self.option("TTF")
317
            ttf_file = pathlib.Path(dir_path, ttf)
318
            if not ttf_file.exists():
319
                raise ValueError(f"Collection '{name}' does not contain '{ttf}'")
320
            if not self.option("prefix"):
321
                raise ValueError("prefix option is mandatory")
322
            prefix = self.option("prefix")
323
            config_path = pathlib.Path(
324
                sys.prefix,
325
                "share",
326
                "pandoc_latex_tip",
327
                "config.yml",
328
            )
329
            if config_path.exists():
330
                with config_path.open(encoding="utf-8") as stream:
331
                    icons = yaml.safe_load(stream)
332
                    for definition in icons:
333
                        if definition["prefix"] == prefix:
334
                            raise ValueError(f"Prefix '{prefix}' is already used")
335
            else:
336
                icons = []
337
            icons.append(
338
                {
339
                    "collection": name,
340
                    "CSS": css,
341
                    "TTF": ttf,
342
                    "prefix": prefix,
343
                },
344
            )
345
            with config_path.open(mode="w", encoding="utf-8") as stream:
346
                stream.write(yaml.dump(icons, sort_keys=False))
347
348
        else:
349
            raise ValueError(f"Collection '{name}' does not exist")
350
        return 0
351
352
353
class IconsDeleteCommand(Command):
354
    """
355
    IconsDeleteCommand.
356
    """
357
358
    name = "icons delete"
359
    description = "Delete a set of icons"
360
    options = [prefix_opt]
361
362
    def handle(self) -> int:
363
        """
364
        Handle icons delete command.
365
366
        Returns
367
        -------
368
        int
369
            status code
370
371
        Raises
372
        ------
373
        ValueError
374
            If an error occurs.
375
        """
376
        if not self.option("prefix"):
377
            raise ValueError("prefix option is mandatory")
378
        prefix = self.option("prefix")
379
        if prefix in ("fa-", "far-", "fab-"):
380
            raise ValueError("You cannot modify core icons")
381
        config_path = pathlib.Path(
382
            sys.prefix,
383
            "share",
384
            "pandoc_latex_tip",
385
            "config.yml",
386
        )
387
        if config_path.exists():
388
            with config_path.open(encoding="utf-8") as stream:
389
                icons = yaml.safe_load(stream)
390
            keep = [
391
                definition for definition in icons if definition["prefix"] != prefix
392
            ]
393
            if keep != icons:
394
                with config_path.open(mode="w", encoding="utf-8") as stream:
395
                    stream.write(yaml.dump(keep, sort_keys=False))
396
            else:
397
                raise ValueError("Unexisting prefix")
398
        else:
399
            raise ValueError("Unexisting config file")
400
        return 0
401
402
403
class IconsListCommand(Command):
404
    """
405
    IconsListCommand.
406
    """
407
408
    name = "icons"
409
    description = "List the set of icons"
410
411
    def handle(self) -> int:
412
        """
413
        Handle icons command.
414
415
        Returns
416
        -------
417
        int
418
            status code
419
        """
420
        icons = get_core_icons()
421
        config_path = pathlib.Path(
422
            sys.prefix,
423
            "share",
424
            "pandoc_latex_tip",
425
            "config.yml",
426
        )
427
        if config_path.exists():
428
            with config_path.open(encoding="utf-8") as stream:
429
                icons.extend(yaml.safe_load(stream))
430
        self.line(yaml.dump(icons, sort_keys=False))
431
432
        return 0
433
434
435
class PandocLaTeXFilterCommand(Command):
436
    """
437
    PandocLaTeXFilterCommand.
438
    """
439
440
    name = "latex"
441
    description = "Run pandoc filter for LaTeX document"
442
443
    def handle(self) -> int:
444
        """
445
        Handle latex command.
446
447
        Returns
448
        -------
449
        int
450
            status code
451
        """
452
        main()
453
        return 0
454
455
456
class PandocBeamerFilterCommand(Command):
457
    """
458
    PandocBeamerFilterCommand.
459
    """
460
461
    name = "beamer"
462
    description = "Run pandoc filter for Beamer document"
463
464
    def handle(self) -> int:
465
        """
466
        Handle beamer command.
467
468
        Returns
469
        -------
470
        int
471
            status code
472
        """
473
        main()
474
        return 0
475
476
477
def app() -> None:
478
    """
479
    Create a cleo application.
480
    """
481
    application = Application(
482
        name="pandoc-latex-tip filter",
483
        version=version("pandoc-latex-tip"),
484
    )
485
    application.set_display_name("pandoc-latex-tip filter")
486
    application.add(InfoCommand())
487
    application.add(CollectionsAddCommand())
488
    application.add(CollectionsDeleteCommand())
489
    application.add(CollectionsListCommand())
490
    application.add(CollectionsInfoCommand())
491
    application.add(IconsAddCommand())
492
    application.add(IconsDeleteCommand())
493
    application.add(IconsListCommand())
494
    application.add(PandocLaTeXFilterCommand())
495
    application.add(PandocBeamerFilterCommand())
496
    application.run()
497