1use std::collections::HashMap;
24use std::sync::Arc as Rc;
25
26use crate::nan_value::{Arena, NanIntExt, NanString, 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(
169 s,
170 saturate_index(from),
171 saturate_index(to),
172 )))
173}
174
175fn saturate_index(n: &aver_rt::AverInt) -> i64 {
179 match n.to_i64() {
180 Some(v) => v,
181 None if *n > aver_rt::AverInt::zero() => i64::MAX,
182 None => i64::MIN,
183 }
184}
185
186fn trim(args: &[Value]) -> Result<Value, RuntimeError> {
187 let [val] = one_arg("String.trim", args)?;
188 let Value::Str(s) = val else {
189 return Err(RuntimeError::Error(
190 "String.trim: argument must be a String".to_string(),
191 ));
192 };
193 Ok(Value::Str(s.trim().to_string()))
194}
195
196fn split(args: &[Value]) -> Result<Value, RuntimeError> {
197 let [a, b] = two_args("String.split", args)?;
198 let (Value::Str(s), Value::Str(delim)) = (a, b) else {
199 return Err(RuntimeError::Error(
200 "String.split: both arguments must be String".to_string(),
201 ));
202 };
203 let parts: Vec<Value> = s
204 .split(delim.as_str())
205 .map(|p| Value::Str(p.to_string()))
206 .collect();
207 Ok(list_from_vec(parts))
208}
209
210fn replace(args: &[Value]) -> Result<Value, RuntimeError> {
211 if args.len() != 3 {
212 return Err(RuntimeError::Error(format!(
213 "String.replace() takes 3 arguments (s, old, new), got {}",
214 args.len()
215 )));
216 }
217 let (Value::Str(s), Value::Str(old), Value::Str(new)) = (&args[0], &args[1], &args[2]) else {
218 return Err(RuntimeError::Error(
219 "String.replace: all arguments must be String".to_string(),
220 ));
221 };
222 Ok(Value::Str(s.replace(old.as_str(), new.as_str())))
223}
224
225fn join(args: &[Value]) -> Result<Value, RuntimeError> {
226 let [a, b] = two_args("String.join", args)?;
227 let items = list_view(a).ok_or_else(|| {
228 RuntimeError::Error("String.join: first argument must be a List".to_string())
229 })?;
230 let Value::Str(sep) = b else {
231 return Err(RuntimeError::Error(
232 "String.join: second argument must be a String".to_string(),
233 ));
234 };
235 let strs: Result<Vec<String>, RuntimeError> = items
236 .iter()
237 .map(|v| match v {
238 Value::Str(s) => Ok(s.clone()),
239 _ => Err(RuntimeError::Error(
240 "String.join: list elements must be String".to_string(),
241 )),
242 })
243 .collect();
244 Ok(Value::Str(strs?.join(sep.as_str())))
245}
246
247fn char_at(args: &[Value]) -> Result<Value, RuntimeError> {
248 let [a, b] = two_args("String.charAt", args)?;
249 let Value::Str(s) = a else {
250 return Err(RuntimeError::Error(
251 "String.charAt: first argument must be a String".to_string(),
252 ));
253 };
254 let Value::Int(idx) = b else {
255 return Err(RuntimeError::Error(
256 "String.charAt: second argument must be an Int".to_string(),
257 ));
258 };
259 let Some(idx) = idx.to_usize() else {
260 return Ok(Value::None);
261 };
262 match s.chars().nth(idx) {
263 Some(c) => Ok(Value::Some(Box::new(Value::Str(c.to_string())))),
264 None => Ok(Value::None),
265 }
266}
267
268fn chars(args: &[Value]) -> Result<Value, RuntimeError> {
269 let [val] = one_arg("String.chars", args)?;
270 let Value::Str(s) = val else {
271 return Err(RuntimeError::Error(
272 "String.chars: argument must be a String".to_string(),
273 ));
274 };
275 let result: Vec<Value> = s.chars().map(|c| Value::Str(c.to_string())).collect();
276 Ok(list_from_vec(result))
277}
278
279fn from_int(args: &[Value]) -> Result<Value, RuntimeError> {
280 let [val] = one_arg("String.fromInt", args)?;
281 let Value::Int(n) = val else {
282 return Err(RuntimeError::Error(
283 "String.fromInt: argument must be an Int".to_string(),
284 ));
285 };
286 Ok(Value::Str(format!("{}", n)))
287}
288
289fn from_float(args: &[Value]) -> Result<Value, RuntimeError> {
290 let [val] = one_arg("String.fromFloat", args)?;
291 let Value::Float(f) = val else {
292 return Err(RuntimeError::Error(
293 "String.fromFloat: argument must be a Float".to_string(),
294 ));
295 };
296 Ok(Value::Str(format!("{}", f)))
297}
298
299fn from_bool(args: &[Value]) -> Result<Value, RuntimeError> {
300 let [val] = one_arg("String.fromBool", args)?;
301 let Value::Bool(b) = val else {
302 return Err(RuntimeError::Error(
303 "String.fromBool: argument must be a Bool".to_string(),
304 ));
305 };
306 Ok(Value::Str(if *b { "true" } else { "false" }.to_string()))
307}
308
309fn to_lower(args: &[Value]) -> Result<Value, RuntimeError> {
310 let [val] = one_arg("String.toLower", args)?;
311 let Value::Str(s) = val else {
312 return Err(RuntimeError::Error(
313 "String.toLower: argument must be a String".to_string(),
314 ));
315 };
316 Ok(Value::Str(s.to_lowercase()))
317}
318
319fn to_upper(args: &[Value]) -> Result<Value, RuntimeError> {
320 let [val] = one_arg("String.toUpper", args)?;
321 let Value::Str(s) = val else {
322 return Err(RuntimeError::Error(
323 "String.toUpper: argument must be a String".to_string(),
324 ));
325 };
326 Ok(Value::Str(s.to_uppercase()))
327}
328
329fn one_arg<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 1], RuntimeError> {
332 if args.len() != 1 {
333 return Err(RuntimeError::Error(format!(
334 "{}() takes 1 argument, got {}",
335 name,
336 args.len()
337 )));
338 }
339 Ok([&args[0]])
340}
341
342fn two_args<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 2], RuntimeError> {
343 if args.len() != 2 {
344 return Err(RuntimeError::Error(format!(
345 "{}() takes 2 arguments, got {}",
346 name,
347 args.len()
348 )));
349 }
350 Ok([&args[0], &args[1]])
351}
352
353pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
356 let methods = &[
357 "len",
358 "byteLength",
359 "startsWith",
360 "endsWith",
361 "contains",
362 "slice",
363 "trim",
364 "split",
365 "replace",
366 "join",
367 "charAt",
368 "chars",
369 "fromInt",
370 "fromFloat",
371 "fromBool",
372 "toLower",
373 "toUpper",
374 ];
375 let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
376 for method in methods {
377 let idx = arena.push_builtin(&format!("String.{}", method));
378 members.push((Rc::from(*method), NanValue::new_builtin(idx)));
379 }
380 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
381 name: Rc::from("String"),
382 members,
383 });
384 global.insert("String".to_string(), NanValue::new_namespace(ns_idx));
385}
386
387pub fn call_nv(
388 name: &str,
389 args: &[NanValue],
390 arena: &mut Arena,
391) -> Option<Result<NanValue, RuntimeError>> {
392 match name {
393 "String.len" => Some(length_nv(args, arena)),
394 "String.byteLength" => Some(byte_length_nv(args, arena)),
395 "String.startsWith" => Some(starts_with_nv(args, arena)),
396 "String.endsWith" => Some(ends_with_nv(args, arena)),
397 "String.contains" => Some(contains_nv(args, arena)),
398 "String.slice" => Some(slice_nv(args, arena)),
399 "String.trim" => Some(trim_nv(args, arena)),
400 "String.split" => Some(split_nv(args, arena)),
401 "String.replace" => Some(replace_nv(args, arena)),
402 "String.join" => Some(join_nv(args, arena)),
403 "String.charAt" => Some(char_at_nv(args, arena)),
404 "String.chars" => Some(chars_nv(args, arena)),
405 "String.fromInt" => Some(from_int_nv(args, arena)),
406 "String.fromFloat" => Some(from_float_nv(args, arena)),
407 "String.fromBool" => Some(from_bool_nv(args, arena)),
408 "String.toLower" => Some(to_lower_nv(args, arena)),
409 "String.toUpper" => Some(to_upper_nv(args, arena)),
410 _ => None,
411 }
412}
413
414fn nv_str(v: NanValue, arena: &Arena) -> Option<NanString<'_>> {
415 if v.is_string() {
416 Some(arena.get_string_value(v))
417 } else {
418 None
419 }
420}
421
422fn length_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
423 if args.len() != 1 {
424 return Err(RuntimeError::Error(format!(
425 "String.len() takes 1 argument, got {}",
426 args.len()
427 )));
428 }
429 let s = nv_str(args[0], arena)
430 .ok_or_else(|| RuntimeError::Error("String.len: argument must be a String".to_string()))?;
431 Ok(NanValue::new_int(s.chars().count() as i64, arena))
432}
433
434fn byte_length_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
435 if args.len() != 1 {
436 return Err(RuntimeError::Error(format!(
437 "String.byteLength() takes 1 argument, got {}",
438 args.len()
439 )));
440 }
441 let s = nv_str(args[0], arena).ok_or_else(|| {
442 RuntimeError::Error("String.byteLength: argument must be a String".to_string())
443 })?;
444 Ok(NanValue::new_int(s.len() as i64, arena))
445}
446
447fn starts_with_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
448 if args.len() != 2 {
449 return Err(RuntimeError::Error(format!(
450 "String.startsWith() takes 2 arguments, got {}",
451 args.len()
452 )));
453 }
454 if !args[0].is_string() || !args[1].is_string() {
455 return Err(RuntimeError::Error(
456 "String.startsWith: both arguments must be String".to_string(),
457 ));
458 }
459 let s = arena.get_string_value(args[0]);
460 let prefix = arena.get_string_value(args[1]);
461 let result = s.starts_with(prefix.as_str());
462 Ok(NanValue::new_bool(result))
463}
464
465fn ends_with_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
466 if args.len() != 2 {
467 return Err(RuntimeError::Error(format!(
468 "String.endsWith() takes 2 arguments, got {}",
469 args.len()
470 )));
471 }
472 if !args[0].is_string() || !args[1].is_string() {
473 return Err(RuntimeError::Error(
474 "String.endsWith: both arguments must be String".to_string(),
475 ));
476 }
477 let s = arena.get_string_value(args[0]);
478 let suffix = arena.get_string_value(args[1]);
479 let result = s.ends_with(suffix.as_str());
480 Ok(NanValue::new_bool(result))
481}
482
483fn contains_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
484 if args.len() != 2 {
485 return Err(RuntimeError::Error(format!(
486 "String.contains() takes 2 arguments, got {}",
487 args.len()
488 )));
489 }
490 if !args[0].is_string() || !args[1].is_string() {
491 return Err(RuntimeError::Error(
492 "String.contains: both arguments must be String".to_string(),
493 ));
494 }
495 let s = arena.get_string_value(args[0]);
496 let sub = arena.get_string_value(args[1]);
497 let result = s.contains(sub.as_str());
498 Ok(NanValue::new_bool(result))
499}
500
501fn slice_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
502 if args.len() != 3 {
503 return Err(RuntimeError::Error(format!(
504 "String.slice() takes 3 arguments (s, from, to), got {}",
505 args.len()
506 )));
507 }
508 if !args[0].is_string() {
509 return Err(RuntimeError::Error(
510 "String.slice: first argument must be a String".to_string(),
511 ));
512 }
513 if !args[1].is_int() {
514 return Err(RuntimeError::Error(
515 "String.slice: second argument must be an Int".to_string(),
516 ));
517 }
518 if !args[2].is_int() {
519 return Err(RuntimeError::Error(
520 "String.slice: third argument must be an Int".to_string(),
521 ));
522 }
523 let s = arena.get_string_value(args[0]).to_string();
524 let from = saturate_index(&args[1].as_aver_int(arena));
525 let to = saturate_index(&args[2].as_aver_int(arena));
526 let result = aver_rt::string_slice(&s, from, to);
527 Ok(NanValue::new_string_value(&result, arena))
528}
529
530fn trim_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
531 if args.len() != 1 {
532 return Err(RuntimeError::Error(format!(
533 "String.trim() takes 1 argument, got {}",
534 args.len()
535 )));
536 }
537 let s = nv_str(args[0], arena)
538 .ok_or_else(|| RuntimeError::Error("String.trim: argument must be a String".to_string()))?;
539 let result = s.trim().to_string();
540 Ok(NanValue::new_string_value(&result, arena))
541}
542
543fn split_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
544 if args.len() != 2 {
545 return Err(RuntimeError::Error(format!(
546 "String.split() takes 2 arguments, got {}",
547 args.len()
548 )));
549 }
550 if !args[0].is_string() || !args[1].is_string() {
551 return Err(RuntimeError::Error(
552 "String.split: both arguments must be String".to_string(),
553 ));
554 }
555 let s = arena.get_string_value(args[0]).to_string();
556 let delim = arena.get_string_value(args[1]).to_string();
557 let parts: Vec<NanValue> = s
558 .split(&*delim)
559 .map(|p| NanValue::new_string_value(p, arena))
560 .collect();
561 let list_idx = arena.push_list(parts);
562 Ok(NanValue::new_list(list_idx))
563}
564
565fn replace_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
566 if args.len() != 3 {
567 return Err(RuntimeError::Error(format!(
568 "String.replace() takes 3 arguments (s, old, new), got {}",
569 args.len()
570 )));
571 }
572 if !args[0].is_string() || !args[1].is_string() || !args[2].is_string() {
573 return Err(RuntimeError::Error(
574 "String.replace: all arguments must be String".to_string(),
575 ));
576 }
577 let s = arena.get_string_value(args[0]).to_string();
578 let old = arena.get_string_value(args[1]).to_string();
579 let new = arena.get_string_value(args[2]).to_string();
580 let result = s.replace(&*old, &new);
581 Ok(NanValue::new_string_value(&result, arena))
582}
583
584fn join_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
585 if args.len() != 2 {
586 return Err(RuntimeError::Error(format!(
587 "String.join() takes 2 arguments, got {}",
588 args.len()
589 )));
590 }
591 if !args[0].is_list() {
592 return Err(RuntimeError::Error(
593 "String.join: first argument must be a List".to_string(),
594 ));
595 }
596 if !args[1].is_string() {
597 return Err(RuntimeError::Error(
598 "String.join: second argument must be a String".to_string(),
599 ));
600 }
601 let items = arena.list_to_vec_value(args[0]);
602 let sep = arena.get_string_value(args[1]).to_string();
603 let mut strs: Vec<String> = Vec::with_capacity(items.len());
604 for item in &items {
605 if !item.is_string() {
606 return Err(RuntimeError::Error(
607 "String.join: list elements must be String".to_string(),
608 ));
609 }
610 strs.push(arena.get_string_value(*item).to_string());
611 }
612 let result = strs.join(&sep);
613 Ok(NanValue::new_string_value(&result, arena))
614}
615
616fn char_at_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
617 if args.len() != 2 {
618 return Err(RuntimeError::Error(format!(
619 "String.charAt() takes 2 arguments, got {}",
620 args.len()
621 )));
622 }
623 if !args[0].is_string() {
624 return Err(RuntimeError::Error(
625 "String.charAt: first argument must be a String".to_string(),
626 ));
627 }
628 if !args[1].is_int() {
629 return Err(RuntimeError::Error(
630 "String.charAt: second argument must be an Int".to_string(),
631 ));
632 }
633 let Some(idx_val) = args[1].as_aver_int(arena).to_usize() else {
634 return Ok(NanValue::NONE);
635 };
636 let s = arena.get_string_value(args[0]);
637 match s.chars().nth(idx_val) {
638 Some(c) => {
639 let cs = c.to_string();
640 let inner = NanValue::new_string_value(&cs, arena);
641 Ok(NanValue::new_some_value(inner, arena))
642 }
643 None => Ok(NanValue::NONE),
644 }
645}
646
647fn chars_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
648 if args.len() != 1 {
649 return Err(RuntimeError::Error(format!(
650 "String.chars() takes 1 argument, got {}",
651 args.len()
652 )));
653 }
654 if !args[0].is_string() {
655 return Err(RuntimeError::Error(
656 "String.chars: argument must be a String".to_string(),
657 ));
658 }
659 let s = arena.get_string_value(args[0]).to_string();
660 let items: Vec<NanValue> = s
661 .chars()
662 .map(|c| NanValue::new_string_value(&c.to_string(), arena))
663 .collect();
664 let list_idx = arena.push_list(items);
665 Ok(NanValue::new_list(list_idx))
666}
667
668fn from_int_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
669 if args.len() != 1 {
670 return Err(RuntimeError::Error(format!(
671 "String.fromInt() takes 1 argument, got {}",
672 args.len()
673 )));
674 }
675 if !args[0].is_int() {
676 return Err(RuntimeError::Error(
677 "String.fromInt: argument must be an Int".to_string(),
678 ));
679 }
680 let s = format!("{}", args[0].as_aver_int(arena));
682 Ok(NanValue::new_string_value(&s, arena))
683}
684
685fn from_float_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
686 if args.len() != 1 {
687 return Err(RuntimeError::Error(format!(
688 "String.fromFloat() takes 1 argument, got {}",
689 args.len()
690 )));
691 }
692 if !args[0].is_float() {
693 return Err(RuntimeError::Error(
694 "String.fromFloat: argument must be a Float".to_string(),
695 ));
696 }
697 let s = format!("{}", args[0].as_float());
698 Ok(NanValue::new_string_value(&s, arena))
699}
700
701fn from_bool_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
702 if args.len() != 1 {
703 return Err(RuntimeError::Error(format!(
704 "String.fromBool() takes 1 argument, got {}",
705 args.len()
706 )));
707 }
708 if !args[0].is_bool() {
709 return Err(RuntimeError::Error(
710 "String.fromBool: argument must be a Bool".to_string(),
711 ));
712 }
713 let s = if args[0].as_bool() { "true" } else { "false" };
714 Ok(NanValue::new_string_value(s, arena))
715}
716
717fn to_lower_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
718 if args.len() != 1 {
719 return Err(RuntimeError::Error(format!(
720 "String.toLower() takes 1 argument, got {}",
721 args.len()
722 )));
723 }
724 if !args[0].is_string() {
725 return Err(RuntimeError::Error(
726 "String.toLower: argument must be a String".to_string(),
727 ));
728 }
729 let s = arena.get_string_value(args[0]).to_lowercase();
730 Ok(NanValue::new_string_value(&s, arena))
731}
732
733fn to_upper_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
734 if args.len() != 1 {
735 return Err(RuntimeError::Error(format!(
736 "String.toUpper() takes 1 argument, got {}",
737 args.len()
738 )));
739 }
740 if !args[0].is_string() {
741 return Err(RuntimeError::Error(
742 "String.toUpper: argument must be a String".to_string(),
743 ));
744 }
745 let s = arena.get_string_value(args[0]).to_uppercase();
746 Ok(NanValue::new_string_value(&s, arena))
747}