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