starlark/values/types/string/
globals.rs

1/*
2 * Copyright 2018 The Starlark in Rust Authors.
3 * Copyright (c) Facebook, Inc. and its affiliates.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     https://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18use starlark_derive::starlark_module;
19
20use crate as starlark;
21use crate::environment::GlobalsBuilder;
22use crate::eval::Evaluator;
23use crate::values::string::StarlarkStr;
24use crate::values::StringValue;
25use crate::values::Value;
26use crate::values::ValueLike;
27
28#[starlark_module]
29pub(crate) fn register_str(globals: &mut GlobalsBuilder) {
30    /// [chr](
31    /// https://github.com/bazelbuild/starlark/blob/master/spec.md#bool
32    /// ): returns a string encoding a codepoint.
33    ///
34    /// `chr(i)` returns a string that encodes the single Unicode code
35    /// point whose value is specified by the integer `i`. `chr` fails
36    /// unless `0 ≤ i ≤ 0x10FFFF`.
37    ///
38    /// ```
39    /// # starlark::assert::all_true(r#"
40    /// chr(65) == 'A'
41    /// chr(1049) == 'Й'
42    /// chr(0x1F63F) == '😿'
43    /// # "#);
44    /// ```
45    #[starlark(speculative_exec_safe)]
46    fn chr(#[starlark(require = pos)] i: i32) -> anyhow::Result<char> {
47        let cp = u32::try_from(i)
48            .map_err(|_| anyhow::anyhow!("chr() parameter value negative integer {i}"))?;
49        match char::from_u32(cp) {
50            Some(x) => Ok(x),
51            None => Err(anyhow::anyhow!(
52                "chr() parameter value is 0x{:x} which is not a valid UTF-8 codepoint",
53                cp
54            )),
55        }
56    }
57
58    /// [ord](
59    /// https://github.com/bazelbuild/starlark/blob/master/spec.md#ord
60    /// ): returns the codepoint of a character
61    ///
62    /// `ord(s)` returns the integer value of the sole Unicode code point
63    /// encoded by the string `s`.
64    ///
65    /// If `s` does not encode exactly one Unicode code point, `ord` fails.
66    /// Each invalid code within the string is treated as if it encodes the
67    /// Unicode replacement character, U+FFFD.
68    ///
69    /// Example:
70    ///
71    /// ```
72    /// # starlark::assert::all_true(r#"
73    /// ord("A")                                == 65
74    /// ord("Й")                                == 1049
75    /// ord("😿")                               == 0x1F63F
76    /// # "#);
77    /// ```
78    #[starlark(speculative_exec_safe)]
79    fn ord<'v>(#[starlark(require = pos)] a: StringValue<'v>) -> anyhow::Result<i32> {
80        let mut chars = a.as_str().chars();
81        if let Some(c) = chars.next() {
82            if chars.next().is_none() {
83                return Ok(u32::from(c) as i32);
84            }
85        }
86        Err(anyhow::anyhow!(
87            "ord(): {} is not a single character string",
88            a.to_value().to_repr()
89        ))
90    }
91
92    /// [repr](
93    /// https://github.com/bazelbuild/starlark/blob/master/spec.md#repr
94    /// ): formats its argument as a string.
95    ///
96    /// All strings in the result are double-quoted.
97    ///
98    /// ```
99    /// # starlark::assert::all_true(r#"
100    /// repr(1)                 == '1'
101    /// repr("x")               == "\"x\""
102    /// repr([1, "x"])          == "[1, \"x\"]"
103    /// repr("test \"'")        == "\"test \\\"'\""
104    /// repr("x\"y😿 \\'")      == "\"x\\\"y\\U0001f63f \\\\'\""
105    /// # "#);
106    /// ```
107    #[starlark(speculative_exec_safe)]
108    fn repr<'v>(
109        #[starlark(require = pos)] a: Value<'v>,
110        eval: &mut Evaluator<'v, '_, '_>,
111    ) -> anyhow::Result<StringValue<'v>> {
112        let mut s = eval.string_pool.alloc();
113        a.collect_repr(&mut s);
114        let r = eval.heap().alloc_str(&s);
115        eval.string_pool.release(s);
116        Ok(r)
117    }
118
119    /// [str](
120    /// https://github.com/bazelbuild/starlark/blob/master/spec.md#str
121    /// ): formats its argument as a string.
122    ///
123    /// If x is a string, the result is x (without quotation).
124    /// All other strings, such as elements of a list of strings, are
125    /// double-quoted.
126    ///
127    /// ```
128    /// # starlark::assert::all_true(r#"
129    /// str(1)                          == '1'
130    /// str("x")                        == 'x'
131    /// str([1, "x"])                   == "[1, \"x\"]"
132    /// # "#);
133    /// ```
134    #[starlark(as_type = StarlarkStr, speculative_exec_safe)]
135    fn str<'v>(
136        #[starlark(require = pos)] a: Value<'v>,
137        eval: &mut Evaluator<'v, '_, '_>,
138    ) -> anyhow::Result<StringValue<'v>> {
139        if let Some(a) = StringValue::new(a) {
140            // Special case that can avoid reallocating, but is equivalent.
141            Ok(a)
142        } else {
143            let mut s = eval.string_pool.alloc();
144            a.collect_repr(&mut s);
145            let r = eval.heap().alloc_str(&s);
146            eval.string_pool.release(s);
147            Ok(r)
148        }
149    }
150}