1use std::collections::HashMap;
24use std::rc::Rc;
25
26use crate::nan_value::{Arena, NanValue};
27use crate::value::{RuntimeError, Value, list_from_vec, list_view};
28
29pub fn register(global: &mut HashMap<String, Value>) {
30 let mut members = HashMap::new();
31 for method in &[
32 "len",
33 "byteLength",
34 "startsWith",
35 "endsWith",
36 "contains",
37 "slice",
38 "trim",
39 "split",
40 "replace",
41 "join",
42 "charAt",
43 "chars",
44 "fromInt",
45 "fromFloat",
46 "fromBool",
47 "toLower",
48 "toUpper",
49 ] {
50 members.insert(
51 method.to_string(),
52 Value::Builtin(format!("String.{}", method)),
53 );
54 }
55 global.insert(
56 "String".to_string(),
57 Value::Namespace {
58 name: "String".to_string(),
59 members,
60 },
61 );
62}
63
64pub fn effects(_name: &str) -> &'static [&'static str] {
65 &[]
66}
67
68pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
70 match name {
71 "String.len" => Some(length(args)),
72 "String.byteLength" => Some(byte_length(args)),
73 "String.startsWith" => Some(starts_with(args)),
74 "String.endsWith" => Some(ends_with(args)),
75 "String.contains" => Some(contains(args)),
76 "String.slice" => Some(slice(args)),
77 "String.trim" => Some(trim(args)),
78 "String.split" => Some(split(args)),
79 "String.replace" => Some(replace(args)),
80 "String.join" => Some(join(args)),
81 "String.charAt" => Some(char_at(args)),
82 "String.chars" => Some(chars(args)),
83 "String.fromInt" => Some(from_int(args)),
84 "String.fromFloat" => Some(from_float(args)),
85 "String.fromBool" => Some(from_bool(args)),
86 "String.toLower" => Some(to_lower(args)),
87 "String.toUpper" => Some(to_upper(args)),
88 _ => None,
89 }
90}
91
92fn length(args: &[Value]) -> Result<Value, RuntimeError> {
95 let [val] = one_arg("String.len", args)?;
96 let Value::Str(s) = val else {
97 return Err(RuntimeError::Error(
98 "String.len: argument must be a String".to_string(),
99 ));
100 };
101 Ok(Value::Int(s.chars().count() as i64))
102}
103
104fn byte_length(args: &[Value]) -> Result<Value, RuntimeError> {
105 let [val] = one_arg("String.byteLength", args)?;
106 let Value::Str(s) = val else {
107 return Err(RuntimeError::Error(
108 "String.byteLength: argument must be a String".to_string(),
109 ));
110 };
111 Ok(Value::Int(s.len() as i64))
112}
113
114fn starts_with(args: &[Value]) -> Result<Value, RuntimeError> {
115 let [a, b] = two_args("String.startsWith", args)?;
116 let (Value::Str(s), Value::Str(prefix)) = (a, b) else {
117 return Err(RuntimeError::Error(
118 "String.startsWith: both arguments must be String".to_string(),
119 ));
120 };
121 Ok(Value::Bool(s.starts_with(prefix.as_str())))
122}
123
124fn ends_with(args: &[Value]) -> Result<Value, RuntimeError> {
125 let [a, b] = two_args("String.endsWith", args)?;
126 let (Value::Str(s), Value::Str(suffix)) = (a, b) else {
127 return Err(RuntimeError::Error(
128 "String.endsWith: both arguments must be String".to_string(),
129 ));
130 };
131 Ok(Value::Bool(s.ends_with(suffix.as_str())))
132}
133
134fn contains(args: &[Value]) -> Result<Value, RuntimeError> {
135 let [a, b] = two_args("String.contains", args)?;
136 let (Value::Str(s), Value::Str(sub)) = (a, b) else {
137 return Err(RuntimeError::Error(
138 "String.contains: both arguments must be String".to_string(),
139 ));
140 };
141 Ok(Value::Bool(s.contains(sub.as_str())))
142}
143
144fn slice(args: &[Value]) -> Result<Value, RuntimeError> {
145 if args.len() != 3 {
146 return Err(RuntimeError::Error(format!(
147 "String.slice() takes 3 arguments (s, from, to), got {}",
148 args.len()
149 )));
150 }
151 let Value::Str(s) = &args[0] else {
152 return Err(RuntimeError::Error(
153 "String.slice: first argument must be a String".to_string(),
154 ));
155 };
156 let Value::Int(from) = &args[1] else {
157 return Err(RuntimeError::Error(
158 "String.slice: second argument must be an Int".to_string(),
159 ));
160 };
161 let Value::Int(to) = &args[2] else {
162 return Err(RuntimeError::Error(
163 "String.slice: third argument must be an Int".to_string(),
164 ));
165 };
166 Ok(Value::Str(aver_rt::string_slice(s, *from, *to)))
167}
168
169fn trim(args: &[Value]) -> Result<Value, RuntimeError> {
170 let [val] = one_arg("String.trim", args)?;
171 let Value::Str(s) = val else {
172 return Err(RuntimeError::Error(
173 "String.trim: argument must be a String".to_string(),
174 ));
175 };
176 Ok(Value::Str(s.trim().to_string()))
177}
178
179fn split(args: &[Value]) -> Result<Value, RuntimeError> {
180 let [a, b] = two_args("String.split", args)?;
181 let (Value::Str(s), Value::Str(delim)) = (a, b) else {
182 return Err(RuntimeError::Error(
183 "String.split: both arguments must be String".to_string(),
184 ));
185 };
186 let parts: Vec<Value> = s
187 .split(delim.as_str())
188 .map(|p| Value::Str(p.to_string()))
189 .collect();
190 Ok(list_from_vec(parts))
191}
192
193fn replace(args: &[Value]) -> Result<Value, RuntimeError> {
194 if args.len() != 3 {
195 return Err(RuntimeError::Error(format!(
196 "String.replace() takes 3 arguments (s, old, new), got {}",
197 args.len()
198 )));
199 }
200 let (Value::Str(s), Value::Str(old), Value::Str(new)) = (&args[0], &args[1], &args[2]) else {
201 return Err(RuntimeError::Error(
202 "String.replace: all arguments must be String".to_string(),
203 ));
204 };
205 Ok(Value::Str(s.replace(old.as_str(), new.as_str())))
206}
207
208fn join(args: &[Value]) -> Result<Value, RuntimeError> {
209 let [a, b] = two_args("String.join", args)?;
210 let items = list_view(a).ok_or_else(|| {
211 RuntimeError::Error("String.join: first argument must be a List".to_string())
212 })?;
213 let Value::Str(sep) = b else {
214 return Err(RuntimeError::Error(
215 "String.join: second argument must be a String".to_string(),
216 ));
217 };
218 let strs: Result<Vec<String>, RuntimeError> = items
219 .iter()
220 .map(|v| match v {
221 Value::Str(s) => Ok(s.clone()),
222 _ => Err(RuntimeError::Error(
223 "String.join: list elements must be String".to_string(),
224 )),
225 })
226 .collect();
227 Ok(Value::Str(strs?.join(sep.as_str())))
228}
229
230fn char_at(args: &[Value]) -> Result<Value, RuntimeError> {
231 let [a, b] = two_args("String.charAt", args)?;
232 let Value::Str(s) = a else {
233 return Err(RuntimeError::Error(
234 "String.charAt: first argument must be a String".to_string(),
235 ));
236 };
237 let Value::Int(idx) = b else {
238 return Err(RuntimeError::Error(
239 "String.charAt: second argument must be an Int".to_string(),
240 ));
241 };
242 let idx = *idx as usize;
243 match s.chars().nth(idx) {
244 Some(c) => Ok(Value::Some(Box::new(Value::Str(c.to_string())))),
245 None => Ok(Value::None),
246 }
247}
248
249fn chars(args: &[Value]) -> Result<Value, RuntimeError> {
250 let [val] = one_arg("String.chars", args)?;
251 let Value::Str(s) = val else {
252 return Err(RuntimeError::Error(
253 "String.chars: argument must be a String".to_string(),
254 ));
255 };
256 let result: Vec<Value> = s.chars().map(|c| Value::Str(c.to_string())).collect();
257 Ok(list_from_vec(result))
258}
259
260fn from_int(args: &[Value]) -> Result<Value, RuntimeError> {
261 let [val] = one_arg("String.fromInt", args)?;
262 let Value::Int(n) = val else {
263 return Err(RuntimeError::Error(
264 "String.fromInt: argument must be an Int".to_string(),
265 ));
266 };
267 Ok(Value::Str(format!("{}", n)))
268}
269
270fn from_float(args: &[Value]) -> Result<Value, RuntimeError> {
271 let [val] = one_arg("String.fromFloat", args)?;
272 let Value::Float(f) = val else {
273 return Err(RuntimeError::Error(
274 "String.fromFloat: argument must be a Float".to_string(),
275 ));
276 };
277 Ok(Value::Str(format!("{}", f)))
278}
279
280fn from_bool(args: &[Value]) -> Result<Value, RuntimeError> {
281 let [val] = one_arg("String.fromBool", args)?;
282 let Value::Bool(b) = val else {
283 return Err(RuntimeError::Error(
284 "String.fromBool: argument must be a Bool".to_string(),
285 ));
286 };
287 Ok(Value::Str(if *b { "true" } else { "false" }.to_string()))
288}
289
290fn to_lower(args: &[Value]) -> Result<Value, RuntimeError> {
291 let [val] = one_arg("String.toLower", args)?;
292 let Value::Str(s) = val else {
293 return Err(RuntimeError::Error(
294 "String.toLower: argument must be a String".to_string(),
295 ));
296 };
297 Ok(Value::Str(s.to_lowercase()))
298}
299
300fn to_upper(args: &[Value]) -> Result<Value, RuntimeError> {
301 let [val] = one_arg("String.toUpper", args)?;
302 let Value::Str(s) = val else {
303 return Err(RuntimeError::Error(
304 "String.toUpper: argument must be a String".to_string(),
305 ));
306 };
307 Ok(Value::Str(s.to_uppercase()))
308}
309
310fn one_arg<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 1], RuntimeError> {
313 if args.len() != 1 {
314 return Err(RuntimeError::Error(format!(
315 "{}() takes 1 argument, got {}",
316 name,
317 args.len()
318 )));
319 }
320 Ok([&args[0]])
321}
322
323fn two_args<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 2], RuntimeError> {
324 if args.len() != 2 {
325 return Err(RuntimeError::Error(format!(
326 "{}() takes 2 arguments, got {}",
327 name,
328 args.len()
329 )));
330 }
331 Ok([&args[0], &args[1]])
332}
333
334pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
337 let methods = &[
338 "len",
339 "byteLength",
340 "startsWith",
341 "endsWith",
342 "contains",
343 "slice",
344 "trim",
345 "split",
346 "replace",
347 "join",
348 "charAt",
349 "chars",
350 "fromInt",
351 "fromFloat",
352 "fromBool",
353 "toLower",
354 "toUpper",
355 ];
356 let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
357 for method in methods {
358 let idx = arena.push_builtin(&format!("String.{}", method));
359 members.push((Rc::from(*method), NanValue::new_builtin(idx)));
360 }
361 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
362 name: Rc::from("String"),
363 members,
364 });
365 global.insert("String".to_string(), NanValue::new_namespace(ns_idx));
366}
367
368pub fn call_nv(
369 name: &str,
370 args: &[NanValue],
371 arena: &mut Arena,
372) -> Option<Result<NanValue, RuntimeError>> {
373 match name {
374 "String.len" => Some(length_nv(args, arena)),
375 "String.byteLength" => Some(byte_length_nv(args, arena)),
376 "String.startsWith" => Some(starts_with_nv(args, arena)),
377 "String.endsWith" => Some(ends_with_nv(args, arena)),
378 "String.contains" => Some(contains_nv(args, arena)),
379 "String.slice" => Some(slice_nv(args, arena)),
380 "String.trim" => Some(trim_nv(args, arena)),
381 "String.split" => Some(split_nv(args, arena)),
382 "String.replace" => Some(replace_nv(args, arena)),
383 "String.join" => Some(join_nv(args, arena)),
384 "String.charAt" => Some(char_at_nv(args, arena)),
385 "String.chars" => Some(chars_nv(args, arena)),
386 "String.fromInt" => Some(from_int_nv(args, arena)),
387 "String.fromFloat" => Some(from_float_nv(args, arena)),
388 "String.fromBool" => Some(from_bool_nv(args, arena)),
389 "String.toLower" => Some(to_lower_nv(args, arena)),
390 "String.toUpper" => Some(to_upper_nv(args, arena)),
391 _ => None,
392 }
393}
394
395fn nv_str(v: NanValue, arena: &Arena) -> Option<&str> {
396 if v.is_string() {
397 Some(arena.get_string(v.arena_index()))
398 } else {
399 None
400 }
401}
402
403fn length_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
404 if args.len() != 1 {
405 return Err(RuntimeError::Error(format!(
406 "String.len() takes 1 argument, got {}",
407 args.len()
408 )));
409 }
410 let s = nv_str(args[0], arena)
411 .ok_or_else(|| RuntimeError::Error("String.len: argument must be a String".to_string()))?;
412 Ok(NanValue::new_int(s.chars().count() as i64, arena))
413}
414
415fn byte_length_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
416 if args.len() != 1 {
417 return Err(RuntimeError::Error(format!(
418 "String.byteLength() takes 1 argument, got {}",
419 args.len()
420 )));
421 }
422 let s = nv_str(args[0], arena).ok_or_else(|| {
423 RuntimeError::Error("String.byteLength: argument must be a String".to_string())
424 })?;
425 Ok(NanValue::new_int(s.len() as i64, arena))
426}
427
428fn starts_with_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
429 if args.len() != 2 {
430 return Err(RuntimeError::Error(format!(
431 "String.startsWith() takes 2 arguments, got {}",
432 args.len()
433 )));
434 }
435 if !args[0].is_string() || !args[1].is_string() {
436 return Err(RuntimeError::Error(
437 "String.startsWith: both arguments must be String".to_string(),
438 ));
439 }
440 let s = arena.get_string(args[0].arena_index());
441 let prefix = arena.get_string(args[1].arena_index());
442 let result = s.starts_with(prefix);
443 Ok(NanValue::new_bool(result))
444}
445
446fn ends_with_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
447 if args.len() != 2 {
448 return Err(RuntimeError::Error(format!(
449 "String.endsWith() takes 2 arguments, got {}",
450 args.len()
451 )));
452 }
453 if !args[0].is_string() || !args[1].is_string() {
454 return Err(RuntimeError::Error(
455 "String.endsWith: both arguments must be String".to_string(),
456 ));
457 }
458 let s = arena.get_string(args[0].arena_index());
459 let suffix = arena.get_string(args[1].arena_index());
460 let result = s.ends_with(suffix);
461 Ok(NanValue::new_bool(result))
462}
463
464fn contains_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
465 if args.len() != 2 {
466 return Err(RuntimeError::Error(format!(
467 "String.contains() takes 2 arguments, got {}",
468 args.len()
469 )));
470 }
471 if !args[0].is_string() || !args[1].is_string() {
472 return Err(RuntimeError::Error(
473 "String.contains: both arguments must be String".to_string(),
474 ));
475 }
476 let s = arena.get_string(args[0].arena_index());
477 let sub = arena.get_string(args[1].arena_index());
478 let result = s.contains(sub);
479 Ok(NanValue::new_bool(result))
480}
481
482fn slice_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
483 if args.len() != 3 {
484 return Err(RuntimeError::Error(format!(
485 "String.slice() takes 3 arguments (s, from, to), got {}",
486 args.len()
487 )));
488 }
489 if !args[0].is_string() {
490 return Err(RuntimeError::Error(
491 "String.slice: first argument must be a String".to_string(),
492 ));
493 }
494 if !args[1].is_int() {
495 return Err(RuntimeError::Error(
496 "String.slice: second argument must be an Int".to_string(),
497 ));
498 }
499 if !args[2].is_int() {
500 return Err(RuntimeError::Error(
501 "String.slice: third argument must be an Int".to_string(),
502 ));
503 }
504 let s = arena.get_string(args[0].arena_index()).to_string();
505 let from = args[1].as_int(arena);
506 let to = args[2].as_int(arena);
507 let result = aver_rt::string_slice(&s, from, to);
508 let idx = arena.push_string(&result);
509 Ok(NanValue::new_string(idx))
510}
511
512fn trim_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
513 if args.len() != 1 {
514 return Err(RuntimeError::Error(format!(
515 "String.trim() takes 1 argument, got {}",
516 args.len()
517 )));
518 }
519 let s = nv_str(args[0], arena)
520 .ok_or_else(|| RuntimeError::Error("String.trim: argument must be a String".to_string()))?;
521 let result = s.trim().to_string();
522 let idx = arena.push_string(&result);
523 Ok(NanValue::new_string(idx))
524}
525
526fn split_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
527 if args.len() != 2 {
528 return Err(RuntimeError::Error(format!(
529 "String.split() takes 2 arguments, got {}",
530 args.len()
531 )));
532 }
533 if !args[0].is_string() || !args[1].is_string() {
534 return Err(RuntimeError::Error(
535 "String.split: both arguments must be String".to_string(),
536 ));
537 }
538 let s = arena.get_string(args[0].arena_index()).to_string();
539 let delim = arena.get_string(args[1].arena_index()).to_string();
540 let parts: Vec<NanValue> = s
541 .split(&*delim)
542 .map(|p| {
543 let idx = arena.push_string(p);
544 NanValue::new_string(idx)
545 })
546 .collect();
547 let list_idx = arena.push_list(parts);
548 Ok(NanValue::new_list(list_idx))
549}
550
551fn replace_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
552 if args.len() != 3 {
553 return Err(RuntimeError::Error(format!(
554 "String.replace() takes 3 arguments (s, old, new), got {}",
555 args.len()
556 )));
557 }
558 if !args[0].is_string() || !args[1].is_string() || !args[2].is_string() {
559 return Err(RuntimeError::Error(
560 "String.replace: all arguments must be String".to_string(),
561 ));
562 }
563 let s = arena.get_string(args[0].arena_index()).to_string();
564 let old = arena.get_string(args[1].arena_index()).to_string();
565 let new = arena.get_string(args[2].arena_index()).to_string();
566 let result = s.replace(&*old, &new);
567 let idx = arena.push_string(&result);
568 Ok(NanValue::new_string(idx))
569}
570
571fn join_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
572 if args.len() != 2 {
573 return Err(RuntimeError::Error(format!(
574 "String.join() takes 2 arguments, got {}",
575 args.len()
576 )));
577 }
578 if !args[0].is_list() {
579 return Err(RuntimeError::Error(
580 "String.join: first argument must be a List".to_string(),
581 ));
582 }
583 if !args[1].is_string() {
584 return Err(RuntimeError::Error(
585 "String.join: second argument must be a String".to_string(),
586 ));
587 }
588 let items = arena.list_to_vec(args[0].arena_index());
589 let sep = arena.get_string(args[1].arena_index()).to_string();
590 let mut strs: Vec<String> = Vec::with_capacity(items.len());
591 for item in &items {
592 if !item.is_string() {
593 return Err(RuntimeError::Error(
594 "String.join: list elements must be String".to_string(),
595 ));
596 }
597 strs.push(arena.get_string(item.arena_index()).to_string());
598 }
599 let result = strs.join(&sep);
600 let idx = arena.push_string(&result);
601 Ok(NanValue::new_string(idx))
602}
603
604fn char_at_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
605 if args.len() != 2 {
606 return Err(RuntimeError::Error(format!(
607 "String.charAt() takes 2 arguments, got {}",
608 args.len()
609 )));
610 }
611 if !args[0].is_string() {
612 return Err(RuntimeError::Error(
613 "String.charAt: first argument must be a String".to_string(),
614 ));
615 }
616 if !args[1].is_int() {
617 return Err(RuntimeError::Error(
618 "String.charAt: second argument must be an Int".to_string(),
619 ));
620 }
621 let s = arena.get_string(args[0].arena_index());
622 let idx_val = args[1].as_int(arena) as usize;
623 match s.chars().nth(idx_val) {
624 Some(c) => {
625 let cs = c.to_string();
626 let s_idx = arena.push_string(&cs);
627 let inner = NanValue::new_string(s_idx);
628 let box_idx = arena.push_boxed(inner);
629 Ok(NanValue::new_some(box_idx))
630 }
631 None => Ok(NanValue::NONE),
632 }
633}
634
635fn chars_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
636 if args.len() != 1 {
637 return Err(RuntimeError::Error(format!(
638 "String.chars() takes 1 argument, got {}",
639 args.len()
640 )));
641 }
642 if !args[0].is_string() {
643 return Err(RuntimeError::Error(
644 "String.chars: argument must be a String".to_string(),
645 ));
646 }
647 let s = arena.get_string(args[0].arena_index()).to_string();
648 let items: Vec<NanValue> = s
649 .chars()
650 .map(|c| {
651 let cs = c.to_string();
652 let idx = arena.push_string(&cs);
653 NanValue::new_string(idx)
654 })
655 .collect();
656 let list_idx = arena.push_list(items);
657 Ok(NanValue::new_list(list_idx))
658}
659
660fn from_int_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
661 if args.len() != 1 {
662 return Err(RuntimeError::Error(format!(
663 "String.fromInt() takes 1 argument, got {}",
664 args.len()
665 )));
666 }
667 if !args[0].is_int() {
668 return Err(RuntimeError::Error(
669 "String.fromInt: argument must be an Int".to_string(),
670 ));
671 }
672 let s = format!("{}", args[0].as_int(arena));
673 let idx = arena.push_string(&s);
674 Ok(NanValue::new_string(idx))
675}
676
677fn from_float_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
678 if args.len() != 1 {
679 return Err(RuntimeError::Error(format!(
680 "String.fromFloat() takes 1 argument, got {}",
681 args.len()
682 )));
683 }
684 if !args[0].is_float() {
685 return Err(RuntimeError::Error(
686 "String.fromFloat: argument must be a Float".to_string(),
687 ));
688 }
689 let s = format!("{}", args[0].as_float());
690 let idx = arena.push_string(&s);
691 Ok(NanValue::new_string(idx))
692}
693
694fn from_bool_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
695 if args.len() != 1 {
696 return Err(RuntimeError::Error(format!(
697 "String.fromBool() takes 1 argument, got {}",
698 args.len()
699 )));
700 }
701 if !args[0].is_bool() {
702 return Err(RuntimeError::Error(
703 "String.fromBool: argument must be a Bool".to_string(),
704 ));
705 }
706 let s = if args[0].as_bool() { "true" } else { "false" };
707 let idx = arena.push_string(s);
708 Ok(NanValue::new_string(idx))
709}
710
711fn to_lower_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
712 if args.len() != 1 {
713 return Err(RuntimeError::Error(format!(
714 "String.toLower() takes 1 argument, got {}",
715 args.len()
716 )));
717 }
718 if !args[0].is_string() {
719 return Err(RuntimeError::Error(
720 "String.toLower: argument must be a String".to_string(),
721 ));
722 }
723 let s = arena.get_string(args[0].arena_index()).to_lowercase();
724 let idx = arena.push_string(&s);
725 Ok(NanValue::new_string(idx))
726}
727
728fn to_upper_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
729 if args.len() != 1 {
730 return Err(RuntimeError::Error(format!(
731 "String.toUpper() takes 1 argument, got {}",
732 args.len()
733 )));
734 }
735 if !args[0].is_string() {
736 return Err(RuntimeError::Error(
737 "String.toUpper: argument must be a String".to_string(),
738 ));
739 }
740 let s = arena.get_string(args[0].arena_index()).to_uppercase();
741 let idx = arena.push_string(&s);
742 Ok(NanValue::new_string(idx))
743}