1"""
2Font Bakery CheckRunner is the driver of a font bakery suite of checks.
3"""
4import glob
5import logging
6import argparse
7from dataclasses import dataclass
8
9from fontbakery.callable import FontBakeryExpectedValue as ExpectedValue
10from fontbakery.profile import Profile
11from fontbakery.errors import ValueValidationError
12
13
14@dataclass
15class FileDescription:
16    name: str
17    extensions: list
18    singular: str
19    description: str
20
21
22class FontsProfile(Profile):
23    accepted_files = [
24        FileDescription(name="fonts",
25                        singular="font",
26                        extensions=[".otf",".ttf"],
27                        description="OpenType binary"),
28        FileDescription(name="ufos",
29                        singular="ufo",
30                        extensions=[".ufo"],
31                        description="UFO source"),
32        FileDescription(name="designspaces",
33                        singular="designspace",
34                        extensions=[".designspace"],
35                        description="Designspace"),
36        FileDescription(name="glyphs_files",
37                        singular="glyphs_file",
38                        extensions=[".glyphs"],
39                        description="Glyphs source"),
40        FileDescription(name="readme_md",
41                        singular="readme_md",
42                        extensions=["README.md"],
43                        description="Project's README markdown file"),
44        FileDescription(name="metadata_pb",
45                        singular="metadata_pb",
46                        extensions=["METADATA.pb"],
47                        description="Project's METADATA protobuf file"),
48    ]
49
50    def setup_argparse(self, argument_parser):
51        """
52        Set up custom arguments needed for this profile.
53        """
54        profile = self
55
56        def get_files(pattern):
57            files_to_check = []
58            # use glob.glob to accept *.ttf
59            # but perform a hacky fixup to workaround the square-brackets naming scheme
60            # currently in use for varfonts in google fonts...
61            if '].ttf' in pattern:
62                pattern = "*.ttf".join(pattern.split('].ttf'))
63
64            # Everything goes in for now, gets sorted in the Merge
65            for fullpath in glob.glob(pattern):
66                files_to_check.append(fullpath)
67            return files_to_check
68
69
70        class MergeAction(argparse.Action):
71            def __call__(self, parser, namespace, values, option_string=None):
72                for file_description in profile.accepted_files:
73                    setattr(namespace, file_description.name, [])
74                target = [item for l in values for item in l]
75                any_accepted = False
76                for file in target:
77                    accepted = False
78                    for file_description in profile.accepted_files:
79                        if any([file.endswith(extension) for extension in file_description.extensions]):
80                          setattr(namespace, file_description.name, getattr(namespace, file_description.name) + [file])
81                          accepted = True
82                          any_accepted = True
83                    if not accepted:
84                        logging.info(f"Skipping '{file}' as it does not"
85                                     f" seem to be accepted by this profile.")
86                if not any_accepted:
87                    raise ValueValidationError('No applicable files found')
88
89        argument_parser.add_argument(
90            'files',
91            # To allow optional commands like "-L" to work without other input
92            # files:
93            nargs='*',
94            type=get_files,
95            action=MergeAction,
96            help='file path(s) to check. Wildcards like *.ttf are allowed.')
97
98        return tuple(x.name for x in self.accepted_files)
99
100    def get_family_checks(self):
101        family_checks = self.get_checks_by_dependencies('fonts')
102        family_checks.extend(self.get_checks_by_dependencies('ttFonts'))
103        return family_checks
104
105    @classmethod
106    def _expected_values(cls):
107        return { val.name:
108          ExpectedValue(val.name,
109                        default = [],
110                        description = f"A list of the {val.description} file paths to check",
111                        force=True
112                        )
113          for val in cls.accepted_files
114        }
115
116    @classmethod
117    def _iterargs(cls):
118        return { val.singular: val.name for val in cls.accepted_files }
119
120
121def profile_factory(**kwds):
122    from fontbakery.profiles.shared_conditions import ttFont
123    profile = FontsProfile(
124        iterargs=FontsProfile._iterargs()
125      , conditions={ttFont.name: ttFont}
126      , derived_iterables={'ttFonts': ('ttFont', True)}
127      , expected_values=FontsProfile._expected_values()
128      , **kwds
129    )
130    return profile
131