1use std::collections::HashMap;
24
25use crate::value::{RuntimeError, Value, list_from_vec, list_view};
26
27pub fn register(global: &mut HashMap<String, Value>) {
28 let mut members = HashMap::new();
29 for method in &[
30 "len",
31 "byteLength",
32 "startsWith",
33 "endsWith",
34 "contains",
35 "slice",
36 "trim",
37 "split",
38 "replace",
39 "join",
40 "charAt",
41 "chars",
42 "fromInt",
43 "fromFloat",
44 "fromBool",
45 "toLower",
46 "toUpper",
47 ] {
48 members.insert(
49 method.to_string(),
50 Value::Builtin(format!("String.{}", method)),
51 );
52 }
53 global.insert(
54 "String".to_string(),
55 Value::Namespace {
56 name: "String".to_string(),
57 members,
58 },
59 );
60}
61
62pub fn effects(_name: &str) -> &'static [&'static str] {
63 &[]
64}
65
66pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
68 match name {
69 "String.len" => Some(length(args)),
70 "String.byteLength" => Some(byte_length(args)),
71 "String.startsWith" => Some(starts_with(args)),
72 "String.endsWith" => Some(ends_with(args)),
73 "String.contains" => Some(contains(args)),
74 "String.slice" => Some(slice(args)),
75 "String.trim" => Some(trim(args)),
76 "String.split" => Some(split(args)),
77 "String.replace" => Some(replace(args)),
78 "String.join" => Some(join(args)),
79 "String.charAt" => Some(char_at(args)),
80 "String.chars" => Some(chars(args)),
81 "String.fromInt" => Some(from_int(args)),
82 "String.fromFloat" => Some(from_float(args)),
83 "String.fromBool" => Some(from_bool(args)),
84 "String.toLower" => Some(to_lower(args)),
85 "String.toUpper" => Some(to_upper(args)),
86 _ => None,
87 }
88}
89
90fn length(args: &[Value]) -> Result<Value, RuntimeError> {
93 let [val] = one_arg("String.len", args)?;
94 let Value::Str(s) = val else {
95 return Err(RuntimeError::Error(
96 "String.len: argument must be a String".to_string(),
97 ));
98 };
99 Ok(Value::Int(s.chars().count() as i64))
100}
101
102fn byte_length(args: &[Value]) -> Result<Value, RuntimeError> {
103 let [val] = one_arg("String.byteLength", args)?;
104 let Value::Str(s) = val else {
105 return Err(RuntimeError::Error(
106 "String.byteLength: argument must be a String".to_string(),
107 ));
108 };
109 Ok(Value::Int(s.len() as i64))
110}
111
112fn starts_with(args: &[Value]) -> Result<Value, RuntimeError> {
113 let [a, b] = two_args("String.startsWith", args)?;
114 let (Value::Str(s), Value::Str(prefix)) = (a, b) else {
115 return Err(RuntimeError::Error(
116 "String.startsWith: both arguments must be String".to_string(),
117 ));
118 };
119 Ok(Value::Bool(s.starts_with(prefix.as_str())))
120}
121
122fn ends_with(args: &[Value]) -> Result<Value, RuntimeError> {
123 let [a, b] = two_args("String.endsWith", args)?;
124 let (Value::Str(s), Value::Str(suffix)) = (a, b) else {
125 return Err(RuntimeError::Error(
126 "String.endsWith: both arguments must be String".to_string(),
127 ));
128 };
129 Ok(Value::Bool(s.ends_with(suffix.as_str())))
130}
131
132fn contains(args: &[Value]) -> Result<Value, RuntimeError> {
133 let [a, b] = two_args("String.contains", args)?;
134 let (Value::Str(s), Value::Str(sub)) = (a, b) else {
135 return Err(RuntimeError::Error(
136 "String.contains: both arguments must be String".to_string(),
137 ));
138 };
139 Ok(Value::Bool(s.contains(sub.as_str())))
140}
141
142fn slice(args: &[Value]) -> Result<Value, RuntimeError> {
143 if args.len() != 3 {
144 return Err(RuntimeError::Error(format!(
145 "String.slice() takes 3 arguments (s, from, to), got {}",
146 args.len()
147 )));
148 }
149 let Value::Str(s) = &args[0] else {
150 return Err(RuntimeError::Error(
151 "String.slice: first argument must be a String".to_string(),
152 ));
153 };
154 let Value::Int(from) = &args[1] else {
155 return Err(RuntimeError::Error(
156 "String.slice: second argument must be an Int".to_string(),
157 ));
158 };
159 let Value::Int(to) = &args[2] else {
160 return Err(RuntimeError::Error(
161 "String.slice: third argument must be an Int".to_string(),
162 ));
163 };
164 Ok(Value::Str(aver_rt::string_slice(s, *from, *to)))
165}
166
167fn trim(args: &[Value]) -> Result<Value, RuntimeError> {
168 let [val] = one_arg("String.trim", args)?;
169 let Value::Str(s) = val else {
170 return Err(RuntimeError::Error(
171 "String.trim: argument must be a String".to_string(),
172 ));
173 };
174 Ok(Value::Str(s.trim().to_string()))
175}
176
177fn split(args: &[Value]) -> Result<Value, RuntimeError> {
178 let [a, b] = two_args("String.split", args)?;
179 let (Value::Str(s), Value::Str(delim)) = (a, b) else {
180 return Err(RuntimeError::Error(
181 "String.split: both arguments must be String".to_string(),
182 ));
183 };
184 let parts: Vec<Value> = s
185 .split(delim.as_str())
186 .map(|p| Value::Str(p.to_string()))
187 .collect();
188 Ok(list_from_vec(parts))
189}
190
191fn replace(args: &[Value]) -> Result<Value, RuntimeError> {
192 if args.len() != 3 {
193 return Err(RuntimeError::Error(format!(
194 "String.replace() takes 3 arguments (s, old, new), got {}",
195 args.len()
196 )));
197 }
198 let (Value::Str(s), Value::Str(old), Value::Str(new)) = (&args[0], &args[1], &args[2]) else {
199 return Err(RuntimeError::Error(
200 "String.replace: all arguments must be String".to_string(),
201 ));
202 };
203 Ok(Value::Str(s.replace(old.as_str(), new.as_str())))
204}
205
206fn join(args: &[Value]) -> Result<Value, RuntimeError> {
207 let [a, b] = two_args("String.join", args)?;
208 let items = list_view(a).ok_or_else(|| {
209 RuntimeError::Error("String.join: first argument must be a List".to_string())
210 })?;
211 let Value::Str(sep) = b else {
212 return Err(RuntimeError::Error(
213 "String.join: second argument must be a String".to_string(),
214 ));
215 };
216 let strs: Result<Vec<String>, RuntimeError> = items
217 .iter()
218 .map(|v| match v {
219 Value::Str(s) => Ok(s.clone()),
220 _ => Err(RuntimeError::Error(
221 "String.join: list elements must be String".to_string(),
222 )),
223 })
224 .collect();
225 Ok(Value::Str(strs?.join(sep.as_str())))
226}
227
228fn char_at(args: &[Value]) -> Result<Value, RuntimeError> {
229 let [a, b] = two_args("String.charAt", args)?;
230 let Value::Str(s) = a else {
231 return Err(RuntimeError::Error(
232 "String.charAt: first argument must be a String".to_string(),
233 ));
234 };
235 let Value::Int(idx) = b else {
236 return Err(RuntimeError::Error(
237 "String.charAt: second argument must be an Int".to_string(),
238 ));
239 };
240 let idx = *idx as usize;
241 match s.chars().nth(idx) {
242 Some(c) => Ok(Value::Some(Box::new(Value::Str(c.to_string())))),
243 None => Ok(Value::None),
244 }
245}
246
247fn chars(args: &[Value]) -> Result<Value, RuntimeError> {
248 let [val] = one_arg("String.chars", args)?;
249 let Value::Str(s) = val else {
250 return Err(RuntimeError::Error(
251 "String.chars: argument must be a String".to_string(),
252 ));
253 };
254 let result: Vec<Value> = s.chars().map(|c| Value::Str(c.to_string())).collect();
255 Ok(list_from_vec(result))
256}
257
258fn from_int(args: &[Value]) -> Result<Value, RuntimeError> {
259 let [val] = one_arg("String.fromInt", args)?;
260 let Value::Int(n) = val else {
261 return Err(RuntimeError::Error(
262 "String.fromInt: argument must be an Int".to_string(),
263 ));
264 };
265 Ok(Value::Str(format!("{}", n)))
266}
267
268fn from_float(args: &[Value]) -> Result<Value, RuntimeError> {
269 let [val] = one_arg("String.fromFloat", args)?;
270 let Value::Float(f) = val else {
271 return Err(RuntimeError::Error(
272 "String.fromFloat: argument must be a Float".to_string(),
273 ));
274 };
275 Ok(Value::Str(format!("{}", f)))
276}
277
278fn from_bool(args: &[Value]) -> Result<Value, RuntimeError> {
279 let [val] = one_arg("String.fromBool", args)?;
280 let Value::Bool(b) = val else {
281 return Err(RuntimeError::Error(
282 "String.fromBool: argument must be a Bool".to_string(),
283 ));
284 };
285 Ok(Value::Str(if *b { "true" } else { "false" }.to_string()))
286}
287
288fn to_lower(args: &[Value]) -> Result<Value, RuntimeError> {
289 let [val] = one_arg("String.toLower", args)?;
290 let Value::Str(s) = val else {
291 return Err(RuntimeError::Error(
292 "String.toLower: argument must be a String".to_string(),
293 ));
294 };
295 Ok(Value::Str(s.to_lowercase()))
296}
297
298fn to_upper(args: &[Value]) -> Result<Value, RuntimeError> {
299 let [val] = one_arg("String.toUpper", args)?;
300 let Value::Str(s) = val else {
301 return Err(RuntimeError::Error(
302 "String.toUpper: argument must be a String".to_string(),
303 ));
304 };
305 Ok(Value::Str(s.to_uppercase()))
306}
307
308fn one_arg<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 1], RuntimeError> {
311 if args.len() != 1 {
312 return Err(RuntimeError::Error(format!(
313 "{}() takes 1 argument, got {}",
314 name,
315 args.len()
316 )));
317 }
318 Ok([&args[0]])
319}
320
321fn two_args<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 2], RuntimeError> {
322 if args.len() != 2 {
323 return Err(RuntimeError::Error(format!(
324 "{}() takes 2 arguments, got {}",
325 name,
326 args.len()
327 )));
328 }
329 Ok([&args[0], &args[1]])
330}