go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/lucicfg/starlark/stdlib/internal/strutil.star (about)

     1  # Copyright 2019 The LUCI Authors.
     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  """Utilities for working with strings."""
    16  
    17  load("@stdlib//internal/re.star", "re")
    18  
    19  def _expand_int_set(s):
    20      """Expands string with sets into a list of strings.
    21  
    22      For example, given `a{1..3}b` produces `['a1b', 'a2b', 'a3b']`.
    23  
    24      The incoming string should have no more than one `{...}` section. If it's
    25      absent, the function returns the list that contains one item: the original
    26      string.
    27  
    28      The set is given as comma-separated list of terms. Each term is either
    29      a single non-negative integer (e.g. `9`) or a range (e.g. `1..5`). Both ends
    30      of the range are inclusive. Ranges where the left hand side is larger than
    31      the right hand side are not allowed. All elements should be listed in the
    32      strictly increasing order (e.g. `1,2,5..10` is fine, but `5..10,1,2` is
    33      not). Spaces are not allowed.
    34  
    35      The output integers are padded with zeros to match the width of
    36      corresponding terms. For ranges this works only if both sides have same
    37      width. For example, `01,002,03..04` will expand into `01, 002, 03, 04`.
    38  
    39      Use `{{` and `}}` to escape `{` and `}` respectively.
    40  
    41      Args:
    42        s: a string with the set to expand. Required.
    43  
    44      Returns:
    45        A list of strings representing the expanded set.
    46      """
    47  
    48      # Implementation is in Go, since it is simpler there, considering Starlark
    49      # strings aren't even iterable.
    50      return __native__.expand_int_set(s)
    51  
    52  def _json_to_yaml(json):
    53      """Takes a JSON string and returns it as a pretty-printed YAML.
    54  
    55      Args:
    56        json: a JSON string to convert to YAML. Required.
    57  
    58      Returns:
    59        A pretty YAML string ending with `\n`.
    60      """
    61      return __native__.json_to_yaml(json)
    62  
    63  def _to_yaml(value):
    64      """Serializes a value to a pretty-printed YAML string.
    65  
    66      Doesn't support integers that do not fit int64. Fails if the value has
    67      cycles.
    68  
    69      Args:
    70        value: a primitive Starlark value: a scalar, or a list/tuple/dict
    71          containing only primitive Starlark values. Required.
    72  
    73      Returns:
    74        A pretty YAML string ending with `\n`.
    75      """
    76      return _json_to_yaml(json.encode(value))
    77  
    78  def _b64_encode(s):
    79      """Encodes a string using standard padded base64 encoding.
    80  
    81      Args:
    82        s: a string to encode. Required.
    83  
    84      Returns:
    85        A base64 string.
    86      """
    87      return __native__.b64_encode(s)
    88  
    89  def _b64_decode(s):
    90      """Decodes a string encoded using standard padded base64 encoding.
    91  
    92      Fails if `s` is not a base64 string.
    93  
    94      Args:
    95        s: a string to decode. Required.
    96  
    97      Returns:
    98        Decoded string.
    99      """
   100      return __native__.b64_decode(s)
   101  
   102  def _hex_encode(s):
   103      """Encodes a string as a sequence of hex bytes.
   104  
   105      Args:
   106        s: a string to encode. Required.
   107  
   108      Returns:
   109        A string with hexadecimal encoding.
   110      """
   111      return __native__.hex_encode(s)
   112  
   113  def _hex_decode(s):
   114      """Decodes a string encoded as a sequence of hex bytes.
   115  
   116      Args:
   117        s: a string to decode. Required.
   118  
   119      Returns:
   120        Decoded string.
   121      """
   122      return __native__.hex_decode(s)
   123  
   124  def _template(s):
   125      """Parses the given string as a Go text template and returns template object.
   126  
   127      See https://golang.org/pkg/text/template to syntax of Go text templates.
   128  
   129      Args:
   130        s: a string to parse as a template. Required.
   131  
   132      Returns:
   133        An object with `render(**kwargs)` method. It takes some kwargs with
   134        elementary types (strings, numbers, list and dicts) and uses them as
   135        inputs to the template, returning rendered template as a string.
   136      """
   137      return __native__.template(s)
   138  
   139  def _join_path(base, rel, allow_dots = False):
   140      """Joins two slash-separated paths together, normalizing the result.
   141  
   142      Args:
   143        base: a string with the base path.
   144        rel: a string with the path to append to it.
   145        allow_dots: if True, allow the resulting path to start with `..`.
   146  
   147      Returns:
   148        The joined path.
   149      """
   150      res, err = __native__.clean_relative_path(base, rel, allow_dots)
   151      if err:
   152          fail(err)
   153      return res
   154  
   155  def _parse_version(ver):
   156      """Parses `major.minor.revision` version string.
   157  
   158      Empty version components are assumed to be zeroes, e.g. `1.1` is the same
   159      as `1.1.0`. Extra components are ignored, e.g. `1.2.3.4` is the same as
   160      `1.2.3`. All components must be positive integers (fails otherwise), in
   161      particular versions like e.g. `1.2.3-rc1` aren't accepted.
   162  
   163      Args:
   164        ver: a version string to parse. Required.
   165  
   166      Returns:
   167        A triple of ints `(major, minor, revision)`.
   168      """
   169      if ver == None:
   170          fail("a version string is required")
   171      if type(ver) != "string":
   172          fail("bad version: got %s, want string" % type(ver))
   173      if not re.submatches(r"^\d+(\.\d+)*$", ver):
   174          fail("bad version string: should be `major.minor.revision`, got %r" % ver)
   175      val = [int(x) for x in ver.split(".")][:3]
   176      if len(val) < 3:
   177          val += [0] * (3 - len(val))
   178      return tuple(val)
   179  
   180  strutil = struct(
   181      expand_int_set = _expand_int_set,
   182      json_to_yaml = _json_to_yaml,
   183      to_yaml = _to_yaml,
   184      b64_encode = _b64_encode,
   185      b64_decode = _b64_decode,
   186      hex_encode = _hex_encode,
   187      hex_decode = _hex_decode,
   188      template = _template,
   189      join_path = _join_path,
   190      parse_version = _parse_version,
   191  )