# Copyright 2023 The Bazel Authors. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Text manipulation utilities useful for repository rule writing.""" def _indent(text, indent = " " * 4): if "\n" not in text: return indent + text return "\n".join([indent + line for line in text.splitlines()]) def _hanging_indent(text, indent = " " * 4): if "\n" not in text: return text lines = text.splitlines() for i, line in enumerate(lines): lines[i] = (indent if i != 0 else "") + line return "\n".join(lines) def _render_alias(name, actual, *, visibility = None): args = [ "name = \"{}\",".format(name), "actual = {},".format(actual), ] if visibility: args.append("visibility = {},".format(render.list(visibility))) return "\n".join([ "alias(", ] + [_indent(arg) for arg in args] + [ ")", ]) def _render_dict(d, *, key_repr = repr, value_repr = repr): if not d: return "{}" return "\n".join([ "{", _indent("\n".join([ "{}: {},".format(key_repr(k), value_repr(v)) for k, v in d.items() ])), "}", ]) def _render_select(selects, *, no_match_error = None, key_repr = repr, value_repr = repr, name = "select"): dict_str = _render_dict(selects, key_repr = key_repr, value_repr = value_repr) + "," if no_match_error: args = "\n".join([ "", _indent(dict_str), _indent("no_match_error = {},".format(no_match_error)), "", ]) else: args = "\n".join([ "", _indent(dict_str), "", ]) return "{}({})".format(name, args) def _render_list(items, *, hanging_indent = ""): """Convert a list to formatted text. Args: items: list of items. hanging_indent: str, indent to apply to second and following lines of the formatted text. Returns: The list pretty formatted as a string. """ if not items: return "[]" if len(items) == 1: return "[{}]".format(repr(items[0])) text = "\n".join([ "[", _indent("\n".join([ "{},".format(repr(item)) for item in items ])), "]", ]) if hanging_indent: text = _hanging_indent(text, hanging_indent) return text def _render_str(value): return repr(value) def _render_tuple(items, *, value_repr = repr): if not items: return "tuple()" if len(items) == 1: return "({},)".format(value_repr(items[0])) return "\n".join([ "(", _indent("\n".join([ "{},".format(value_repr(item)) for item in items ])), ")", ]) def _toolchain_prefix(index, name, pad_length): """Prefixes the given name with the index, padded with zeros to ensure lexicographic sorting. Examples: toolchain_prefix( 2, "foo", 4) == "_0002_foo_" toolchain_prefix(2000, "foo", 4) == "_2000_foo_" """ return "_{}_{}_".format(_left_pad_zero(index, pad_length), name) def _left_pad_zero(index, length): if index < 0: fail("index must be non-negative") return ("0" * length + str(index))[-length:] render = struct( alias = _render_alias, dict = _render_dict, hanging_indent = _hanging_indent, indent = _indent, left_pad_zero = _left_pad_zero, list = _render_list, select = _render_select, str = _render_str, toolchain_prefix = _toolchain_prefix, tuple = _render_tuple, )