chromium/components/exo/wayland/compatibility_test/wayland_protocol_identifiers.py

# Copyright 2020 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import collections
import functools
import itertools
import re
from typing import Dict, Iterable, Tuple

import wayland_protocol_data_classes

# Short aliases for typing
Interface = wayland_protocol_data_classes.Interface
Protocol = wayland_protocol_data_classes.Protocol


def split_for_words(x: str) -> Tuple[str, ...]:
    """Split underscore_seperated identifiers into a component words."""
    return tuple(w.lower() for w in x.split('_'))


def kebab_case(x: str) -> str:
    """Convert the input identifier to kebab-case"""
    return '-'.join(split_for_words(x))


def pascal_case(x: str) -> str:
    """Convert the input identifier to PascalCase"""
    return ''.join(w.title() for w in split_for_words(x))


@functools.lru_cache(maxsize=None)
def get_base_human_readable_name_map(
        protocols: Iterable[Protocol]) -> Dict[str, str]:
    """Determines a mapping of each interface name to an identifier name."""

    # Default to the full interface name as the identifier.
    interface_identifiers = dict((interface.name, interface.name)
                                 for protocol in protocols
                                 for interface in protocol.interfaces)

    # Reserve the full interface names as identifiers, so nothing is
    # ever shortened to another interface name.
    used = set(interface_identifiers.keys())

    def set_identifier_name_for_protocol(names: Iterable[str],
                                         interface: Interface) -> None:
        """Set the first unused name in |names| as the name for |interface|"""
        for name in names:
            if name not in used:
                used.add(name)
                interface_identifiers[interface.name] = name
                return

    def set_identifier_names_for_interface(
            interfaces: Iterable[Interface]) -> None:
        """Try and set a shorter name for each interface"""
        # For each interface in protocol, try and find a shorter name to use
        # when representing instances of that interface.
        for interface in interfaces:
            name = interface.name
            name_no_prefix = name.split('_', 1)[-1]
            name_no_suffix = re.sub('_v\d+$', '', name)
            name_no_prefix_or_suffix = re.sub(r'_v\d+$', '', name_no_prefix)

            set_identifier_name_for_protocol(
                [name_no_prefix_or_suffix, name_no_prefix, name_no_suffix],
                interface)

    def prioritize_interfaces(
            interfaces: Iterable[Interface]) -> Tuple[Interface, ...]:
        DEFAULT_STABLE_PRIORITY = 100
        DEFAULT_UNSTABLE_PRIORITY = 101
        UNSTABLE_PREFIX = 'z'
        prefix_priorities = dict(wl_=0,
                                 wp_=1,
                                 xdg_=2,
                                 zwl_=10,
                                 zwp_=11,
                                 zxdg_=12,
                                 aura_=200,
                                 cr_=201,
                                 zaura_=202,
                                 zcr_=203)

        interfaces_by_priority = collections.defaultdict(list)

        for protocol in protocols:
            for interface in protocol.interfaces:
                name = interface.name
                priority = DEFAULT_STABLE_PRIORITY if not name.startswith(
                    UNSTABLE_PREFIX) else DEFAULT_UNSTABLE_PRIORITY
                for prefix, prefix_priority in prefix_priorities.items():
                    if name.startswith(prefix):
                        priority = prefix_priority
                        break
                interfaces_by_priority[priority].append(interface)

        return tuple(
            itertools.chain(*tuple(interfaces_by_priority[p]
                                   for p in sorted(interfaces_by_priority))))

    set_identifier_names_for_interface(
        prioritize_interfaces(
            tuple(
                itertools.chain(*tuple(protocol.interfaces
                                       for protocol in protocols)))))
    return interface_identifiers