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}