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 )