aboutsummaryrefslogtreecommitdiff
path: root/lib/python/qmk/community_modules.py
blob: f7e96a6b93eaa3ccc0e78b6c55ad052013739fd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import os

from pathlib import Path
from functools import lru_cache

from milc.attrdict import AttrDict

from qmk.json_schema import json_load, validate, merge_ordered_dicts
from qmk.util import truthy
from qmk.constants import QMK_FIRMWARE, QMK_USERSPACE, HAS_QMK_USERSPACE
from qmk.path import under_qmk_firmware, under_qmk_userspace

COMMUNITY_MODULE_JSON_FILENAME = 'qmk_module.json'


class ModuleAPI(AttrDict):
    def __init__(self, **kwargs):
        super().__init__()
        for key, value in kwargs.items():
            self[key] = value


@lru_cache(maxsize=1)
def module_api_list():
    module_definition_files = sorted(set(QMK_FIRMWARE.glob('data/constants/module_hooks/*.hjson')))
    module_definition_jsons = [json_load(f) for f in module_definition_files]
    module_definitions = merge_ordered_dicts(module_definition_jsons)
    latest_module_version = module_definition_files[-1].stem
    latest_module_version_parts = latest_module_version.split('.')

    api_list = []
    for name, mod in module_definitions.items():
        api_list.append(ModuleAPI(
            ret_type=mod['ret_type'],
            name=name,
            args=mod['args'],
            call_params=mod.get('call_params', ''),
            guard=mod.get('guard', None),
            header=mod.get('header', None),
        ))

    return api_list, latest_module_version, latest_module_version_parts[0], latest_module_version_parts[1], latest_module_version_parts[2]


def find_available_module_paths():
    """Find all available modules.
    """
    search_dirs = []
    if HAS_QMK_USERSPACE:
        search_dirs.append(QMK_USERSPACE / 'modules')
    search_dirs.append(QMK_FIRMWARE / 'modules')

    modules = []
    for search_dir in search_dirs:
        for module_json_path in search_dir.rglob(COMMUNITY_MODULE_JSON_FILENAME):
            modules.append(module_json_path.parent)
    return modules


def find_module_path(module):
    """Find a module by name.
    """
    for module_path in find_available_module_paths():
        # Ensure the module directory is under QMK Firmware or QMK Userspace
        relative_path = under_qmk_firmware(module_path)
        if not relative_path:
            relative_path = under_qmk_userspace(module_path)
        if not relative_path:
            continue

        lhs = str(relative_path.as_posix())[len('modules/'):]
        rhs = str(Path(module).as_posix())

        if relative_path and lhs == rhs:
            return module_path
    return None


def load_module_json(module):
    """Load a module JSON file.
    """
    module_path = find_module_path(module)
    if not module_path:
        raise FileNotFoundError(f'Module not found: {module}')

    module_json = json_load(module_path / COMMUNITY_MODULE_JSON_FILENAME)

    if not truthy(os.environ.get('SKIP_SCHEMA_VALIDATION'), False):
        validate(module_json, 'qmk.community_module.v1')

    module_json['module'] = module
    module_json['module_path'] = module_path

    return module_json


def load_module_jsons(modules):
    """Load the module JSON files, matching the specified order.
    """
    return list(map(load_module_json, modules))