xref: /aosp_15_r20/external/bazelbuild-rules_python/python/private/text_util.bzl (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
1# Copyright 2023 The Bazel Authors. All rights reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Text manipulation utilities useful for repository rule writing."""
16
17def _indent(text, indent = " " * 4):
18    if "\n" not in text:
19        return indent + text
20
21    return "\n".join([indent + line for line in text.splitlines()])
22
23def _hanging_indent(text, indent = " " * 4):
24    if "\n" not in text:
25        return text
26
27    lines = text.splitlines()
28    for i, line in enumerate(lines):
29        lines[i] = (indent if i != 0 else "") + line
30    return "\n".join(lines)
31
32def _render_alias(name, actual, *, visibility = None):
33    args = [
34        "name = \"{}\",".format(name),
35        "actual = {},".format(actual),
36    ]
37
38    if visibility:
39        args.append("visibility = {},".format(render.list(visibility)))
40
41    return "\n".join([
42        "alias(",
43    ] + [_indent(arg) for arg in args] + [
44        ")",
45    ])
46
47def _render_dict(d, *, key_repr = repr, value_repr = repr):
48    if not d:
49        return "{}"
50
51    return "\n".join([
52        "{",
53        _indent("\n".join([
54            "{}: {},".format(key_repr(k), value_repr(v))
55            for k, v in d.items()
56        ])),
57        "}",
58    ])
59
60def _render_select(selects, *, no_match_error = None, key_repr = repr, value_repr = repr, name = "select"):
61    dict_str = _render_dict(selects, key_repr = key_repr, value_repr = value_repr) + ","
62
63    if no_match_error:
64        args = "\n".join([
65            "",
66            _indent(dict_str),
67            _indent("no_match_error = {},".format(no_match_error)),
68            "",
69        ])
70    else:
71        args = "\n".join([
72            "",
73            _indent(dict_str),
74            "",
75        ])
76
77    return "{}({})".format(name, args)
78
79def _render_list(items, *, hanging_indent = ""):
80    """Convert a list to formatted text.
81
82    Args:
83        items: list of items.
84        hanging_indent: str, indent to apply to second and following lines of
85            the formatted text.
86
87    Returns:
88        The list pretty formatted as a string.
89    """
90    if not items:
91        return "[]"
92
93    if len(items) == 1:
94        return "[{}]".format(repr(items[0]))
95
96    text = "\n".join([
97        "[",
98        _indent("\n".join([
99            "{},".format(repr(item))
100            for item in items
101        ])),
102        "]",
103    ])
104    if hanging_indent:
105        text = _hanging_indent(text, hanging_indent)
106    return text
107
108def _render_str(value):
109    return repr(value)
110
111def _render_tuple(items, *, value_repr = repr):
112    if not items:
113        return "tuple()"
114
115    if len(items) == 1:
116        return "({},)".format(value_repr(items[0]))
117
118    return "\n".join([
119        "(",
120        _indent("\n".join([
121            "{},".format(value_repr(item))
122            for item in items
123        ])),
124        ")",
125    ])
126
127def _toolchain_prefix(index, name, pad_length):
128    """Prefixes the given name with the index, padded with zeros to ensure lexicographic sorting.
129
130    Examples:
131      toolchain_prefix(   2, "foo", 4) == "_0002_foo_"
132      toolchain_prefix(2000, "foo", 4) == "_2000_foo_"
133    """
134    return "_{}_{}_".format(_left_pad_zero(index, pad_length), name)
135
136def _left_pad_zero(index, length):
137    if index < 0:
138        fail("index must be non-negative")
139    return ("0" * length + str(index))[-length:]
140
141render = struct(
142    alias = _render_alias,
143    dict = _render_dict,
144    hanging_indent = _hanging_indent,
145    indent = _indent,
146    left_pad_zero = _left_pad_zero,
147    list = _render_list,
148    select = _render_select,
149    str = _render_str,
150    toolchain_prefix = _toolchain_prefix,
151    tuple = _render_tuple,
152)
153